From e29e171958486fc8fa617a8e3d8b389e5dc71a9a Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 09:53:46 +0200 Subject: [PATCH 01/66] Refactor the IPackageController hooks Don't use the old `.create()`, `.edit()`, `.delete()` hooks and use the new `after_dataset_*()` ones. Use the geojson package to validate the spatial extra --- ckanext/spatial/plugin/__init__.py | 123 +++++++++++++++++------------ ckanext/spatial/util.py | 14 ---- requirements.txt | 1 + 3 files changed, 74 insertions(+), 64 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 89e93ed0..aaa272f5 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -4,13 +4,14 @@ from logging import getLogger import six +import geojson import ckantoolkit as tk from ckan import plugins as p +from ckanext.spatial.lib import save_package_extent from ckan.lib.helpers import json -from ckanext.spatial.util import _get_package_extras if tk.check_ckan_version(min_version="2.9.0"): from ckanext.spatial.plugin.flask_plugin import ( SpatialQueryMixin, HarvestMetadataApiMixin @@ -73,6 +74,7 @@ def prettify(field_name): summary[tk._(prettify(key))] = error[0] return summary + class SpatialMetadata(p.SingletonPlugin): p.implements(p.IPackageController, inherit=True) @@ -80,6 +82,8 @@ class SpatialMetadata(p.SingletonPlugin): p.implements(p.IConfigurer, inherit=True) p.implements(p.ITemplateHelpers, inherit=True) + # IConfigurable + def configure(self, config): from ckanext.spatial.model.package_extent import setup as setup_model @@ -87,6 +91,8 @@ def configure(self, config): log.debug('Setting up the spatial model') setup_model() + # IConfigure + def update_config(self, config): ''' Set up the resource library, public directory and template directory for all the spatial extensions @@ -100,62 +106,79 @@ def update_config(self, config): mimetypes.add_type('application/json', '.geojson') mimetypes.add_type('application/gml+xml', '.gml') - def create(self, package): - self.check_spatial_extra(package) + # IPackageController + + def after_create(self, context, data_dict): + return self.after_dataset_create(context, data_dict) + + def after_dataset_create(self, context, data_dict): + self.check_spatial_extra(data_dict) + + def after_update(self, context, data_dict): + return self.after_dataset_update(context, data_dict) + + def after_dataset_update(self, context, data_dict): + self.check_spatial_extra(data_dict) - def edit(self, package): - self.check_spatial_extra(package) + def after_delete(self, context, data_dict): + return self.after_dataset_delete(context, data_dict) - def check_spatial_extra(self,package): + def after_dataset_delete(self, context, data_dict): + save_package_extent(data_dict["id"], None) + + def check_spatial_extra(self, dataset_dict): ''' - For a given package, looks at the spatial extent (as given in the - extra "spatial" in GeoJSON format) and records it in PostGIS. + For a given dataset, looks at the spatial extent (as given in the + "spatial" field/extra in GeoJSON format) and stores it in the database. ''' - from ckanext.spatial.lib import save_package_extent - if not package.id: - log.warning('Couldn\'t store spatial extent because no id was provided for the package') + dataset_id = dataset_dict["id"] + geometry = dataset_dict.get("spatial") + delete = False + + if not geometry: + # Check extras + for extra in dataset_dict.get("extras", []): + if extra["key"] == "spatial": + if extra.get("deleted"): + delete = True + else: + geometry = extra["value"] + + if geometry is None or geometry == "" or delete: + save_package_extent(dataset_id, None) + elif not geometry: return - # TODO: deleted extra - for extra in _get_package_extras(package.id): - if extra.key != 'spatial': - continue - if extra.state == 'active' and extra.value: - try: - log.debug('Received: %r' % extra.value) - geometry = json.loads(extra.value) - except ValueError as e: - error_dict = {'spatial':[u'Error decoding JSON object: %s' % six.text_type(e)]} - raise tk.ValidationError(error_dict, error_summary=package_error_summary(error_dict)) - except TypeError as e: - error_dict = {'spatial':[u'Error decoding JSON object: %s' % six.text_type(e)]} - raise tk.ValidationError(error_dict, error_summary=package_error_summary(error_dict)) - - try: - save_package_extent(package.id,geometry) - - except AttributeError as e: - error_dict = {'spatial':[u'Error creating geometry: invalid GeoJSON']} - raise tk.ValidationError(error_dict, error_summary=package_error_summary(error_dict)) - except Exception as e: - if bool(os.getenv('DEBUG')): - raise - error_dict = {'spatial':[u'Error: %s' % six.text_type(e)]} - raise tk.ValidationError(error_dict, error_summary=package_error_summary(error_dict)) - - elif (extra.state == 'active' and not extra.value) or extra.state == 'deleted': - # Delete extent from table - save_package_extent(package.id,None) - - break - - - def delete(self, package): - from ckanext.spatial.lib import save_package_extent - save_package_extent(package.id,None) - - ## ITemplateHelpers + # Check valid JSON + try: + log.debug("Received geometry: {}".format(geometry)) + + geometry = geojson.loads(geometry) + except ValueError as e: + error_dict = { + "spatial": ["Error decoding JSON object: {}".format(six.text_type(e))]} + raise tk.ValidationError( + error_dict, error_summary=package_error_summary(error_dict)) + + if not hasattr(geometry, "is_valid") or not geometry.is_valid: + msg = "Wrong GeoJSON object" + if hasattr(geometry, "errors"): + msg = msg + ": {}".format(geometry.errors()) + error_dict = {"spatial": [msg]} + raise tk.ValidationError( + error_dict, error_summary=package_error_summary(error_dict)) + + try: + save_package_extent(dataset_id, geometry) + except Exception as e: + if bool(os.getenv('DEBUG')): + raise + error_dict = {'spatial': [u'Error: %s' % six.text_type(e)]} + raise tk.ValidationError( + error_dict, error_summary=package_error_summary(error_dict)) + + # ITemplateHelpers def get_helpers(self): from ckanext.spatial import helpers as spatial_helpers diff --git a/ckanext/spatial/util.py b/ckanext/spatial/util.py index 3aef1308..984e3b00 100644 --- a/ckanext/spatial/util.py +++ b/ckanext/spatial/util.py @@ -204,17 +204,3 @@ def _transform_to_html(content, xslt_package=None, xslt_path=None): result = etree.tostring(html, pretty_print=True) return result - - -def _get_package_extras(pkg_id): - """Returns a list of package extras by its ID - - Args: - pkg_id (str): an ID of package - - Returns: - List[PackageExtra]: a list of package extras - """ - return model.meta.Session.query(PackageExtra) \ - .filter_by(package_id=pkg_id) \ - .all() diff --git a/requirements.txt b/requirements.txt index 387f4171..a6e99f7d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ argparse pyparsing>=2.1.10 requests>=1.1.0 six +geojson==2.5.0 From f63797d0545407b9e50f2b11b3ee4591f56a94e6 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 10:00:21 +0200 Subject: [PATCH 02/66] Remove un-needed error function --- ckanext/spatial/plugin/__init__.py | 33 ++++-------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index aaa272f5..26fa4b18 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -53,28 +53,6 @@ def check_geoalchemy_requirement(): log = getLogger(__name__) -def package_error_summary(error_dict): - ''' Do some i18n stuff on the error_dict keys ''' - - def prettify(field_name): - field_name = re.sub('(? Date: Wed, 24 Aug 2022 10:14:54 +0200 Subject: [PATCH 03/66] Remove support for old GeoAlchemy library Not needed since CKAN 2.3 --- ckanext/spatial/geoalchemy_common.py | 81 +++++++--------------------- ckanext/spatial/plugin/__init__.py | 27 ---------- requirements-py2.txt | 3 +- requirements.txt | 3 +- 4 files changed, 21 insertions(+), 93 deletions(-) diff --git a/ckanext/spatial/geoalchemy_common.py b/ckanext/spatial/geoalchemy_common.py index c76e28f7..08900d21 100644 --- a/ckanext/spatial/geoalchemy_common.py +++ b/ckanext/spatial/geoalchemy_common.py @@ -1,38 +1,14 @@ -''' -Common codebase for GeoAlchemy and GeoAlchemy2 - -It is assumed that the relevant library is already installed, as we check -it against the CKAN version on startup -''' - -from ckan.plugins import toolkit from ckan.model import meta, Session from sqlalchemy import types, Column, Table -if toolkit.check_ckan_version(min_version='2.3'): - # CKAN >= 2.3, use GeoAlchemy2 - - from geoalchemy2.elements import WKTElement - from geoalchemy2 import Geometry - from sqlalchemy import func - ST_Transform = func.ST_Transform - ST_Equals = func.ST_Equals - - legacy_geoalchemy = False -else: - # CKAN < 2.3, use GeoAlchemy +from geoalchemy2.elements import WKTElement +from geoalchemy2 import Geometry +from sqlalchemy import func +ST_Transform = func.ST_Transform +ST_Equals = func.ST_Equals - from geoalchemy import WKTSpatialElement as WKTElement - from geoalchemy import functions - ST_Transform = functions.transform - ST_Equals = functions.equals - - from geoalchemy import (Geometry, GeometryColumn, GeometryDDL, - GeometryExtensionColumn) - from geoalchemy.postgis import PGComparator - - legacy_geoalchemy = True +legacy_geoalchemy = False def postgis_version(): @@ -44,38 +20,19 @@ def postgis_version(): def setup_spatial_table(package_extent_class, db_srid=None): - if legacy_geoalchemy: - - package_extent_table = Table( - 'package_extent', meta.metadata, - Column('package_id', types.UnicodeText, primary_key=True), - GeometryExtensionColumn('the_geom', Geometry(2, srid=db_srid)) - ) - - meta.mapper( - package_extent_class, - package_extent_table, - properties={'the_geom': - GeometryColumn(package_extent_table.c.the_geom, - comparator=PGComparator)} - ) - - GeometryDDL(package_extent_table) - else: - - # PostGIS 1.5 requires management=True when defining the Geometry - # field - management = (postgis_version()[:1] == '1') - - package_extent_table = Table( - 'package_extent', meta.metadata, - Column('package_id', types.UnicodeText, primary_key=True), - Column('the_geom', Geometry('GEOMETRY', srid=db_srid, - management=management)), - extend_existing=True - ) - - meta.mapper(package_extent_class, package_extent_table) + # PostGIS 1.5 requires management=True when defining the Geometry + # field + management = (postgis_version()[:1] == '1') + + package_extent_table = Table( + 'package_extent', meta.metadata, + Column('package_id', types.UnicodeText, primary_key=True), + Column('the_geom', Geometry('GEOMETRY', srid=db_srid, + management=management)), + extend_existing=True + ) + + meta.mapper(package_extent_class, package_extent_table) return package_extent_table diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 26fa4b18..d3c0f70e 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -1,5 +1,4 @@ import os -import re import mimetypes from logging import getLogger @@ -24,32 +23,6 @@ config = tk.config - -def check_geoalchemy_requirement(): - '''Checks if a suitable geoalchemy version installed - - Checks if geoalchemy2 is present when using CKAN >= 2.3, and raises - an ImportError otherwise so users can upgrade manually. - ''' - - msg = ('This version of ckanext-spatial requires {0}. ' + - 'Please install it by running `pip install {0}`.\n' + - 'For more details see the "Troubleshooting" section of the ' + - 'install documentation') - - if tk.check_ckan_version(min_version='2.3'): - try: - import geoalchemy2 - except ImportError: - raise ImportError(msg.format('geoalchemy2')) - else: - try: - import geoalchemy - except ImportError: - raise ImportError(msg.format('geoalchemy')) - -check_geoalchemy_requirement() - log = getLogger(__name__) diff --git a/requirements-py2.txt b/requirements-py2.txt index 42163700..a748016d 100644 --- a/requirements-py2.txt +++ b/requirements-py2.txt @@ -1,6 +1,5 @@ ckantoolkit -GeoAlchemy>=0.6 -GeoAlchemy2==0.5.0 +GeoAlchemy2==0.12.3 Shapely>=1.2.13 pyproj==2.2.2 OWSLib==0.18.0 diff --git a/requirements.txt b/requirements.txt index a6e99f7d..482b723a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ ckantoolkit -GeoAlchemy>=0.6 -GeoAlchemy2==0.5.0 +GeoAlchemy2==0.12.3 Shapely>=1.2.13 pyproj==2.6.1 OWSLib==0.18.0 From 93f6f87868c89829977acc0172f194b0527e7a0d Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 11:24:30 +0200 Subject: [PATCH 04/66] [#195] Don't use PostGIS by default Consolidate all DB related modules and functions into a single module for easier encapsulation. The PostGIS database table and the functions to store PostGIS geometries are no longer called by default. There is a new config option (`ckan.spatial.use_postgis=true`) to re-enable this behaviour. This option (and the actual PostGIS modules) will be dropped in future versions. --- ckanext/spatial/geoalchemy_common.py | 42 --- .../spatial/{model => }/harvested_metadata.py | 0 ckanext/spatial/harvesters/base.py | 4 +- ckanext/spatial/harvesters/gemini.py | 2 +- ckanext/spatial/lib/__init__.py | 173 ++---------- ckanext/spatial/model/__init__.py | 11 - ckanext/spatial/model/package_extent.py | 69 ----- ckanext/spatial/plugin/__init__.py | 41 ++- ckanext/spatial/postgis/README.md | 7 + ckanext/spatial/postgis/__init__.py | 0 ckanext/spatial/postgis/model.py | 256 ++++++++++++++++++ ckanext/spatial/util.py | 19 +- ckanext/spatial/validation/validation.py | 2 +- 13 files changed, 324 insertions(+), 302 deletions(-) delete mode 100644 ckanext/spatial/geoalchemy_common.py rename ckanext/spatial/{model => }/harvested_metadata.py (100%) delete mode 100644 ckanext/spatial/model/__init__.py delete mode 100644 ckanext/spatial/model/package_extent.py create mode 100644 ckanext/spatial/postgis/README.md create mode 100644 ckanext/spatial/postgis/__init__.py create mode 100644 ckanext/spatial/postgis/model.py diff --git a/ckanext/spatial/geoalchemy_common.py b/ckanext/spatial/geoalchemy_common.py deleted file mode 100644 index 08900d21..00000000 --- a/ckanext/spatial/geoalchemy_common.py +++ /dev/null @@ -1,42 +0,0 @@ -from ckan.model import meta, Session - -from sqlalchemy import types, Column, Table - -from geoalchemy2.elements import WKTElement -from geoalchemy2 import Geometry -from sqlalchemy import func -ST_Transform = func.ST_Transform -ST_Equals = func.ST_Equals - -legacy_geoalchemy = False - - -def postgis_version(): - - result = Session.execute('SELECT postgis_lib_version()') - - return result.scalar() - - -def setup_spatial_table(package_extent_class, db_srid=None): - - # PostGIS 1.5 requires management=True when defining the Geometry - # field - management = (postgis_version()[:1] == '1') - - package_extent_table = Table( - 'package_extent', meta.metadata, - Column('package_id', types.UnicodeText, primary_key=True), - Column('the_geom', Geometry('GEOMETRY', srid=db_srid, - management=management)), - extend_existing=True - ) - - meta.mapper(package_extent_class, package_extent_table) - - return package_extent_table - - -def compare_geometry_fields(geom_field1, geom_field2): - - return Session.scalar(ST_Equals(geom_field1, geom_field2)) diff --git a/ckanext/spatial/model/harvested_metadata.py b/ckanext/spatial/harvested_metadata.py similarity index 100% rename from ckanext/spatial/model/harvested_metadata.py rename to ckanext/spatial/harvested_metadata.py diff --git a/ckanext/spatial/harvesters/base.py b/ckanext/spatial/harvesters/base.py index 625b462d..947defc4 100644 --- a/ckanext/spatial/harvesters/base.py +++ b/ckanext/spatial/harvesters/base.py @@ -31,7 +31,7 @@ from ckanext.harvest.model import HarvestObject from ckanext.spatial.validation import Validators, all_validators -from ckanext.spatial.model import ISODocument +from ckanext.spatial.harvested_metadata import ISODocument from ckanext.spatial.interfaces import ISpatialHarvester from ckantoolkit import config @@ -133,7 +133,7 @@ def guess_resource_format(resource_locator, use_mimetypes=True): resource_type = protocols.get(protocol) if resource_type: return resource_type - + url = resource_locator.get('url').lower().strip() resource_types = { diff --git a/ckanext/spatial/harvesters/gemini.py b/ckanext/spatial/harvesters/gemini.py index 18158bee..07fa0b9d 100644 --- a/ckanext/spatial/harvesters/gemini.py +++ b/ckanext/spatial/harvesters/gemini.py @@ -33,7 +33,7 @@ from ckanext.harvest.interfaces import IHarvester from ckanext.harvest.model import HarvestObject -from ckanext.spatial.model import GeminiDocument +from ckanext.spatial.harvested_metadata import GeminiDocument from ckanext.spatial.lib.csw_client import CswService from ckanext.spatial.harvesters.base import SpatialHarvester, text_traceback diff --git a/ckanext/spatial/lib/__init__.py b/ckanext/spatial/lib/__init__.py index 6798ac86..6462a85b 100644 --- a/ckanext/spatial/lib/__init__.py +++ b/ckanext/spatial/lib/__init__.py @@ -1,17 +1,7 @@ -import six import logging -from string import Template - -from ckan.model import Session, Package +import six import ckantoolkit as tk -from ckanext.spatial.model import PackageExtent -from shapely.geometry import shape - - -from ckanext.spatial.geoalchemy_common import (WKTElement, ST_Transform, - compare_geometry_fields, - ) config = tk.config log = logging.getLogger(__name__) @@ -19,71 +9,23 @@ def get_srid(crs): """Returns the SRID for the provided CRS definition - The CRS can be defined in the following formats - - urn:ogc:def:crs:EPSG::4326 - - EPSG:4326 - - 4326 - """ - - if ':' in crs: - crs = crs.split(':') - srid = crs[len(crs)-1] + The CRS can be defined in the following formats + - urn:ogc:def:crs:EPSG::4326 + - EPSG:4326 + - 4326 + """ + + if ":" in crs: + crs = crs.split(":") + srid = crs[len(crs) - 1] else: - srid = crs + srid = crs return int(srid) -def save_package_extent(package_id, geometry = None, srid = None): - '''Adds, updates or deletes the package extent geometry. - - package_id: Package unique identifier - geometry: a Python object implementing the Python Geo Interface - (i.e a loaded GeoJSON object) - srid: The spatial reference in which the geometry is provided. - If None, it defaults to the DB srid. - - Will throw ValueError if the geometry object does not provide a geo interface. - - The responsibility for calling model.Session.commit() is left to the - caller. - ''' - db_srid = int(config.get('ckan.spatial.srid', '4326')) - - - existing_package_extent = Session.query(PackageExtent).filter(PackageExtent.package_id==package_id).first() - - if geometry: - geom_obj = shape(geometry) - - if not srid: - srid = db_srid - - package_extent = PackageExtent(package_id=package_id, - the_geom=WKTElement(geom_obj.wkt, srid)) - - # Check if extent exists - if existing_package_extent: - - # If extent exists but we received no geometry, we'll delete the existing one - if not geometry: - existing_package_extent.delete() - log.debug('Deleted extent for package %s' % package_id) - else: - # Check if extent changed - if not compare_geometry_fields(package_extent.the_geom, existing_package_extent.the_geom): - # Update extent - existing_package_extent.the_geom = package_extent.the_geom - existing_package_extent.save() - log.debug('Updated extent for package %s' % package_id) - else: - log.debug('Extent for package %s unchanged' % package_id) - elif geometry: - # Insert extent - Session.add(package_extent) - log.debug('Created new extent for package %s' % package_id) def validate_bbox(bbox_values): - ''' + """ Ensures a bbox is expressed in a standard dict. bbox_values may be: @@ -97,96 +39,21 @@ def validate_bbox(bbox_values): 'maxy': 56.43} Any problems and it returns None. - ''' + """ - if isinstance(bbox_values,six.string_types): - bbox_values = bbox_values.split(',') + if isinstance(bbox_values, six.string_types): + bbox_values = bbox_values.split(",") if len(bbox_values) != 4: return None try: bbox = {} - bbox['minx'] = float(bbox_values[0]) - bbox['miny'] = float(bbox_values[1]) - bbox['maxx'] = float(bbox_values[2]) - bbox['maxy'] = float(bbox_values[3]) - except ValueError as e: + bbox["minx"] = float(bbox_values[0]) + bbox["miny"] = float(bbox_values[1]) + bbox["maxx"] = float(bbox_values[2]) + bbox["maxy"] = float(bbox_values[3]) + except ValueError: return None return bbox - -def _bbox_2_wkt(bbox, srid): - ''' - Given a bbox dictionary, return a WKTSpatialElement, transformed - into the database\'s CRS if necessary. - - returns e.g. WKTSpatialElement("POLYGON ((2 0, 2 1, 7 1, 7 0, 2 0))", 4326) - ''' - db_srid = int(config.get('ckan.spatial.srid', '4326')) - - bbox_template = Template('POLYGON (($minx $miny, $minx $maxy, $maxx $maxy, $maxx $miny, $minx $miny))') - - wkt = bbox_template.substitute(minx=bbox['minx'], - miny=bbox['miny'], - maxx=bbox['maxx'], - maxy=bbox['maxy']) - - if srid and srid != db_srid: - # Input geometry needs to be transformed to the one used on the database - input_geometry = ST_Transform(WKTElement(wkt,srid),db_srid) - else: - input_geometry = WKTElement(wkt,db_srid) - return input_geometry - -def bbox_query(bbox,srid=None): - ''' - Performs a spatial query of a bounding box. - - bbox - bounding box dict - - Returns a query object of PackageExtents, which each reference a package - by ID. - ''' - - input_geometry = _bbox_2_wkt(bbox, srid) - - extents = Session.query(PackageExtent) \ - .filter(PackageExtent.package_id==Package.id) \ - .filter(PackageExtent.the_geom.intersects(input_geometry)) \ - .filter(Package.state==u'active') - return extents - -def bbox_query_ordered(bbox, srid=None): - ''' - Performs a spatial query of a bounding box. Returns packages in order - of how similar the data\'s bounding box is to the search box (best first). - - bbox - bounding box dict - - Returns a query object of PackageExtents, which each reference a package - by ID. - ''' - - input_geometry = _bbox_2_wkt(bbox, srid) - - params = {'query_bbox': six.text_type(input_geometry), - 'query_srid': input_geometry.srid} - - # First get the area of the query box - sql = "SELECT ST_Area(ST_GeomFromText(:query_bbox, :query_srid));" - params['search_area'] = Session.execute(sql, params).fetchone()[0] - - # Uses spatial ranking method from "USGS - 2006-1279" (Lanfear) - sql = """SELECT ST_AsBinary(package_extent.the_geom) AS package_extent_the_geom, - POWER(ST_Area(ST_Intersection(package_extent.the_geom, ST_GeomFromText(:query_bbox, :query_srid))),2)/ST_Area(package_extent.the_geom)/:search_area as spatial_ranking, - package_extent.package_id AS package_id - FROM package_extent, package - WHERE package_extent.package_id = package.id - AND ST_Intersects(package_extent.the_geom, ST_GeomFromText(:query_bbox, :query_srid)) - AND package.state = 'active' - ORDER BY spatial_ranking desc""" - extents = Session.execute(sql, params).fetchall() - log.debug('Spatial results: %r', - [('%.2f' % extent.spatial_ranking, extent.package_id) for extent in extents[:20]]) - return extents diff --git a/ckanext/spatial/model/__init__.py b/ckanext/spatial/model/__init__.py deleted file mode 100644 index 8f042ace..00000000 --- a/ckanext/spatial/model/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import absolute_import -# this is a namespace package -try: - import pkg_resources - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) - -from .package_extent import * -from .harvested_metadata import * diff --git a/ckanext/spatial/model/package_extent.py b/ckanext/spatial/model/package_extent.py deleted file mode 100644 index 14464975..00000000 --- a/ckanext/spatial/model/package_extent.py +++ /dev/null @@ -1,69 +0,0 @@ -from logging import getLogger - -from sqlalchemy import Table - -from ckan.lib.base import config -from ckan import model -from ckan.model import Session -from ckan.model import meta -from ckan.model.domain_object import DomainObject - -from ckanext.spatial.geoalchemy_common import setup_spatial_table - -log = getLogger(__name__) - -package_extent_table = None - -DEFAULT_SRID = 4326 #(WGS 84) - -def setup(srid=None): - - if package_extent_table is None: - define_spatial_tables(srid) - log.debug('Spatial tables defined in memory') - - if model.package_table.exists(): - if not Table('geometry_columns',meta.metadata).exists() or \ - not Table('spatial_ref_sys',meta.metadata).exists(): - raise Exception('The spatial extension is enabled, but PostGIS ' + \ - 'has not been set up in the database. ' + \ - 'Please refer to the "Setting up PostGIS" section in the README.') - - - if not package_extent_table.exists(): - try: - package_extent_table.create() - except Exception as e: - # Make sure the table does not remain incorrectly created - # (eg without geom column or constraints) - if package_extent_table.exists(): - Session.execute('DROP TABLE package_extent') - Session.commit() - - raise e - - log.debug('Spatial tables created') - else: - log.debug('Spatial tables already exist') - # Future migrations go here - - else: - log.debug('Spatial tables creation deferred') - - -class PackageExtent(DomainObject): - def __init__(self, package_id=None, the_geom=None): - self.package_id = package_id - self.the_geom = the_geom - - -def define_spatial_tables(db_srid=None): - - global package_extent_table - - if not db_srid: - db_srid = int(config.get('ckan.spatial.srid', DEFAULT_SRID)) - else: - db_srid = int(db_srid) - - package_extent_table = setup_spatial_table(PackageExtent, db_srid) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index d3c0f70e..034426af 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -8,7 +8,6 @@ from ckan import plugins as p -from ckanext.spatial.lib import save_package_extent from ckan.lib.helpers import json if tk.check_ckan_version(min_version="2.9.0"): @@ -33,14 +32,22 @@ class SpatialMetadata(p.SingletonPlugin): p.implements(p.IConfigurer, inherit=True) p.implements(p.ITemplateHelpers, inherit=True) + use_postgis = False + # IConfigurable def configure(self, config): - from ckanext.spatial.model.package_extent import setup as setup_model - if not tk.asbool(config.get('ckan.spatial.testing', 'False')): - log.debug('Setting up the spatial model') - setup_model() + # PostGIS is no longer required, support for it will be dropped in the future + self.use_postgis = tk.asbool(config.get("ckan.spatial.use_postgis", False)) + + if self.use_postgis: + + from ckanext.spatial.postgis.model import setup as setup_model + + if not tk.asbool(config.get("ckan.spatial.testing", False)): + log.debug("Setting up the spatial model") + setup_model() # IConfigure @@ -75,7 +82,10 @@ def after_delete(self, context, data_dict): return self.after_dataset_delete(context, data_dict) def after_dataset_delete(self, context, data_dict): - save_package_extent(data_dict["id"], None) + + if self.use_postgis: + from ckanext.spatial.postgis.model import save_package_extent + save_package_extent(data_dict["id"], None) def check_spatial_extra(self, dataset_dict): ''' @@ -96,7 +106,8 @@ def check_spatial_extra(self, dataset_dict): else: geometry = extra["value"] - if geometry is None or geometry == "" or delete: + if (geometry is None or geometry == "" or delete) and self.use_postgis: + from ckanext.spatial.postgis.model import save_package_extent save_package_extent(dataset_id, None) elif not geometry: return @@ -118,13 +129,15 @@ def check_spatial_extra(self, dataset_dict): error_dict = {"spatial": [msg]} raise tk.ValidationError(error_dict) - try: - save_package_extent(dataset_id, geometry) - except Exception as e: - if bool(os.getenv('DEBUG')): - raise - error_dict = {"spatial": ["Error: {}".format(six.text_type(e))]} - raise tk.ValidationError(error_dict) + if self.use_postgis: + from ckanext.spatial.postgis.model import save_package_extent + try: + save_package_extent(dataset_id, geometry) + except Exception as e: + if bool(os.getenv('DEBUG')): + raise + error_dict = {"spatial": ["Error: {}".format(six.text_type(e))]} + raise tk.ValidationError(error_dict) # ITemplateHelpers diff --git a/ckanext/spatial/postgis/README.md b/ckanext/spatial/postgis/README.md new file mode 100644 index 00000000..ef547258 --- /dev/null +++ b/ckanext/spatial/postgis/README.md @@ -0,0 +1,7 @@ +PostGIS is no longer required, extents are not stored in a table with a geometry column +anymore by default. This modules are kept here for backwards compatibility only, and will +be removed in future versions. + +Users that need to keep storing dataset extents in PostGIS for some reason can re-enable +this behaviour by setting the `ckan.spatial.use_postgis=True` configuration option. +Again, this feature will be dropped in future versions. diff --git a/ckanext/spatial/postgis/__init__.py b/ckanext/spatial/postgis/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ckanext/spatial/postgis/model.py b/ckanext/spatial/postgis/model.py new file mode 100644 index 00000000..5a89e1d7 --- /dev/null +++ b/ckanext/spatial/postgis/model.py @@ -0,0 +1,256 @@ +import logging +from string import Template + +import six +from sqlalchemy import Table, Column, types, func + +from geoalchemy2.elements import WKTElement +from geoalchemy2 import Geometry + +from shapely.geometry import shape + +from ckan.lib.base import config +from ckan import model +from ckan.model import meta, Session, Package +from ckan.model.domain_object import DomainObject + + +log = logging.getLogger(__name__) + +package_extent_table = None + +DEFAULT_SRID = 4326 # (WGS 84) + +ST_Transform = func.ST_Transform +ST_Equals = func.ST_Equals + + +def setup(srid=None): + + if package_extent_table is None: + define_spatial_tables(srid) + log.debug("Spatial tables defined in memory") + + if model.package_table.exists(): + if ( + not Table("geometry_columns", meta.metadata).exists() + or not Table("spatial_ref_sys", meta.metadata).exists() + ): + raise Exception( + "The spatial extension is enabled, but PostGIS " + + "has not been set up in the database. " + + 'Please refer to the "Setting up PostGIS" section in the README.' + ) + + if not package_extent_table.exists(): + try: + package_extent_table.create() + except Exception as e: + # Make sure the table does not remain incorrectly created + # (eg without geom column or constraints) + if package_extent_table.exists(): + Session.execute("DROP TABLE package_extent") + Session.commit() + + raise e + + log.debug("Spatial tables created") + else: + log.debug("Spatial tables already exist") + # Future migrations go here + + else: + log.debug("Spatial tables creation deferred") + + +class PackageExtent(DomainObject): + def __init__(self, package_id=None, the_geom=None): + self.package_id = package_id + self.the_geom = the_geom + + +def define_spatial_tables(db_srid=None): + + global package_extent_table + + if not db_srid: + db_srid = int(config.get("ckan.spatial.srid", DEFAULT_SRID)) + else: + db_srid = int(db_srid) + + package_extent_table = setup_spatial_table(PackageExtent, db_srid) + + +def postgis_version(): + + result = Session.execute("SELECT postgis_lib_version()") + + return result.scalar() + + +def setup_spatial_table(package_extent_class, db_srid=None): + + # PostGIS 1.5 requires management=True when defining the Geometry + # field + management = postgis_version()[:1] == "1" + + package_extent_table = Table( + "package_extent", + meta.metadata, + Column("package_id", types.UnicodeText, primary_key=True), + Column("the_geom", Geometry("GEOMETRY", srid=db_srid, management=management)), + extend_existing=True, + ) + + meta.mapper(package_extent_class, package_extent_table) + + return package_extent_table + + +def compare_geometry_fields(geom_field1, geom_field2): + + return Session.scalar(ST_Equals(geom_field1, geom_field2)) + + +def save_package_extent(package_id, geometry=None, srid=None): + """Adds, updates or deletes the package extent geometry. + + package_id: Package unique identifier + geometry: a Python object implementing the Python Geo Interface + (i.e a loaded GeoJSON object) + srid: The spatial reference in which the geometry is provided. + If None, it defaults to the DB srid. + + Will throw ValueError if the geometry object does not provide a geo interface. + + The responsibility for calling model.Session.commit() is left to the + caller. + """ + db_srid = int(config.get("ckan.spatial.srid", "4326")) + + existing_package_extent = ( + Session.query(PackageExtent) + .filter(PackageExtent.package_id == package_id) + .first() + ) + + if geometry: + geom_obj = shape(geometry) + + if not srid: + srid = db_srid + + package_extent = PackageExtent( + package_id=package_id, the_geom=WKTElement(geom_obj.wkt, srid) + ) + + # Check if extent exists + if existing_package_extent: + + # If extent exists but we received no geometry, we'll delete the existing one + if not geometry: + existing_package_extent.delete() + log.debug("Deleted extent for package %s" % package_id) + else: + # Check if extent changed + if not compare_geometry_fields( + package_extent.the_geom, existing_package_extent.the_geom + ): + # Update extent + existing_package_extent.the_geom = package_extent.the_geom + existing_package_extent.save() + log.debug("Updated extent for package %s" % package_id) + else: + log.debug("Extent for package %s unchanged" % package_id) + elif geometry: + # Insert extent + Session.add(package_extent) + log.debug("Created new extent for package %s" % package_id) + + +def _bbox_2_wkt(bbox, srid): + """ + Given a bbox dictionary, return a WKTSpatialElement, transformed + into the database\'s CRS if necessary. + + returns e.g. WKTSpatialElement("POLYGON ((2 0, 2 1, 7 1, 7 0, 2 0))", 4326) + """ + db_srid = int(config.get("ckan.spatial.srid", "4326")) + + bbox_template = Template( + "POLYGON (($minx $miny, $minx $maxy, $maxx $maxy, $maxx $miny, $minx $miny))" + ) + + wkt = bbox_template.substitute( + minx=bbox["minx"], miny=bbox["miny"], maxx=bbox["maxx"], maxy=bbox["maxy"] + ) + + if srid and srid != db_srid: + # Input geometry needs to be transformed to the one used on the database + input_geometry = ST_Transform(WKTElement(wkt, srid), db_srid) + else: + input_geometry = WKTElement(wkt, db_srid) + return input_geometry + + +def bbox_query(bbox, srid=None): + """ + Performs a spatial query of a bounding box. + + bbox - bounding box dict + + Returns a query object of PackageExtents, which each reference a package + by ID. + """ + + input_geometry = _bbox_2_wkt(bbox, srid) + + extents = ( + Session.query(PackageExtent) + .filter(PackageExtent.package_id == Package.id) + .filter(PackageExtent.the_geom.intersects(input_geometry)) + .filter(Package.state == u"active") + ) + return extents + + +def bbox_query_ordered(bbox, srid=None): + """ + Performs a spatial query of a bounding box. Returns packages in order + of how similar the data\'s bounding box is to the search box (best first). + + bbox - bounding box dict + + Returns a query object of PackageExtents, which each reference a package + by ID. + """ + + input_geometry = _bbox_2_wkt(bbox, srid) + + params = { + "query_bbox": six.text_type(input_geometry), + "query_srid": input_geometry.srid, + } + + # First get the area of the query box + sql = "SELECT ST_Area(ST_GeomFromText(:query_bbox, :query_srid));" + params["search_area"] = Session.execute(sql, params).fetchone()[0] + + # Uses spatial ranking method from "USGS - 2006-1279" (Lanfear) + sql = """SELECT ST_AsBinary(package_extent.the_geom) AS package_extent_the_geom, + POWER(ST_Area(ST_Intersection(package_extent.the_geom, ST_GeomFromText(:query_bbox, :query_srid))),2)/ST_Area(package_extent.the_geom)/:search_area as spatial_ranking, + package_extent.package_id AS package_id + FROM package_extent, package + WHERE package_extent.package_id = package.id + AND ST_Intersects(package_extent.the_geom, ST_GeomFromText(:query_bbox, :query_srid)) + AND package.state = 'active' + ORDER BY spatial_ranking desc""" + extents = Session.execute(sql, params).fetchall() + log.debug( + "Spatial results: %r", + [ + ("%.2f" % extent.spatial_ranking, extent.package_id) + for extent in extents[:20] + ], + ) + return extents diff --git a/ckanext/spatial/util.py b/ckanext/spatial/util.py index 984e3b00..636faad7 100644 --- a/ckanext/spatial/util.py +++ b/ckanext/spatial/util.py @@ -15,10 +15,9 @@ from ckan import model from ckan.model.package_extra import PackageExtra -from ckanext.spatial.lib import save_package_extent from ckanext.spatial.lib.reports import validation_report from ckanext.spatial.harvesters import SpatialHarvester -from ckanext.spatial.model import ISODocument +from ckanext.spatial.harvested_metadata import ISODocument from ckantoolkit import config @@ -95,7 +94,7 @@ def initdb(srid=None): if srid: srid = six.text_type(srid) - from ckanext.spatial.model import setup as db_setup + from ckanext.spatial.postgis.model import setup as db_setup db_setup(srid) @@ -103,15 +102,17 @@ def initdb(srid=None): def update_extents(): - from ckan.model import PackageExtra, Package, Session - conn = Session.connection() - packages = [extra.package \ - for extra in \ - Session.query(PackageExtra).filter(PackageExtra.key == 'spatial').all()] + from ckanext.spatial.postgis.model import save_package_extent + + packages = [ + extra.package for extra in + model.Session.query(PackageExtra).filter(PackageExtra.key == 'spatial').all() + ] errors = [] count = 0 for package in packages: + geometry = None try: value = package.extras['spatial'] log.debug('Received: %r' % value) @@ -127,7 +128,7 @@ def update_extents(): save_package_extent(package.id, geometry) - Session.commit() + model.Session.commit() if errors: msg = 'Errors were found:\n%s' % '\n'.join(errors) diff --git a/ckanext/spatial/validation/validation.py b/ckanext/spatial/validation/validation.py index f89a42c5..85c0d58d 100644 --- a/ckanext/spatial/validation/validation.py +++ b/ckanext/spatial/validation/validation.py @@ -1,6 +1,6 @@ import os from pkg_resources import resource_stream -from ckanext.spatial.model import ISODocument +from ckanext.spatial.harvested_metadata import ISODocument from lxml import etree From 43e573d4da6f4f56aaa4d12396a68af54ef4d153 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 12:02:01 +0200 Subject: [PATCH 05/66] Consolidate all PostGIS tests Also remove old unused API endpoint --- ckanext/spatial/plugin/flask_plugin.py | 6 - ckanext/spatial/tests/conftest.py | 28 --- .../spatial/tests/functional/test_package.py | 141 +++------------ ckanext/spatial/tests/lib/test_spatial.py | 164 +----------------- .../tests/model/test_package_extent.py | 107 ------------ ckanext/spatial/tests/test_api.py | 2 + ckanext/spatial/views.py | 67 +++---- 7 files changed, 54 insertions(+), 461 deletions(-) delete mode 100644 ckanext/spatial/tests/model/test_package_extent.py diff --git a/ckanext/spatial/plugin/flask_plugin.py b/ckanext/spatial/plugin/flask_plugin.py index 3c77fe0d..6ef49854 100644 --- a/ckanext/spatial/plugin/flask_plugin.py +++ b/ckanext/spatial/plugin/flask_plugin.py @@ -6,14 +6,8 @@ class SpatialQueryMixin(p.SingletonPlugin): - p.implements(p.IBlueprint) p.implements(p.IClick) - # IBlueprint - - def get_blueprint(self): - return [blueprints.api] - # IClick def get_commands(self): diff --git a/ckanext/spatial/tests/conftest.py b/ckanext/spatial/tests/conftest.py index 14d42996..648639c6 100644 --- a/ckanext/spatial/tests/conftest.py +++ b/ckanext/spatial/tests/conftest.py @@ -1,38 +1,10 @@ # -*- coding: utf-8 -*- import pytest -import os -import re -from sqlalchemy import Table -from ckan.model import Session, meta -from ckanext.spatial.geoalchemy_common import postgis_version -from ckanext.spatial.model.package_extent import setup as spatial_db_setup -from ckanext.harvest.model import setup as harvest_model_setup import ckanext.harvest.model as harvest_model -def _create_postgis_extension(): - Session.execute("CREATE EXTENSION IF NOT EXISTS postgis") - Session.commit() - - -def create_postgis_tables(): - _create_postgis_extension() - - -@pytest.fixture -def clean_postgis(): - Session.execute("DROP TABLE IF EXISTS package_extent") - Session.execute("DROP EXTENSION IF EXISTS postgis CASCADE") - Session.commit() - @pytest.fixture def harvest_setup(): harvest_model.setup() - - -@pytest.fixture -def spatial_setup(): - create_postgis_tables() - spatial_db_setup() diff --git a/ckanext/spatial/tests/functional/test_package.py b/ckanext/spatial/tests/functional/test_package.py index 811b0000..67aa88db 100644 --- a/ckanext/spatial/tests/functional/test_package.py +++ b/ckanext/spatial/tests/functional/test_package.py @@ -1,21 +1,12 @@ -import json - import pytest -from ckan.model import Session -from ckan.lib.helpers import url_for - -import ckan.plugins.toolkit as tk - +import ckantoolkit as tk import ckan.tests.factories as factories - -from ckanext.spatial.model import PackageExtent from ckanext.spatial.tests.base import SpatialTestBase +import ckan.tests.helpers as helpers -if not tk.check_ckan_version(min_version="2.9"): - import ckan.tests.helpers as helpers -@pytest.mark.usefixtures('with_plugins', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') +@pytest.mark.usefixtures("with_plugins", "clean_db", "clean_index", "harvest_setup") class TestSpatialExtra(SpatialTestBase): def test_spatial_extra_base(self, app): @@ -24,108 +15,30 @@ def test_spatial_extra_base(self, app): dataset = factories.Dataset(user=user) if tk.check_ckan_version(min_version="2.9"): - offset = url_for("dataset.edit", id=dataset["id"]) + offset = tk.url_for("dataset.edit", id=dataset["id"]) else: - offset = url_for(controller="package", action="edit", id=dataset["id"]) + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) res = app.get(offset, extra_environ=env) if tk.check_ckan_version(min_version="2.9"): data = { - "name": dataset['name'], + "name": dataset["name"], "extras__0__key": u"spatial", - "extras__0__value": self.geojson_examples["point"] + "extras__0__value": self.geojson_examples["point"], } res = app.post(offset, environ_overrides=env, data=data) else: form = res.forms[1] - form['extras__0__key'] = u'spatial' - form['extras__0__value'] = self.geojson_examples['point'] - res = helpers.submit_and_follow(app, form, env, 'save') + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = self.geojson_examples["point"] + res = helpers.submit_and_follow(app, form, env, "save") assert "Error" not in res, res - package_extent = ( - Session.query(PackageExtent) - .filter(PackageExtent.package_id == dataset["id"]) - .first() - ) - - geojson = json.loads(self.geojson_examples["point"]) - - assert package_extent.package_id == dataset["id"] - from sqlalchemy import func - - assert ( - Session.query(func.ST_X(package_extent.the_geom)).first()[0] - == geojson["coordinates"][0] - ) - assert ( - Session.query(func.ST_Y(package_extent.the_geom)).first()[0] - == geojson["coordinates"][1] - ) - assert package_extent.the_geom.srid == self.db_srid - - def test_spatial_extra_edit(self, app): - - user = factories.User() - env = {"REMOTE_USER": user["name"].encode("ascii")} - dataset = factories.Dataset(user=user) - - if tk.check_ckan_version(min_version="2.9"): - offset = url_for("dataset.edit", id=dataset["id"]) - else: - offset = url_for(controller="package", action="edit", id=dataset["id"]) - res = app.get(offset, extra_environ=env) - - - if tk.check_ckan_version(min_version="2.9"): - data = { - "name": dataset['name'], - "extras__0__key": u"spatial", - "extras__0__value": self.geojson_examples["point"] - } - res = app.post(offset, environ_overrides=env, data=data) - else: - form = res.forms[1] - form['extras__0__key'] = u'spatial' - form['extras__0__value'] = self.geojson_examples['point'] - res = helpers.submit_and_follow(app, form, env, 'save') - - assert "Error" not in res, res - - res = app.get(offset, extra_environ=env) - - if tk.check_ckan_version(min_version="2.9"): - data = { - "name": dataset['name'], - "extras__0__key": u"spatial", - "extras__0__value": self.geojson_examples["polygon"] - } - res = app.post(offset, environ_overrides=env, data=data) - else: - form = res.forms[1] - form['extras__0__key'] = u'spatial' - form['extras__0__value'] = self.geojson_examples['polygon'] - res = helpers.submit_and_follow(app, form, env, 'save') - - assert "Error" not in res, res - - package_extent = ( - Session.query(PackageExtent) - .filter(PackageExtent.package_id == dataset["id"]) - .first() - ) - - assert package_extent.package_id == dataset["id"] - from sqlalchemy import func + dataset_dict = tk.get_action("package_show")({}, {"id": dataset["id"]}) - assert ( - Session.query( - func.ST_GeometryType(package_extent.the_geom) - ).first()[0] - == "ST_Polygon" - ) - assert package_extent.the_geom.srid == self.db_srid + assert dataset_dict["extras"][0]["key"] == "spatial" + assert dataset_dict["extras"][0]["value"] == self.geojson_examples["point"] def test_spatial_extra_bad_json(self, app): @@ -134,23 +47,23 @@ def test_spatial_extra_bad_json(self, app): dataset = factories.Dataset(user=user) if tk.check_ckan_version(min_version="2.9"): - offset = url_for("dataset.edit", id=dataset["id"]) + offset = tk.url_for("dataset.edit", id=dataset["id"]) else: - offset = url_for(controller="package", action="edit", id=dataset["id"]) + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) res = app.get(offset, extra_environ=env) if tk.check_ckan_version(min_version="2.9"): data = { - "name": dataset['name'], + "name": dataset["name"], "extras__0__key": u"spatial", - "extras__0__value": u'{"Type":Bad Json]' + "extras__0__value": u'{"Type":Bad Json]', } res = app.post(offset, environ_overrides=env, data=data) else: form = res.forms[1] - form['extras__0__key'] = u'spatial' - form['extras__0__value'] = u'{"Type":Bad Json]' - res = helpers.webtest_submit(form, extra_environ=env, name='save') + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = u'{"Type":Bad Json]' + res = helpers.webtest_submit(form, extra_environ=env, name="save") assert "Error" in res, res assert "Spatial" in res @@ -163,23 +76,23 @@ def test_spatial_extra_bad_geojson(self, app): dataset = factories.Dataset(user=user) if tk.check_ckan_version(min_version="2.9"): - offset = url_for("dataset.edit", id=dataset["id"]) + offset = tk.url_for("dataset.edit", id=dataset["id"]) else: - offset = url_for(controller="package", action="edit", id=dataset["id"]) + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) res = app.get(offset, extra_environ=env) if tk.check_ckan_version(min_version="2.9"): data = { - "name": dataset['name'], + "name": dataset["name"], "extras__0__key": u"spatial", - "extras__0__value": u'{"Type":"Bad_GeoJSON","a":2}' + "extras__0__value": u'{"Type":"Bad_GeoJSON","a":2}', } res = app.post(offset, environ_overrides=env, data=data) else: form = res.forms[1] - form['extras__0__key'] = u'spatial' - form['extras__0__value'] = u'{"Type":"Bad_GeoJSON","a":2}' - res = helpers.webtest_submit(form, extra_environ=env, name='save') + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = u'{"Type":"Bad_GeoJSON","a":2}' + res = helpers.webtest_submit(form, extra_environ=env, name="save") assert "Error" in res, res assert "Spatial" in res diff --git a/ckanext/spatial/tests/lib/test_spatial.py b/ckanext/spatial/tests/lib/test_spatial.py index c56e6c13..423a7996 100644 --- a/ckanext/spatial/tests/lib/test_spatial.py +++ b/ckanext/spatial/tests/lib/test_spatial.py @@ -1,66 +1,4 @@ -import six - -import time -import random - -import pytest - -from shapely.geometry import shape - -from ckan import model -from ckan import plugins -from ckan.lib.helpers import json -from ckan.logic.action.create import package_create -from ckan.lib.munge import munge_title_to_name - -from ckanext.spatial.model import PackageExtent -from ckanext.spatial.lib import validate_bbox, bbox_query, bbox_query_ordered -from ckanext.spatial.geoalchemy_common import ( - WKTElement, - compare_geometry_fields, -) -from ckanext.spatial.tests.base import SpatialTestBase - - -def create_package(**package_dict): - user = plugins.toolkit.get_action("get_site_user")( - {"model": model, "ignore_auth": True}, {} - ) - context = { - "model": model, - "session": model.Session, - "user": user["name"], - "extras_as_string": True, - "api_version": 2, - "ignore_auth": True, - } - package_dict = package_create(context, package_dict) - return context.get("id") - - -@pytest.mark.usefixtures('with_plugins', 'clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') -class TestCompareGeometries(SpatialTestBase): - def _get_extent_object(self, geometry): - if isinstance(geometry, six.string_types): - geometry = json.loads(geometry) - geom_obj = shape(geometry) - return PackageExtent( - package_id="xxx", the_geom=WKTElement(geom_obj.wkt, 4326) - ) - - def test_same_points(self): - - extent1 = self._get_extent_object(self.geojson_examples["point"]) - extent2 = self._get_extent_object(self.geojson_examples["point"]) - - assert compare_geometry_fields(extent1.the_geom, extent2.the_geom) - - def test_different_points(self): - - extent1 = self._get_extent_object(self.geojson_examples["point"]) - extent2 = self._get_extent_object(self.geojson_examples["point_2"]) - - assert not compare_geometry_fields(extent1.the_geom, extent2.the_geom) +from ckanext.spatial.lib import validate_bbox class TestValidateBbox(object): @@ -68,108 +6,16 @@ class TestValidateBbox(object): def test_string(self): res = validate_bbox("-4.96,55.70,-3.78,56.43") - assert(res == self.bbox_dict) + assert res == self.bbox_dict def test_list(self): res = validate_bbox([-4.96, 55.70, -3.78, 56.43]) - assert(res == self.bbox_dict) + assert res == self.bbox_dict def test_bad(self): res = validate_bbox([-4.96, 55.70, -3.78]) - assert(res is None) + assert res is None def test_bad_2(self): res = validate_bbox("random") - assert(res is None) - - -def bbox_2_geojson(bbox_dict): - return ( - '{"type":"Polygon","coordinates":[[[%(minx)s, %(miny)s],' - '[%(minx)s, %(maxy)s], [%(maxx)s, %(maxy)s], ' - '[%(maxx)s, %(miny)s], [%(minx)s, %(miny)s]]]}' - % bbox_dict - ) - - -class SpatialQueryTestBase(SpatialTestBase): - """Base class for tests of spatial queries""" - - miny = 0 - maxy = 1 - - def initial_data(self): - for fixture_x in self.fixtures_x: - bbox = self.x_values_to_bbox(fixture_x) - bbox_geojson = bbox_2_geojson(bbox) - create_package( - name=munge_title_to_name(six.text_type(fixture_x)), - title=six.text_type(fixture_x), - extras=[{"key": "spatial", "value": bbox_geojson}], - ) - - @classmethod - def x_values_to_bbox(cls, x_tuple): - return { - "minx": x_tuple[0], - "maxx": x_tuple[1], - "miny": cls.miny, - "maxy": cls.maxy, - } - - -@pytest.mark.usefixtures('with_plugins', 'clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') -class TestBboxQuery(SpatialQueryTestBase): - # x values for the fixtures - fixtures_x = [(0, 1), (0, 3), (0, 4), (4, 5), (6, 7)] - - def test_query(self): - self.initial_data() - bbox_dict = self.x_values_to_bbox((2, 5)) - package_ids = [res.package_id for res in bbox_query(bbox_dict)] - package_titles = [model.Package.get(id_).title for id_ in package_ids] - assert(set(package_titles) == {"(0, 3)", "(0, 4)", "(4, 5)"}) - - -@pytest.mark.usefixtures('with_plugins', 'clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') -class TestBboxQueryOrdered(SpatialQueryTestBase): - # x values for the fixtures - fixtures_x = [(0, 9), (1, 8), (2, 7), (3, 6), (4, 5), (8, 9)] - - def test_query(self): - self.initial_data() - bbox_dict = self.x_values_to_bbox((2, 7)) - q = bbox_query_ordered(bbox_dict) - package_ids = [res.package_id for res in q] - package_titles = [model.Package.get(id_).title for id_ in package_ids] - # check the right items are returned - assert( - set(package_titles) == - set(("(0, 9)", "(1, 8)", "(2, 7)", "(3, 6)", "(4, 5)")) - ) - # check the order is good - assert( - package_titles == ["(2, 7)", "(1, 8)", "(3, 6)", "(0, 9)", "(4, 5)"] - ) - - -@pytest.mark.usefixtures('with_plugins', 'clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') -class TestBboxQueryPerformance(SpatialQueryTestBase): - # x values for the fixtures - fixtures_x = [ - (random.uniform(0, 3), random.uniform(3, 9)) for x in range(10) - ] # increase the number to 1000 say - - def test_query(self): - bbox_dict = self.x_values_to_bbox((2, 7)) - t0 = time.time() - bbox_query(bbox_dict) - t1 = time.time() - print("bbox_query took: ", t1 - t0) - - def test_query_ordered(self): - bbox_dict = self.x_values_to_bbox((2, 7)) - t0 = time.time() - bbox_query_ordered(bbox_dict) - t1 = time.time() - print("bbox_query_ordered took: ", t1 - t0) + assert res is None diff --git a/ckanext/spatial/tests/model/test_package_extent.py b/ckanext/spatial/tests/model/test_package_extent.py deleted file mode 100644 index 23fcf6f0..00000000 --- a/ckanext/spatial/tests/model/test_package_extent.py +++ /dev/null @@ -1,107 +0,0 @@ -import pytest - -from shapely.geometry import shape - -from ckan.model import Session -from ckan.lib.helpers import json - -import ckan.tests.factories as factories - -from ckanext.spatial.model import PackageExtent -from ckanext.spatial.geoalchemy_common import WKTElement, legacy_geoalchemy -from ckanext.spatial.tests.base import SpatialTestBase - - -@pytest.mark.usefixtures('with_plugins', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') -class TestPackageExtent(SpatialTestBase): - def test_create_extent(self): - - package = factories.Dataset() - - geojson = json.loads(self.geojson_examples["point"]) - - geom_obj = shape(geojson) - package_extent = PackageExtent( - package_id=package["id"], - the_geom=WKTElement(geom_obj.wkt, self.db_srid), - ) - package_extent.save() - - assert(package_extent.package_id == package["id"]) - if legacy_geoalchemy: - assert( - Session.scalar(package_extent.the_geom.x) == - geojson["coordinates"][0] - ) - assert( - Session.scalar(package_extent.the_geom.y) == - geojson["coordinates"][1] - ) - assert( - Session.scalar(package_extent.the_geom.srid) == self.db_srid - ) - else: - from sqlalchemy import func - - assert( - Session.query(func.ST_X(package_extent.the_geom)).first()[0] == - geojson["coordinates"][0] - ) - assert( - Session.query(func.ST_Y(package_extent.the_geom)).first()[0] == - geojson["coordinates"][1] - ) - assert(package_extent.the_geom.srid == self.db_srid) - - def test_update_extent(self): - - package = factories.Dataset() - - geojson = json.loads(self.geojson_examples["point"]) - - geom_obj = shape(geojson) - package_extent = PackageExtent( - package_id=package["id"], - the_geom=WKTElement(geom_obj.wkt, self.db_srid), - ) - package_extent.save() - if legacy_geoalchemy: - assert( - Session.scalar(package_extent.the_geom.geometry_type) == - "ST_Point" - ) - else: - from sqlalchemy import func - - assert( - Session.query( - func.ST_GeometryType(package_extent.the_geom) - ).first()[0] == - "ST_Point" - ) - - # Update the geometry (Point -> Polygon) - geojson = json.loads(self.geojson_examples["polygon"]) - - geom_obj = shape(geojson) - package_extent.the_geom = WKTElement(geom_obj.wkt, self.db_srid) - package_extent.save() - - assert(package_extent.package_id == package["id"]) - if legacy_geoalchemy: - assert( - Session.scalar(package_extent.the_geom.geometry_type) == - "ST_Polygon" - ) - assert( - Session.scalar(package_extent.the_geom.srid) == self.db_srid - ) - else: - assert( - Session.query( - func.ST_GeometryType(package_extent.the_geom) - ).first()[0] == - "ST_Polygon" - ) - assert(package_extent.the_geom.srid == self.db_srid) - diff --git a/ckanext/spatial/tests/test_api.py b/ckanext/spatial/tests/test_api.py index 2542a19f..fd42c692 100644 --- a/ckanext/spatial/tests/test_api.py +++ b/ckanext/spatial/tests/test_api.py @@ -15,6 +15,8 @@ "dateline2": '{"type":"Polygon","coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}', } +# TODO: migrate to Solr +@pytest.skip(reason="These tests need to be migrated to Solr") class TestAction(SpatialTestBase): @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query(self): diff --git a/ckanext/spatial/views.py b/ckanext/spatial/views.py index 3c4d0824..7e7834f5 100644 --- a/ckanext/spatial/views.py +++ b/ckanext/spatial/views.py @@ -6,52 +6,22 @@ import ckan.lib.helpers as h import ckan.plugins.toolkit as tk -from ckantoolkit import request -from ckan.views.api import _finish_ok, _finish_bad_request -from ckanext.spatial.lib import get_srid, validate_bbox, bbox_query from ckanext.spatial import util log = logging.getLogger(__name__) -api = Blueprint("spatial_api", __name__) - - -def spatial_query(register): - error_400_msg = \ - 'Please provide a suitable bbox parameter [minx,miny,maxx,maxy]' - - if 'bbox' not in request.args: - return _finish_bad_request(error_400_msg) - - bbox = validate_bbox(request.params['bbox']) - - if not bbox: - return _finish_bad_request(error_400_msg) - - srid = get_srid(request.args.get('crs')) if 'crs' in \ - request.args else None - - extents = bbox_query(bbox, srid) - - ids = [extent.package_id for extent in extents] - output = dict(count=len(ids), results=ids) - - return _finish_ok(output) - - -api.add_url_rule('/api/2/search//geo', view_func=spatial_query) harvest_metadata = Blueprint("spatial_harvest_metadata", __name__) def harvest_object_redirect_xml(id): - return h.redirect_to('/harvest/object/{}'.format(id)) + return h.redirect_to("/harvest/object/{}".format(id)) def harvest_object_redirect_html(id): - return h.redirect_to('/harvest/object/{}/html'.format(id)) + return h.redirect_to("/harvest/object/{}/html".format(id)) def display_xml_original(id): @@ -60,9 +30,9 @@ def display_xml_original(id): if not content: return tk.abort(404) - headers = {'Content-Type': 'application/xml; charset=utf-8'} + headers = {"Content-Type": "application/xml; charset=utf-8"} - if '\n' + content return make_response((content, 200, headers)) @@ -72,7 +42,7 @@ def display_html(id): if not content: return tk.abort(404) - headers = {'Content-Type': 'text/html; charset=utf-8'} + headers = {"Content-Type": "text/html; charset=utf-8"} xslt_package, xslt_path = util.get_xslt() content = util.transform_to_html(content, xslt_package, xslt_path) @@ -84,21 +54,24 @@ def display_html_original(id): if content is None: return tk.abort(404) - headers = {'Content-Type': 'text/html; charset=utf-8'} + headers = {"Content-Type": "text/html; charset=utf-8"} xslt_package, xslt_path = util.get_xslt(original=True) content = util.transform_to_html(content, xslt_package, xslt_path) return make_response((content, 200, headers)) -harvest_metadata.add_url_rule('/api/2/rest/harvestobject//xml', - view_func=harvest_object_redirect_xml) -harvest_metadata.add_url_rule('/api/2/rest/harvestobject//html', - view_func=harvest_object_redirect_html) - -harvest_metadata.add_url_rule('/harvest/object//original', - view_func=display_xml_original) -harvest_metadata.add_url_rule('/harvest/object//html', - view_func=display_html) -harvest_metadata.add_url_rule('/harvest/object//html/original', - view_func=display_html_original) +harvest_metadata.add_url_rule( + "/api/2/rest/harvestobject//xml", view_func=harvest_object_redirect_xml +) +harvest_metadata.add_url_rule( + "/api/2/rest/harvestobject//html", view_func=harvest_object_redirect_html +) + +harvest_metadata.add_url_rule( + "/harvest/object//original", view_func=display_xml_original +) +harvest_metadata.add_url_rule("/harvest/object//html", view_func=display_html) +harvest_metadata.add_url_rule( + "/harvest/object//html/original", view_func=display_html_original +) From a14dcf31096d206f9e5e310d2996a1e98b465858 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 12:50:04 +0200 Subject: [PATCH 06/66] Fix check spatial field logic --- ckanext/spatial/plugin/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 034426af..7f376518 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -76,7 +76,7 @@ def after_update(self, context, data_dict): return self.after_dataset_update(context, data_dict) def after_dataset_update(self, context, data_dict): - self.check_spatial_extra(data_dict) + self.check_spatial_extra(data_dict, update=True) def after_delete(self, context, data_dict): return self.after_dataset_delete(context, data_dict) @@ -87,7 +87,7 @@ def after_dataset_delete(self, context, data_dict): from ckanext.spatial.postgis.model import save_package_extent save_package_extent(data_dict["id"], None) - def check_spatial_extra(self, dataset_dict): + def check_spatial_extra(self, dataset_dict, update=False): ''' For a given dataset, looks at the spatial extent (as given in the "spatial" field/extra in GeoJSON format) and stores it in the database. @@ -106,9 +106,12 @@ def check_spatial_extra(self, dataset_dict): else: geometry = extra["value"] - if (geometry is None or geometry == "" or delete) and self.use_postgis: + if ((geometry is None or geometry == "" or delete) + and update + and self.use_postgis): from ckanext.spatial.postgis.model import save_package_extent save_package_extent(dataset_id, None) + return elif not geometry: return @@ -116,7 +119,7 @@ def check_spatial_extra(self, dataset_dict): try: log.debug("Received geometry: {}".format(geometry)) - geometry = geojson.loads(geometry) + geometry = geojson.loads(six.text_type(geometry)) except ValueError as e: error_dict = { "spatial": ["Error decoding JSON object: {}".format(six.text_type(e))]} From 6f8870ee58bfe077c742c84ff3e045460694302a Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:00:00 +0200 Subject: [PATCH 07/66] Actually add the postgis tests --- .../tests/postgis/test_package_extent.py | 442 ++++++++++++++++++ 1 file changed, 442 insertions(+) create mode 100644 ckanext/spatial/tests/postgis/test_package_extent.py diff --git a/ckanext/spatial/tests/postgis/test_package_extent.py b/ckanext/spatial/tests/postgis/test_package_extent.py new file mode 100644 index 00000000..b78a5a1a --- /dev/null +++ b/ckanext/spatial/tests/postgis/test_package_extent.py @@ -0,0 +1,442 @@ +import pytest +import six +import time +import random + +from shapely.geometry import shape + +from sqlalchemy import func + +import ckantoolkit as tk +from ckantoolkit.tests import helpers + +from ckan import model +from ckan.model import Session +from ckan.lib.helpers import json + +from ckan.lib.munge import munge_title_to_name + +import ckan.tests.factories as factories + +from ckanext.spatial.postgis.model import ( + PackageExtent, + WKTElement, # type: ignore + bbox_query, + bbox_query_ordered, + compare_geometry_fields, + setup as spatial_db_setup, +) + +from ckanext.spatial.tests.base import SpatialTestBase + + +def _create_postgis_extension(): + Session.execute("CREATE EXTENSION IF NOT EXISTS postgis") + Session.commit() + + +def create_postgis_tables(): + _create_postgis_extension() + + +@pytest.fixture +def clean_postgis(): + Session.execute("DROP TABLE IF EXISTS package_extent") + Session.execute("DROP EXTENSION IF EXISTS postgis CASCADE") + Session.commit() + + +@pytest.fixture +def spatial_setup(): + create_postgis_tables() + spatial_db_setup() + + +pytestmark = pytest.mark.skipif( + tk.asbool(tk.config.get('ckan.spatial.use_postgis', False)) is False, + reason="PostGIS is no longer used by default" +) + + +@pytest.mark.usefixtures( + "with_plugins", "clean_postgis", "clean_db", "clean_index", "harvest_setup", "spatial_setup" +) +class TestPackageExtent(SpatialTestBase): + def test_create_extent(self): + + package = factories.Dataset() + + geojson = json.loads(self.geojson_examples["point"]) + + geom_obj = shape(geojson) + package_extent = PackageExtent( + package_id=package["id"], + the_geom=WKTElement(geom_obj.wkt, self.db_srid), + ) + package_extent.save() + + assert package_extent.package_id == package["id"] + + assert ( + Session.query(func.ST_X(package_extent.the_geom)).first()[0] + == geojson["coordinates"][0] + ) + assert ( + Session.query(func.ST_Y(package_extent.the_geom)).first()[0] + == geojson["coordinates"][1] + ) + assert package_extent.the_geom.srid == self.db_srid # type: ignore + + def test_update_extent(self): + + package = factories.Dataset() + + geojson = json.loads(self.geojson_examples["point"]) + + geom_obj = shape(geojson) + package_extent = PackageExtent( + package_id=package["id"], + the_geom=WKTElement(geom_obj.wkt, self.db_srid), + ) + package_extent.save() + assert ( + Session.query(func.ST_GeometryType(package_extent.the_geom)).first()[0] + == "ST_Point" + ) + + # Update the geometry (Point -> Polygon) + geojson = json.loads(self.geojson_examples["polygon"]) + + geom_obj = shape(geojson) + package_extent.the_geom = WKTElement(geom_obj.wkt, self.db_srid) + package_extent.save() + + assert package_extent.package_id == package["id"] + assert ( + Session.query(func.ST_GeometryType(package_extent.the_geom)).first()[0] + == "ST_Polygon" + ) + assert package_extent.the_geom.srid == self.db_srid + + +def create_package(**package_dict): + user = tk.get_action("get_site_user")({"model": model, "ignore_auth": True}, {}) + context = { + "model": model, + "session": model.Session, + "user": user["name"], + "extras_as_string": True, + "api_version": 2, + "ignore_auth": True, + } + package_dict = tk.get_action("package_create")(context, package_dict) + return context.get("id") + + +@pytest.mark.usefixtures( + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", +) +class TestCompareGeometries(SpatialTestBase): + def _get_extent_object(self, geometry): + if isinstance(geometry, six.string_types): + geometry = json.loads(geometry) + geom_obj = shape(geometry) + return PackageExtent(package_id="xxx", the_geom=WKTElement(geom_obj.wkt, 4326)) + + def test_same_points(self): + + extent1 = self._get_extent_object(self.geojson_examples["point"]) + extent2 = self._get_extent_object(self.geojson_examples["point"]) + + assert compare_geometry_fields(extent1.the_geom, extent2.the_geom) + + def test_different_points(self): + + extent1 = self._get_extent_object(self.geojson_examples["point"]) + extent2 = self._get_extent_object(self.geojson_examples["point_2"]) + + assert not compare_geometry_fields(extent1.the_geom, extent2.the_geom) + + +def bbox_2_geojson(bbox_dict): + return ( + '{"type":"Polygon","coordinates":[[[%(minx)s, %(miny)s],' + "[%(minx)s, %(maxy)s], [%(maxx)s, %(maxy)s], " + "[%(maxx)s, %(miny)s], [%(minx)s, %(miny)s]]]}" % bbox_dict + ) + + +class SpatialQueryTestBase(SpatialTestBase): + """Base class for tests of spatial queries""" + + miny = 0 + maxy = 1 + + def initial_data(self): + for fixture_x in self.fixtures_x: # type: ignore + bbox = self.x_values_to_bbox(fixture_x) + bbox_geojson = bbox_2_geojson(bbox) + create_package( + name=munge_title_to_name(six.text_type(fixture_x)), + title=six.text_type(fixture_x), + extras=[{"key": "spatial", "value": bbox_geojson}], + ) + + @classmethod + def x_values_to_bbox(cls, x_tuple): + return { + "minx": x_tuple[0], + "maxx": x_tuple[1], + "miny": cls.miny, + "maxy": cls.maxy, + } + + +@pytest.mark.usefixtures( + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", +) +class TestBboxQuery(SpatialQueryTestBase): + # x values for the fixtures + fixtures_x = [(0, 1), (0, 3), (0, 4), (4, 5), (6, 7)] + + def test_query(self): + self.initial_data() + bbox_dict = self.x_values_to_bbox((2, 5)) + package_ids = [res.package_id for res in bbox_query(bbox_dict)] + package_titles = [model.Package.get(id_).title for id_ in package_ids] + assert set(package_titles) == {"(0, 3)", "(0, 4)", "(4, 5)"} + + +@pytest.mark.usefixtures( + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", +) +class TestBboxQueryOrdered(SpatialQueryTestBase): + # x values for the fixtures + fixtures_x = [(0, 9), (1, 8), (2, 7), (3, 6), (4, 5), (8, 9)] + + def test_query(self): + self.initial_data() + bbox_dict = self.x_values_to_bbox((2, 7)) + q = bbox_query_ordered(bbox_dict) + package_ids = [res.package_id for res in q] + package_titles = [model.Package.get(id_).title for id_ in package_ids] + # check the right items are returned + assert set(package_titles) == set( + ("(0, 9)", "(1, 8)", "(2, 7)", "(3, 6)", "(4, 5)") + ) + # check the order is good + assert package_titles == ["(2, 7)", "(1, 8)", "(3, 6)", "(0, 9)", "(4, 5)"] + + +@pytest.mark.usefixtures( + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", +) +class TestBboxQueryPerformance(SpatialQueryTestBase): + # x values for the fixtures + fixtures_x = [ + (random.uniform(0, 3), random.uniform(3, 9)) for x in range(10) + ] # increase the number to 1000 say + + def test_query(self): + bbox_dict = self.x_values_to_bbox((2, 7)) + t0 = time.time() + bbox_query(bbox_dict) + t1 = time.time() + print("bbox_query took: ", t1 - t0) + + def test_query_ordered(self): + bbox_dict = self.x_values_to_bbox((2, 7)) + t0 = time.time() + bbox_query_ordered(bbox_dict) + t1 = time.time() + print("bbox_query_ordered took: ", t1 - t0) + + +@pytest.mark.usefixtures( + "with_plugins", "clean_postgis", "clean_db", "clean_index", "harvest_setup", "spatial_setup" +) +class TestSpatialExtra(SpatialTestBase): + def test_spatial_extra_base(self, app): + + user = factories.User() + env = {"REMOTE_USER": user["name"].encode("ascii")} + dataset = factories.Dataset(user=user) + + if tk.check_ckan_version(min_version="2.9"): + offset = tk.url_for("dataset.edit", id=dataset["id"]) + else: + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) + res = app.get(offset, extra_environ=env) + + if tk.check_ckan_version(min_version="2.9"): + data = { + "name": dataset["name"], + "extras__0__key": u"spatial", + "extras__0__value": self.geojson_examples["point"], + } + res = app.post(offset, environ_overrides=env, data=data) + else: + form = res.forms[1] + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = self.geojson_examples["point"] + res = helpers.submit_and_follow(app, form, env, "save") + + assert "Error" not in res, res + + package_extent = ( + Session.query(PackageExtent) + .filter(PackageExtent.package_id == dataset["id"]) + .first() + ) + + geojson = json.loads(self.geojson_examples["point"]) + + assert package_extent.package_id == dataset["id"] + from sqlalchemy import func + + assert ( + Session.query(func.ST_X(package_extent.the_geom)).first()[0] + == geojson["coordinates"][0] + ) + assert ( + Session.query(func.ST_Y(package_extent.the_geom)).first()[0] + == geojson["coordinates"][1] + ) + assert package_extent.the_geom.srid == self.db_srid + + def test_spatial_extra_edit(self, app): + + user = factories.User() + env = {"REMOTE_USER": user["name"].encode("ascii")} + dataset = factories.Dataset(user=user) + + if tk.check_ckan_version(min_version="2.9"): + offset = tk.url_for("dataset.edit", id=dataset["id"]) + else: + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) + res = app.get(offset, extra_environ=env) + + if tk.check_ckan_version(min_version="2.9"): + data = { + "name": dataset["name"], + "extras__0__key": u"spatial", + "extras__0__value": self.geojson_examples["point"], + } + res = app.post(offset, environ_overrides=env, data=data) + else: + form = res.forms[1] + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = self.geojson_examples["point"] + res = helpers.submit_and_follow(app, form, env, "save") + + assert "Error" not in res, res + + res = app.get(offset, extra_environ=env) + + if tk.check_ckan_version(min_version="2.9"): + data = { + "name": dataset["name"], + "extras__0__key": u"spatial", + "extras__0__value": self.geojson_examples["polygon"], + } + res = app.post(offset, environ_overrides=env, data=data) + else: + form = res.forms[1] + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = self.geojson_examples["polygon"] + res = helpers.submit_and_follow(app, form, env, "save") + + assert "Error" not in res, res + + package_extent = ( + Session.query(PackageExtent) + .filter(PackageExtent.package_id == dataset["id"]) + .first() + ) + + assert package_extent.package_id == dataset["id"] + from sqlalchemy import func + + assert ( + Session.query(func.ST_GeometryType(package_extent.the_geom)).first()[0] + == "ST_Polygon" + ) + assert package_extent.the_geom.srid == self.db_srid + + def test_spatial_extra_bad_json(self, app): + + user = factories.User() + env = {"REMOTE_USER": user["name"].encode("ascii")} + dataset = factories.Dataset(user=user) + + if tk.check_ckan_version(min_version="2.9"): + offset = tk.url_for("dataset.edit", id=dataset["id"]) + else: + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) + res = app.get(offset, extra_environ=env) + + if tk.check_ckan_version(min_version="2.9"): + data = { + "name": dataset["name"], + "extras__0__key": u"spatial", + "extras__0__value": u'{"Type":Bad Json]', + } + res = app.post(offset, environ_overrides=env, data=data) + else: + form = res.forms[1] + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = u'{"Type":Bad Json]' + res = helpers.webtest_submit(form, extra_environ=env, name="save") + + assert "Error" in res, res + assert "Spatial" in res + assert "Error decoding JSON object" in res + + def test_spatial_extra_bad_geojson(self, app): + + user = factories.User() + env = {"REMOTE_USER": user["name"].encode("ascii")} + dataset = factories.Dataset(user=user) + + if tk.check_ckan_version(min_version="2.9"): + offset = tk.url_for("dataset.edit", id=dataset["id"]) + else: + offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) + res = app.get(offset, extra_environ=env) + + if tk.check_ckan_version(min_version="2.9"): + data = { + "name": dataset["name"], + "extras__0__key": u"spatial", + "extras__0__value": u'{"Type":"Bad_GeoJSON","a":2}', + } + res = app.post(offset, environ_overrides=env, data=data) + else: + form = res.forms[1] + form["extras__0__key"] = u"spatial" + form["extras__0__value"] = u'{"Type":"Bad_GeoJSON","a":2}' + res = helpers.webtest_submit(form, extra_environ=env, name="save") + + assert "Error" in res, res + assert "Spatial" in res + assert "Error creating geometry" in res From f12464972c5e6c9176a91f41b094f7b1ce188fa0 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:00:32 +0200 Subject: [PATCH 08/66] Separate test workflows for postgis and default setup --- .github/workflows/test-postgis.yml | 104 +++++++++++++++++++++++++++++ .github/workflows/test.yml | 13 +--- requirements-postgis.txt | 1 + requirements-py2.txt | 2 +- requirements.txt | 1 - test-postgis.ini | 50 ++++++++++++++ 6 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/test-postgis.yml create mode 100644 requirements-postgis.txt create mode 100644 test-postgis.ini diff --git a/.github/workflows/test-postgis.yml b/.github/workflows/test-postgis.yml new file mode 100644 index 00000000..1de5ee25 --- /dev/null +++ b/.github/workflows/test-postgis.yml @@ -0,0 +1,104 @@ +name: Tests +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.6' + - name: Install requirements + run: pip install flake8 pycodestyle + - name: Check syntax + run: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics --exclude ckan + test: + needs: lint + strategy: + matrix: + ckan-version: [master, 2.9, 2.9-py2, 2.8, 2.7] + fail-fast: false + + name: CKAN ${{ matrix.ckan-version }} + runs-on: ubuntu-latest + container: + image: openknowledge/ckan-dev:${{ matrix.ckan-version }} + services: + solr: + image: ckan/ckan-solr-dev:${{ matrix.ckan-version }} + postgres: + image: postgis/postgis:10-3.1 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + redis: + image: redis:3 + env: + CKAN_SQLALCHEMY_URL: postgresql://ckan_default:pass@postgres/ckan_test + CKAN_DATASTORE_WRITE_URL: postgresql://datastore_write:pass@postgres/datastore_test + CKAN_DATASTORE_READ_URL: postgresql://datastore_read:pass@postgres/datastore_test + CKAN_SOLR_URL: http://solr:8983/solr/ckan + CKAN_REDIS_URL: redis://redis:6379/1 + PGPASSWORD: postgres + + steps: + - uses: actions/checkout@v2 + - name: Create Database + run: | + psql --host=postgres --username=postgres --command="CREATE USER ckan_default WITH PASSWORD 'pass' NOSUPERUSER NOCREATEDB NOCREATEROLE;" + createdb --encoding=utf-8 --host=postgres --username=postgres --owner=ckan_default ckan_test + psql --host=postgres --username=postgres --command="CREATE USER datastore_write WITH PASSWORD 'pass' NOSUPERUSER NOCREATEDB NOCREATEROLE;" + psql --host=postgres --username=postgres --command="CREATE USER datastore_read WITH PASSWORD 'pass' NOSUPERUSER NOCREATEDB NOCREATEROLE;" + createdb --encoding=utf-8 --host=postgres --username=postgres --owner=datastore_write datastore_test + - name: Install harvester + run: | + git clone https://github.com/ckan/ckanext-harvest + cd ckanext-harvest + pip install -r pip-requirements.txt + pip install -r dev-requirements.txt + pip install -e . + - name: Install dependency (common) + run: | + apk add --no-cache \ + geos \ + geos-dev \ + proj-util \ + proj-dev \ + libxml2 \ + libxslt \ + gcc \ + libxml2-dev \ + libxslt-dev + - name: Install dependency (python2) + if: ${{ matrix.ckan-version == '2.9-py2' || matrix.ckan-version == '2.8' || matrix.ckan-version == '2.7' }} + run: | + apk add --no-cache \ + python2-dev + pip install -r requirements-py2.txt + - name: Install dependency (python3) + if: ${{ matrix.ckan-version != '2.9-py2' && matrix.ckan-version != '2.8' && matrix.ckan-version != '2.7' }} + run: | + apk add --no-cache \ + python3-dev + pip install -r requirements.txt + - name: Install requirements + run: | + + pip install -r requirements-postgis.txt + pip install -e . + # Replace default path to CKAN core config file with the one on the container + sed -i -e 's/use = config:.*/use = config:\/srv\/app\/src\/ckan\/test-core.ini/' test.ini + - name: setup postgis + run: | + psql --host=postgres --username=postgres -d ckan_test --command="ALTER ROLE ckan_default WITH superuser;" + psql --host=postgres --username=postgres -d ckan_test --command="CREATE EXTENSION postgis;" + - name: Run tests + run: pytest --ckan-ini=test-postgis.ini --cov=ckanext.spatial --cov-report=xml --cov-append --disable-warnings ckanext/spatial/tests + + - name: Upload coverage report to codecov + uses: codecov/codecov-action@v1 + with: + file: ./coverage.xml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d12e7f49..81476f35 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: solr: image: ckan/ckan-solr-dev:${{ matrix.ckan-version }} postgres: - image: postgis/postgis:10-3.1 + image: ckan/ckan-postgres-dev:${{ matrix.ckan-version }} env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -46,13 +46,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Create Database - run: | - psql --host=postgres --username=postgres --command="CREATE USER ckan_default WITH PASSWORD 'pass' NOSUPERUSER NOCREATEDB NOCREATEROLE;" - createdb --encoding=utf-8 --host=postgres --username=postgres --owner=ckan_default ckan_test - psql --host=postgres --username=postgres --command="CREATE USER datastore_write WITH PASSWORD 'pass' NOSUPERUSER NOCREATEDB NOCREATEROLE;" - psql --host=postgres --username=postgres --command="CREATE USER datastore_read WITH PASSWORD 'pass' NOSUPERUSER NOCREATEDB NOCREATEROLE;" - createdb --encoding=utf-8 --host=postgres --username=postgres --owner=datastore_write datastore_test - name: Install harvester run: | git clone https://github.com/ckan/ckanext-harvest @@ -89,10 +82,6 @@ jobs: pip install -e . # Replace default path to CKAN core config file with the one on the container sed -i -e 's/use = config:.*/use = config:\/srv\/app\/src\/ckan\/test-core.ini/' test.ini - - name: setup postgis - run: | - psql --host=postgres --username=postgres -d ckan_test --command="ALTER ROLE ckan_default WITH superuser;" - psql --host=postgres --username=postgres -d ckan_test --command="CREATE EXTENSION postgis;" - name: Run tests run: pytest --ckan-ini=test.ini --cov=ckanext.spatial --cov-report=xml --cov-append --disable-warnings ckanext/spatial/tests diff --git a/requirements-postgis.txt b/requirements-postgis.txt new file mode 100644 index 00000000..1f1cb0da --- /dev/null +++ b/requirements-postgis.txt @@ -0,0 +1 @@ +GeoAlchemy2==0.12.3 diff --git a/requirements-py2.txt b/requirements-py2.txt index a748016d..660d059a 100644 --- a/requirements-py2.txt +++ b/requirements-py2.txt @@ -1,5 +1,4 @@ ckantoolkit -GeoAlchemy2==0.12.3 Shapely>=1.2.13 pyproj==2.2.2 OWSLib==0.18.0 @@ -8,3 +7,4 @@ argparse pyparsing>=2.1.10 requests>=1.1.0 six +geojson==2.5.0 diff --git a/requirements.txt b/requirements.txt index 482b723a..315b067d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ ckantoolkit -GeoAlchemy2==0.12.3 Shapely>=1.2.13 pyproj==2.6.1 OWSLib==0.18.0 diff --git a/test-postgis.ini b/test-postgis.ini new file mode 100644 index 00000000..3e84d55c --- /dev/null +++ b/test-postgis.ini @@ -0,0 +1,50 @@ +[DEFAULT] +debug = false +# Uncomment and replace with the address which should receive any error reports +#email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5000 + + +[app:main] +use = config:test.ini +ckanext.spatial.search_backend = postgis +ckan.spatial.use_postgis = true + +# Logging configuration +[loggers] +keys = root, ckan, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_ckan] +qualname = ckan +handlers = +level = INFO + +[logger_sqlalchemy] +handlers = +qualname = sqlalchemy.engine +level = WARN + +[handler_console] +class = StreamHandler +args = (sys.stdout,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s From 470ac76cba2bc8fc3ab223b449faa8e1af94acb7 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:09:07 +0200 Subject: [PATCH 09/66] Move Postgis API tests to own module TODO: Enable tests for Solr --- ckanext/spatial/plugin/__init__.py | 2 +- .../spatial/tests/functional/test_widgets.py | 18 +- .../tests/postgis/test_package_extent.py | 160 +++++++++++++++++- 3 files changed, 165 insertions(+), 15 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 7f376518..03b854c8 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -337,7 +337,7 @@ def _params_for_solr_spatial_field_search(self, bbox, search_params): return search_params def _params_for_postgis_search(self, bbox, search_params): - from ckanext.spatial.lib import bbox_query, bbox_query_ordered + from ckanext.spatial.postgis.model import bbox_query, bbox_query_ordered from ckan.lib.search import SearchError # Note: This will be deprecated at some point in favour of the diff --git a/ckanext/spatial/tests/functional/test_widgets.py b/ckanext/spatial/tests/functional/test_widgets.py index 756a02c1..fdc9d865 100644 --- a/ckanext/spatial/tests/functional/test_widgets.py +++ b/ckanext/spatial/tests/functional/test_widgets.py @@ -1,24 +1,22 @@ import pytest -from ckan.lib.helpers import url_for from ckanext.spatial.tests.base import SpatialTestBase -import ckan.tests.factories as factories +from ckan.tests import factories import ckan.plugins.toolkit as tk + class TestSpatialWidgets(SpatialTestBase): - @pytest.mark.usefixtures('with_plugins', 'clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') + @pytest.mark.usefixtures("with_plugins", "clean_db", "clean_index", "harvest_setup") def test_dataset_map(self, app): dataset = factories.Dataset( - extras=[ - {"key": "spatial", "value": self.geojson_examples["point"]} - ], + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}], ) if tk.check_ckan_version(min_version="2.9"): - offset = url_for("dataset.read", id=dataset["id"]) + offset = tk.url_for("dataset.read", id=dataset["id"]) else: - offset = url_for(controller="package", action="read", id=dataset["id"]) + offset = tk.url_for(controller="package", action="read", id=dataset["id"]) res = app.get(offset) assert 'data-module="dataset-map"' in res @@ -26,9 +24,9 @@ def test_dataset_map(self, app): def test_spatial_search_widget(self, app): if tk.check_ckan_version(min_version="2.9"): - offset = url_for("dataset.search") + offset = tk.url_for("dataset.search") else: - offset = url_for(controller="package", action="search") + offset = tk.url_for(controller="package", action="search") res = app.get(offset) assert 'data-module="spatial-query"' in res diff --git a/ckanext/spatial/tests/postgis/test_package_extent.py b/ckanext/spatial/tests/postgis/test_package_extent.py index b78a5a1a..a98b3a2b 100644 --- a/ckanext/spatial/tests/postgis/test_package_extent.py +++ b/ckanext/spatial/tests/postgis/test_package_extent.py @@ -13,6 +13,7 @@ from ckan import model from ckan.model import Session from ckan.lib.helpers import json +from ckan.lib.search import SearchError from ckan.lib.munge import munge_title_to_name @@ -53,13 +54,18 @@ def spatial_setup(): pytestmark = pytest.mark.skipif( - tk.asbool(tk.config.get('ckan.spatial.use_postgis', False)) is False, - reason="PostGIS is no longer used by default" + tk.asbool(tk.config.get("ckan.spatial.use_postgis", False)) is False, + reason="PostGIS is no longer used by default", ) @pytest.mark.usefixtures( - "with_plugins", "clean_postgis", "clean_db", "clean_index", "harvest_setup", "spatial_setup" + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", ) class TestPackageExtent(SpatialTestBase): def test_create_extent(self): @@ -273,7 +279,12 @@ def test_query_ordered(self): @pytest.mark.usefixtures( - "with_plugins", "clean_postgis", "clean_db", "clean_index", "harvest_setup", "spatial_setup" + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", ) class TestSpatialExtra(SpatialTestBase): def test_spatial_extra_base(self, app): @@ -440,3 +451,144 @@ def test_spatial_extra_bad_geojson(self, app): assert "Error" in res, res assert "Spatial" in res assert "Error creating geometry" in res + + +extents = { + "nz": '{"type":"Polygon","coordinates":[[[174,-38],[176,-38],[176,-40],[174,-40],[174,-38]]]}', + "ohio": '{"type": "Polygon","coordinates": [[[-84,38],[-84,40],[-80,42],[-80,38],[-84,38]]]}', + "dateline": '{"type":"Polygon","coordinates":[[[169,70],[169,60],[192,60],[192,70],[169,70]]]}', + "dateline2": '{"type":"Polygon","coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}', +} + + +@pytest.mark.usefixtures( + "clean_postgis", "clean_db", "clean_index", "harvest_setup", "spatial_setup" +) +class TestSearchActionPostgis(SpatialTestBase): + def test_spatial_query(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_outside_bbox(self): + + factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-10,-20,10,20"} + ) + + assert result["count"] == 0 + + def test_spatial_query_wrong_bbox(self): + with pytest.raises(SearchError): + helpers.call_action( + "package_search", + extras={"ext_bbox": "-10,-20,10,a"}, + ) + + def test_spatial_query_nz(self): + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "56,-54,189,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_nz_wrap(self): + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-203,-54,-167,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_ohio(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["ohio"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-110,37,-78,53"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_ohio_wrap(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["ohio"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "258,37,281,51"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_1(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-197,56,-128,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_2(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "162,54,237,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_3(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline2"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-197,56,-128,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_4(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline2"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "162,54,237,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] From 5742a5fff0a7f28d1c922bf6b1a627f1a50b66e1 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:17:54 +0200 Subject: [PATCH 10/66] Test cleanups --- ckanext/spatial/tests/test_api.py | 98 ++++++++++++------------------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/ckanext/spatial/tests/test_api.py b/ckanext/spatial/tests/test_api.py index fd42c692..2985059c 100644 --- a/ckanext/spatial/tests/test_api.py +++ b/ckanext/spatial/tests/test_api.py @@ -16,39 +16,34 @@ } # TODO: migrate to Solr -@pytest.skip(reason="These tests need to be migrated to Solr") +@pytest.mark.skip(reason="These tests need to be migrated to Solr") +@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") class TestAction(SpatialTestBase): - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') + def test_spatial_query(self): dataset = factories.Dataset( - extras=[ - {"key": "spatial", "value": self.geojson_examples["point"]} - ] + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] ) result = helpers.call_action( "package_search", extras={"ext_bbox": "-180,-90,180,90"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_outside_bbox(self): factories.Dataset( - extras=[ - {"key": "spatial", "value": self.geojson_examples["point"]} - ] + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] ) result = helpers.call_action( "package_search", extras={"ext_bbox": "-10,-20,10,20"} ) - assert(result["count"] == 0) + assert result["count"] == 0 - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_wrong_bbox(self): with pytest.raises(SearchError): helpers.call_action( @@ -56,32 +51,25 @@ def test_spatial_query_wrong_bbox(self): extras={"ext_bbox": "-10,-20,10,a"}, ) - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_nz(self): - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["nz"]}] - ) + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) result = helpers.call_action( "package_search", extras={"ext_bbox": "56,-54,189,-28"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_nz_wrap(self): - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["nz"]}] - ) + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) result = helpers.call_action( "package_search", extras={"ext_bbox": "-203,-54,-167,-28"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_ohio(self): dataset = factories.Dataset( @@ -92,10 +80,9 @@ def test_spatial_query_ohio(self): "package_search", extras={"ext_bbox": "-110,37,-78,53"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_ohio_wrap(self): dataset = factories.Dataset( @@ -106,10 +93,9 @@ def test_spatial_query_ohio_wrap(self): "package_search", extras={"ext_bbox": "258,37,281,51"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_dateline_1(self): dataset = factories.Dataset( @@ -120,10 +106,9 @@ def test_spatial_query_dateline_1(self): "package_search", extras={"ext_bbox": "-197,56,-128,70"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_dateline_2(self): dataset = factories.Dataset( @@ -134,10 +119,9 @@ def test_spatial_query_dateline_2(self): "package_search", extras={"ext_bbox": "162,54,237,70"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_dateline_3(self): dataset = factories.Dataset( @@ -148,10 +132,9 @@ def test_spatial_query_dateline_3(self): "package_search", extras={"ext_bbox": "-197,56,-128,70"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures('clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') def test_spatial_query_dateline_4(self): dataset = factories.Dataset( @@ -162,11 +145,16 @@ def test_spatial_query_dateline_4(self): "package_search", extras={"ext_bbox": "162,54,237,70"} ) - assert(result["count"] == 1) - assert(result["results"][0]["id"] == dataset["id"]) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] -@pytest.mark.usefixtures('with_plugins', 'clean_postgis', 'clean_db', 'clean_index', 'harvest_setup', 'spatial_setup') +@pytest.mark.usefixtures( + "with_plugins", + "clean_db", + "clean_index", + "harvest_setup", +) class TestHarvestedMetadataAPI(SpatialTestBase): def test_api(self, app): try: @@ -177,8 +165,7 @@ def test_api(self, app): HarvestObjectExtra, ) except ImportError: - raise pytest.skip( - "The harvester extension is needed for these tests") + raise pytest.skip("The harvester extension is needed for these tests") content1 = "Content 1" ho1 = HarvestObject( @@ -210,13 +197,8 @@ def test_api(self, app): # Access object content url = "/harvest/object/{0}".format(object_id_1) r = app.get(url, status=200) - assert( - r.headers["Content-Type"] == "application/xml; charset=utf-8" - ) - assert( - r.body == - '\nContent 1' - ) + assert r.headers["Content-Type"] == "application/xml; charset=utf-8" + assert r.body == '\nContent 1' # Access original content in object extra (if present) url = "/harvest/object/{0}/original".format(object_id_1) @@ -224,11 +206,9 @@ def test_api(self, app): url = "/harvest/object/{0}/original".format(object_id_2) r = app.get(url, status=200) - assert( - r.headers["Content-Type"] == "application/xml; charset=utf-8" - ) - assert( - r.body == - '\n' + assert r.headers["Content-Type"] == "application/xml; charset=utf-8" + assert ( + r.body + == '\n' + "Original Content 2" ) From c83fde7d9de0176ab3a8755eda63919e6e1f0f2f Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:26:27 +0200 Subject: [PATCH 11/66] py2 geoalchemy requirement --- .github/workflows/test-postgis.yml | 6 +++--- requirements-postgis-py2.txt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 requirements-postgis-py2.txt diff --git a/.github/workflows/test-postgis.yml b/.github/workflows/test-postgis.yml index 1de5ee25..965681e7 100644 --- a/.github/workflows/test-postgis.yml +++ b/.github/workflows/test-postgis.yml @@ -1,4 +1,4 @@ -name: Tests +name: Legacy PostGIS Tests on: [push, pull_request] jobs: @@ -78,16 +78,16 @@ jobs: apk add --no-cache \ python2-dev pip install -r requirements-py2.txt + pip install -r requirements-postgis-py2.txt - name: Install dependency (python3) if: ${{ matrix.ckan-version != '2.9-py2' && matrix.ckan-version != '2.8' && matrix.ckan-version != '2.7' }} run: | apk add --no-cache \ python3-dev pip install -r requirements.txt + pip install -r requirements-postgis.txt - name: Install requirements run: | - - pip install -r requirements-postgis.txt pip install -e . # Replace default path to CKAN core config file with the one on the container sed -i -e 's/use = config:.*/use = config:\/srv\/app\/src\/ckan\/test-core.ini/' test.ini diff --git a/requirements-postgis-py2.txt b/requirements-postgis-py2.txt new file mode 100644 index 00000000..5c8d1027 --- /dev/null +++ b/requirements-postgis-py2.txt @@ -0,0 +1 @@ +GeoAlchemy2==0.5.0 From 81a4b1529bf86f64a5d6089eaccb5ef387759657 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:30:54 +0200 Subject: [PATCH 12/66] Don't load postgis related libraries --- .github/workflows/test-postgis.yml | 6 ++--- .github/workflows/test.yml | 6 ++--- .../tests/postgis/test_package_extent.py | 25 ++++++++++--------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-postgis.yml b/.github/workflows/test-postgis.yml index 965681e7..6084641a 100644 --- a/.github/workflows/test-postgis.yml +++ b/.github/workflows/test-postgis.yml @@ -60,7 +60,7 @@ jobs: pip install -r pip-requirements.txt pip install -r dev-requirements.txt pip install -e . - - name: Install dependency (common) + - name: Install dependencies (common) run: | apk add --no-cache \ geos \ @@ -72,14 +72,14 @@ jobs: gcc \ libxml2-dev \ libxslt-dev - - name: Install dependency (python2) + - name: Install dependencies (python2) if: ${{ matrix.ckan-version == '2.9-py2' || matrix.ckan-version == '2.8' || matrix.ckan-version == '2.7' }} run: | apk add --no-cache \ python2-dev pip install -r requirements-py2.txt pip install -r requirements-postgis-py2.txt - - name: Install dependency (python3) + - name: Install dependencies (python3) if: ${{ matrix.ckan-version != '2.9-py2' && matrix.ckan-version != '2.8' && matrix.ckan-version != '2.7' }} run: | apk add --no-cache \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81476f35..bd17d66b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,7 +53,7 @@ jobs: pip install -r pip-requirements.txt pip install -r dev-requirements.txt pip install -e . - - name: Install dependency (common) + - name: Install dependencies (common) run: | apk add --no-cache \ geos \ @@ -65,13 +65,13 @@ jobs: gcc \ libxml2-dev \ libxslt-dev - - name: Install dependency (python2) + - name: Install dependencies (python2) if: ${{ matrix.ckan-version == '2.9-py2' || matrix.ckan-version == '2.8' || matrix.ckan-version == '2.7' }} run: | apk add --no-cache \ python2-dev pip install -r requirements-py2.txt - - name: Install dependency (python3) + - name: Install dependencies (python3) if: ${{ matrix.ckan-version != '2.9-py2' && matrix.ckan-version != '2.8' && matrix.ckan-version != '2.7' }} run: | apk add --no-cache \ diff --git a/ckanext/spatial/tests/postgis/test_package_extent.py b/ckanext/spatial/tests/postgis/test_package_extent.py index a98b3a2b..c60fdf82 100644 --- a/ckanext/spatial/tests/postgis/test_package_extent.py +++ b/ckanext/spatial/tests/postgis/test_package_extent.py @@ -19,17 +19,20 @@ import ckan.tests.factories as factories -from ckanext.spatial.postgis.model import ( - PackageExtent, - WKTElement, # type: ignore - bbox_query, - bbox_query_ordered, - compare_geometry_fields, - setup as spatial_db_setup, -) - from ckanext.spatial.tests.base import SpatialTestBase +use_postgis = tk.asbool(tk.config.get("ckan.spatial.use_postgis", False)) + +if use_postgis: + from ckanext.spatial.postgis.model import ( + PackageExtent, + WKTElement, # type: ignore + bbox_query, + bbox_query_ordered, + compare_geometry_fields, + setup as spatial_db_setup, + ) + def _create_postgis_extension(): Session.execute("CREATE EXTENSION IF NOT EXISTS postgis") @@ -54,9 +57,7 @@ def spatial_setup(): pytestmark = pytest.mark.skipif( - tk.asbool(tk.config.get("ckan.spatial.use_postgis", False)) is False, - reason="PostGIS is no longer used by default", -) + use_postgis is False, reason="PostGIS is no longer used by default") @pytest.mark.usefixtures( From 6b61ac66db031282b92a56e834c84393aabd7603 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:42:00 +0200 Subject: [PATCH 13/66] Update expected error message --- ckanext/spatial/plugin/__init__.py | 2 +- ckanext/spatial/tests/functional/test_package.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 03b854c8..788927c6 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -126,7 +126,7 @@ def check_spatial_extra(self, dataset_dict, update=False): raise tk.ValidationError(error_dict) if not hasattr(geometry, "is_valid") or not geometry.is_valid: - msg = "Wrong GeoJSON object" + msg = "Error: Wrong GeoJSON object" if hasattr(geometry, "errors"): msg = msg + ": {}".format(geometry.errors()) error_dict = {"spatial": [msg]} diff --git a/ckanext/spatial/tests/functional/test_package.py b/ckanext/spatial/tests/functional/test_package.py index 67aa88db..67d01b67 100644 --- a/ckanext/spatial/tests/functional/test_package.py +++ b/ckanext/spatial/tests/functional/test_package.py @@ -67,7 +67,7 @@ def test_spatial_extra_bad_json(self, app): assert "Error" in res, res assert "Spatial" in res - assert "Error decoding JSON object" in res + assert "Wrong GeoJSON object" in res def test_spatial_extra_bad_geojson(self, app): @@ -96,4 +96,4 @@ def test_spatial_extra_bad_geojson(self, app): assert "Error" in res, res assert "Spatial" in res - assert "Error creating geometry" in res + assert "Wrong GeoJSON object" in res From 0ee15fe9974638cd55eb5ec4e0783f2c10d28a09 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:46:57 +0200 Subject: [PATCH 14/66] Downgrade geoalchemy to match CKAN core sqlalchemy requirement --- requirements-postgis.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-postgis.txt b/requirements-postgis.txt index 1f1cb0da..27895f35 100644 --- a/requirements-postgis.txt +++ b/requirements-postgis.txt @@ -1 +1 @@ -GeoAlchemy2==0.12.3 +GeoAlchemy2==0.11.1 From 2b26b7c746525eb07af2f58fda9c0e9ee94527a0 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:53:29 +0200 Subject: [PATCH 15/66] Fix wrong expected message --- ckanext/spatial/tests/functional/test_package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/spatial/tests/functional/test_package.py b/ckanext/spatial/tests/functional/test_package.py index 67d01b67..5da5f68e 100644 --- a/ckanext/spatial/tests/functional/test_package.py +++ b/ckanext/spatial/tests/functional/test_package.py @@ -67,7 +67,7 @@ def test_spatial_extra_bad_json(self, app): assert "Error" in res, res assert "Spatial" in res - assert "Wrong GeoJSON object" in res + assert "Error decoding JSON object" in res def test_spatial_extra_bad_geojson(self, app): From 322a92bcec7329ce599badd7dbb268355859e406 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 13:56:50 +0200 Subject: [PATCH 16/66] Remove old API in Pylons controller --- ckanext/spatial/controllers/api.py | 37 +------------------------ ckanext/spatial/plugin/pylons_plugin.py | 9 +----- 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/ckanext/spatial/controllers/api.py b/ckanext/spatial/controllers/api.py index ef379e57..e293bee0 100644 --- a/ckanext/spatial/controllers/api.py +++ b/ckanext/spatial/controllers/api.py @@ -1,50 +1,15 @@ import logging -from pylons import response - -from ckan.lib.base import request, abort +from ckan.lib.base import abort from ckan.controllers.api import ApiController as BaseApiController from ckan.model import Session from ckanext.harvest.model import HarvestObject, HarvestObjectExtra -from ckanext.spatial.lib import get_srid, validate_bbox, bbox_query from ckanext.spatial import util log = logging.getLogger(__name__) -class ApiController(BaseApiController): - - def spatial_query(self): - - error_400_msg = \ - 'Please provide a suitable bbox parameter [minx,miny,maxx,maxy]' - - if 'bbox' not in request.params: - abort(400, error_400_msg) - - bbox = validate_bbox(request.params['bbox']) - - if not bbox: - abort(400, error_400_msg) - - srid = get_srid(request.params.get('crs')) if 'crs' in \ - request.params else None - - extents = bbox_query(bbox, srid) - - format = request.params.get('format', '') - - return self._output_results(extents, format) - - def _output_results(self, extents, format=None): - - ids = [extent.package_id for extent in extents] - output = dict(count=len(ids), results=ids) - - return self._finish_ok(output) - - class HarvestMetadataApiController(BaseApiController): def _get_content(self, id): diff --git a/ckanext/spatial/plugin/pylons_plugin.py b/ckanext/spatial/plugin/pylons_plugin.py index 2d0bc95e..3ebe4546 100644 --- a/ckanext/spatial/plugin/pylons_plugin.py +++ b/ckanext/spatial/plugin/pylons_plugin.py @@ -1,16 +1,9 @@ import ckan.plugins as p - class SpatialQueryMixin(p.SingletonPlugin): - p.implements(p.IRoutes, inherit=True) + pass - # IRoutes - def before_map(self, map): - map.connect('api_spatial_query', '/api/2/search/{register:dataset|package}/geo', - controller='ckanext.spatial.controllers.api:ApiController', - action='spatial_query') - return map class HarvestMetadataApiMixin(p.SingletonPlugin): p.implements(p.IRoutes, inherit=True) From b4a9eba67aa1d7dade9956296f4933f0c373faff Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 16:24:14 +0200 Subject: [PATCH 17/66] Fix all tests --- .github/workflows/test-postgis.yml | 4 ++-- ckanext/spatial/controllers/api.py | 1 + ckanext/spatial/tests/postgis/test_package_extent.py | 12 +++++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-postgis.yml b/.github/workflows/test-postgis.yml index 6084641a..43fa2458 100644 --- a/.github/workflows/test-postgis.yml +++ b/.github/workflows/test-postgis.yml @@ -17,7 +17,7 @@ jobs: needs: lint strategy: matrix: - ckan-version: [master, 2.9, 2.9-py2, 2.8, 2.7] + ckan-version: [2.9, 2.9-py2, 2.8, 2.7] fail-fast: false name: CKAN ${{ matrix.ckan-version }} @@ -96,7 +96,7 @@ jobs: psql --host=postgres --username=postgres -d ckan_test --command="ALTER ROLE ckan_default WITH superuser;" psql --host=postgres --username=postgres -d ckan_test --command="CREATE EXTENSION postgis;" - name: Run tests - run: pytest --ckan-ini=test-postgis.ini --cov=ckanext.spatial --cov-report=xml --cov-append --disable-warnings ckanext/spatial/tests + run: pytest --ckan-ini=test-postgis.ini --cov=ckanext.spatial --cov-report=xml --cov-append --disable-warnings ckanext/spatial/tests/postgis - name: Upload coverage report to codecov uses: codecov/codecov-action@v1 diff --git a/ckanext/spatial/controllers/api.py b/ckanext/spatial/controllers/api.py index e293bee0..6f462c49 100644 --- a/ckanext/spatial/controllers/api.py +++ b/ckanext/spatial/controllers/api.py @@ -3,6 +3,7 @@ from ckan.lib.base import abort from ckan.controllers.api import ApiController as BaseApiController from ckan.model import Session +from ckantoolkit import response from ckanext.harvest.model import HarvestObject, HarvestObjectExtra from ckanext.spatial import util diff --git a/ckanext/spatial/tests/postgis/test_package_extent.py b/ckanext/spatial/tests/postgis/test_package_extent.py index c60fdf82..1d4ba1bc 100644 --- a/ckanext/spatial/tests/postgis/test_package_extent.py +++ b/ckanext/spatial/tests/postgis/test_package_extent.py @@ -57,7 +57,8 @@ def spatial_setup(): pytestmark = pytest.mark.skipif( - use_postgis is False, reason="PostGIS is no longer used by default") + use_postgis is False, reason="PostGIS is no longer used by default" +) @pytest.mark.usefixtures( @@ -451,7 +452,7 @@ def test_spatial_extra_bad_geojson(self, app): assert "Error" in res, res assert "Spatial" in res - assert "Error creating geometry" in res + assert "Wrong GeoJSON object" in res extents = { @@ -463,7 +464,12 @@ def test_spatial_extra_bad_geojson(self, app): @pytest.mark.usefixtures( - "clean_postgis", "clean_db", "clean_index", "harvest_setup", "spatial_setup" + "with_plugins", + "clean_postgis", + "clean_db", + "clean_index", + "harvest_setup", + "spatial_setup", ) class TestSearchActionPostgis(SpatialTestBase): def test_spatial_query(self): From 87b723e9deba80b6c6d5da6466d01649d62030bc Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 24 Aug 2022 16:25:35 +0200 Subject: [PATCH 18/66] Update docs to reflect PostGIS not being supported --- doc/csw.rst | 5 +- doc/index.rst | 9 +- doc/install.rst | 269 ++++++----------------------------------- doc/postgis-manual.rst | 43 ------- doc/spatial-search.rst | 78 +----------- 5 files changed, 43 insertions(+), 361 deletions(-) delete mode 100644 doc/postgis-manual.rst diff --git a/doc/csw.rst b/doc/csw.rst index 3c4cf235..d2e0e3ec 100644 --- a/doc/csw.rst +++ b/doc/csw.rst @@ -90,9 +90,8 @@ Setup sudo -u postgres createdb -O ckan_default pycsw -E utf-8 - It is strongly recommended that you install PostGIS in the pycsw databaset, - so its spatial functions are used. See the :ref:`install_postgis` - section for details. + It is strongly recommended that you install PostGIS in the pycsw database, + so its spatial functions are used. 3. Configure pycsw. An example configuration file is included on the source:: diff --git a/doc/index.rst b/doc/index.rst index d8b4547c..5fd0e767 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -7,11 +7,9 @@ This extension contains plugins that add geospatial capabilities to CKAN_. You should have a CKAN instance installed before adding these plugins. Head to the `CKAN documentation`_ for information on how to set up CKAN. -The extension adds a spatial field to the default CKAN dataset schema, -using PostGIS_ as the backend. This allows to perform spatial queries and -display the dataset extent on the frontend. It also provides harvesters to -import geospatial metadata into CKAN from other sources, as well as commands -to support the OGC CSW standard via pycsw_. +The extension allows to perform spatial queries and display the dataset extent +on the frontend. It also provides harvesters to import geospatial metadata into +CKAN from other sources, as well as commands to support the OGC CSW standard via pycsw_. Contents: @@ -27,6 +25,5 @@ Contents: .. _CKAN: http://ckan.org .. _CKAN Documentation: http://docs.ckan.org -.. _PostGIS: http://postgis.org .. _GeoJSON: http://geojson.org .. _pycsw: http://pycsw.org diff --git a/doc/install.rst b/doc/install.rst index eab0a97e..7f239ccd 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -4,11 +4,6 @@ Installation and Setup Check the Troubleshooting_ section if you get errors at any stage. -.. _install_postgis: - -Install PostGIS and system packages ------------------------------------ - .. warning:: If you are looking for the geospatial preview plugins to render (eg GeoJSON or WMS services), these are now located in ckanext-geoview_. They have a much simpler installation, so you can skip all the following steps if you just want the previews. @@ -17,109 +12,33 @@ Install PostGIS and system packages .. note:: The package names and paths shown are the defaults on Ubuntu installs. Adjust the package names and the paths if you are using a different platform. -All commands assume an existing CKAN database named ``ckan_default``. - -Ubuntu 14.04 (PostgreSQL 9.3 and PostGIS 2.1) -+++++++++++++++++++++++++++++++++++++++++++++ - -#. Install PostGIS:: - - sudo apt-get install postgresql-9.3-postgis-2.1 - -#. Run the following commands. The first one will create the necessary - tables and functions in the database, and the second will populate - the spatial reference table:: - - sudo -u postgres psql -d ckan_default -f /usr/share/postgresql/9.3/contrib/postgis-2.1/postgis.sql - sudo -u postgres psql -d ckan_default -f /usr/share/postgresql/9.3/contrib/postgis-2.1/spatial_ref_sys.sql - -#. Change the owner of spatial tables to the CKAN user to avoid errors later - on:: - - sudo -u postgres psql -d ckan_default -c 'ALTER VIEW geometry_columns OWNER TO ckan_default;' - sudo -u postgres psql -d ckan_default -c 'ALTER TABLE spatial_ref_sys OWNER TO ckan_default;' - -#. Execute the following command to see if PostGIS was properly - installed:: - - sudo -u postgres psql -d ckan_default -c "SELECT postgis_full_version()" - - You should get something like:: - - postgis_full_version - ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- - POSTGIS="2.1.2 r12389" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.10.1, released 2013/08/26" LIBXML="2.9.1" LIBJSON="UNKNOWN" RASTER - (1 row) - -#. Install some other packages needed by the extension dependencies:: - - sudo apt-get install python-dev libxml2-dev libxslt1-dev libgeos-c1 - - -Ubuntu 12.04 (PostgreSQL 9.1 and PostGIS 1.5) -+++++++++++++++++++++++++++++++++++++++++++++ - -.. note:: You can also install PostGIS 2.x on Ubuntu 12.04 using the packages - on the UbuntuGIS_ repository. Check the documentation there for details. - -#. Install PostGIS:: - - sudo apt-get install postgresql-9.1-postgis - -#. Run the following commands. The first one will create the necessary - tables and functions in the database, and the second will populate - the spatial reference table:: - - sudo -u postgres psql -d ckan_default -f /usr/share/postgresql/9.1/contrib/postgis-1.5/postgis.sql - sudo -u postgres psql -d ckan_default -f /usr/share/postgresql/9.1/contrib/postgis-1.5/spatial_ref_sys.sql - - .. note:: If using PostgreSQL 8.x, run the following command to enable - the necessary language:: +.. note:: Starting from ckanext-spatial 2.0.0 **PostGIS is no longer required** to use the extension, + and its use has been deprected. If for some reason you still need to use the old PostGIS backend + see :ref:`legacy_postgis`. - sudo -u postgres createlang plpgsql ckan_default - -#. Change the owner to spatial tables to the CKAN user to avoid errors later - on:: - - sudo -u postgres psql -d ckan_default -c 'ALTER TABLE geometry_columns OWNER TO ckan_default;' - sudo -u postgres psql -d ckan_default -c 'ALTER TABLE spatial_ref_sys OWNER TO ckan_default;' - -#. Execute the following command to see if PostGIS was properly - installed:: - - sudo -u postgres psql -d ckan_default -c "SELECT postgis_full_version()" - - You should get something like:: +All commands assume an existing CKAN database named ``ckan_default``. - postgis_full_version - ------------------------------------------------------------------------------------------------------ - POSTGIS="1.5.2" GEOS="3.2.2-CAPI-1.6.2" PROJ="Rel. 4.7.1, 23 September 2009" LIBXML="2.7.7" USE_STATS - (1 row) +Install the extension +--------------------- -#. Install some other packages needed by the extension dependencies:: +#. Install some packages needed by the extension dependencies:: sudo apt-get install python-dev libxml2-dev libxslt1-dev libgeos-c1 - -.. _UbuntuGIS: https://wiki.ubuntu.com/UbuntuGIS - -Install the extension ---------------------- - -1. Activate your CKAN virtual environment, for example:: +#. Activate your CKAN virtual environment, for example:: . /usr/lib/ckan/default/bin/activate -2. Install the ckanext-spatial Python package into your virtual environment:: +#. Install the ckanext-spatial Python package into your virtual environment:: pip install -e "git+https://github.com/ckan/ckanext-spatial.git#egg=ckanext-spatial" -3. Install the rest of Python modules required by the extension:: +#. Install the rest of Python modules required by the extension:: - pip install -r /usr/lib/ckan/default/src/ckanext-spatial/pip-requirements.txt + pip install -r /usr/lib/ckan/default/src/ckanext-spatial/requirements.txt -4. Restart CKAN. For example if you've deployed CKAN with Apache on Ubuntu:: +#. Restart CKAN. For example if you've deployed CKAN with Apache on Ubuntu:: sudo service apache2 reload @@ -131,29 +50,37 @@ its documentation for details on how to set it up. Configuration ------------- -Once PostGIS is installed and configured in the database, the extension needs -to create a table to store the datasets extent, called ``package_extent``. -This will happen automatically the next CKAN is restarted after adding the -plugins on the configuration ini file (eg when restarting Apache). +Add the following plugins to the ``ckan.plugins`` directive in the +CKAN ini file:: -If for some reason you need to explicitly create the table beforehand, you can -do it with the following command (with the virtualenv activated):: + ckan.plugins = spatial_metadata spatial_query - (pyenv) $ ckan --config=mysite.ini spatial initdb [srid] -On CKAN 2.8 and below use:: +.. _legacy_postgis: - (pyenv) $ paster --plugin=ckanext-spatial spatial initdb [srid] --config=mysite.ini +Legacy PostGIS support +---------------------- -You can define the SRID of the geometry column. Default is 4326. If you are not -familiar with projections, we recommend to use the default value. To know more -about PostGIS tables, see :doc:`postgis-manual` +Starting from ckanext-spatial 2.0.0 PostGIS is **no longer required** to use the extension, +and its use has been deprected. If for some reason you are using the deprecated ``postgis`` +search backend or you still need to store the dataset extents in the PostGIS +enabled ``package_extent`` table you can use the following steps, but note that this support +will removed in the future so update your workflows accordingly. -Each plugin can be enabled by adding its name to the ``ckan.plugins`` in the -CKAN ini file. For example:: +#. Add the ``ckan.spatial.use_postgis=true`` config option to your ini file. - ckan.plugins = spatial_metadata spatial_query + +#. Create the ``package_extent`` table with the following command (with the virtualenv activated):: + + (pyenv) $ ckan --config=mysite.ini spatial initdb [srid] + +On CKAN 2.8 and below use:: + + (pyenv) $ paster --plugin=ckanext-spatial spatial initdb [srid] --config=mysite.ini + +You can define the SRID of the geometry column. Default is 4326. If you are not +familiar with projections, we recommend to use the default value. When enabling the spatial metadata, you can define the projection in which extents are stored in the database with the following option. Use the EPSG code @@ -161,11 +88,6 @@ as an integer (e.g 4326, 4258, 27700, etc). It defaults to 4326:: ckan.spatial.srid = 4326 -As with any configuration change, for it to take effect you need to restart -CKAN. For example if you've deployed CKAN with Apache on Ubuntu:: - - sudo service apache2 reload - Troubleshooting --------------- @@ -176,50 +98,6 @@ extension: When upgrading the extension to a newer version +++++++++++++++++++++++++++++++++++++++++++++++ -This version of ckanext-spatial requires geoalchemy2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - File "/home/adria/dev/pyenvs/spatial/src/ckanext-spatial/ckanext/spatial/plugin.py", line 39, in - check_geoalchemy_requirement() - File "/home/adria/dev/pyenvs/spatial/src/ckanext-spatial/ckanext/spatial/plugin.py", line 37, in check_geoalchemy_requirement - raise ImportError(msg.format('geoalchemy')) - ImportError: This version of ckanext-spatial requires geoalchemy2. Please install it by running `pip install geoalchemy2`. - For more details see the "Troubleshooting" section of the install documentation - -Starting from CKAN 2.3, the spatial requires GeoAlchemy2_ instead of GeoAlchemy, as this -is incompatible with the SQLAlchemy version that CKAN core uses. GeoAlchemy2 will get -installed on a new deployment, but if you are upgrading an existing ckanext-spatial -install you'll need to install it manually. With the virtualenv CKAN is installed on -activated, run:: - - pip install GeoAlchemy2 - -Restart the server for the changes to take effect. - -AttributeError: type object 'UserDefinedType' has no attribute 'Comparator' -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - File "/home/adria/dev/pyenvs/spatial/src/ckanext-spatial/ckanext/spatial/plugin.py", line 30, in check_geoalchemy_requirement - import geoalchemy2 - File "/home/adria/dev/pyenvs/spatial/local/lib/python2.7/site-packages/geoalchemy2/__init__.py", line 1, in - from .types import ( # NOQA - File "/home/adria/dev/pyenvs/spatial/local/lib/python2.7/site-packages/geoalchemy2/types.py", line 15, in - from .comparator import BaseComparator, Comparator - File "/home/adria/dev/pyenvs/spatial/local/lib/python2.7/site-packages/geoalchemy2/comparator.py", line 52, in - class BaseComparator(UserDefinedType.Comparator): - AttributeError: type object 'UserDefinedType' has no attribute 'Comparator' - -You are trying to run the extension against CKAN 2.3, but the requirements for CKAN haven't been updated -(GeoAlchemy2 is crashing against SQLAlchemy 0.7.x). Upgrade the CKAN requirements as described in the -`upgrade documentation`_. - -.. _GeoAlchemy2: http://geoalchemy-2.readthedocs.org/en/0.2.4/ -.. _upgrade documentation: http://docs.ckan.org/en/latest/maintaining/upgrading/upgrade-source.html - ckan.plugins.core.PluginNotFoundException: geojson_view ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -289,82 +167,6 @@ extension. With the virtualenv CKAN is installed on activated, run:: git pull python setup.py develop - - -When initializing the spatial tables -++++++++++++++++++++++++++++++++++++ - -No function matches the given name and argument types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - LINE 1: SELECT AddGeometryColumn('package_extent','the_geom', E'4326... - ^ - HINT: No function matches the given name and argument types. You might need to add explicit type casts. - "SELECT AddGeometryColumn('package_extent','the_geom', %s, 'GEOMETRY', 2)" ('4326',) - - -PostGIS was not installed correctly. Please check the "Setting up PostGIS" -section. - -permission denied for relation spatial_ref_sys -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - sqlalchemy.exc.ProgrammingError: (ProgrammingError) permission denied for relation spatial_ref_sys - - -The user accessing the ckan database needs to be owner (or have permissions) -of the geometry_columns and spatial_ref_sys tables. - -When migrating to an existing PostGIS database -++++++++++++++++++++++++++++++++++++++++++++++ - -If you are loading a database dump to an existing PostGIS database, you may -find errors like :: - - ERROR: type "spheroid" already exists - -This means that the PostGIS functions are installed, but you may need to -create the necessary tables anyway. You can force psql to ignore these -errors and continue the transaction with the ON_ERROR_ROLLBACK=on:: - - sudo -u postgres psql -d ckan_default -f /usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql -v ON_ERROR_ROLLBACK=on - -You will still need to populate the spatial_ref_sys table and change the -tables permissions. Refer to the previous section for details on how to do -it. - -When performing a spatial query -+++++++++++++++++++++++++++++++ - -SQL expression, column, or mapped entity expected - got ' -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -:: - - InvalidRequestError: SQL expression, column, or mapped entity expected - got '' - -The spatial model has not been loaded. You probably forgot to add the -``spatial_metadata`` plugin to your ini configuration file. - -Operation on two geometries with different SRIDs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - InternalError: (InternalError) Operation on two geometries with different SRIDs - -The spatial reference system of the database geometry column and the one -used by CKAN differ. Remember, if you are using a different spatial -reference system from the default one (WGS 84 lat/lon, EPSG:4326), you must -define it in the configuration file as follows:: - - ckan.spatial.srid = 4258 - When running the spatial harvesters +++++++++++++++++++++++++++++++++++ @@ -410,6 +212,5 @@ Now check the install by running xmllint:: xmllint: using libxml version 20900 compiled with: Threads Tree Output Push Reader Patterns Writer SAXv1 FTP HTTP DTDValid HTML Legacy C14N Catalog XPath XPointer XInclude Iconv ISO8859X Unicode Regexps Automata Expr Schemas Schematron Modules Debug Zlib -.. _PostGIS: http://postgis.org .. _ckanext-harvest: https://github.com/okfn/ckanext-harvest .. _ckanext-geoview: https://github.com/ckan/ckanext-geoview diff --git a/doc/postgis-manual.rst b/doc/postgis-manual.rst deleted file mode 100644 index 5fd08eee..00000000 --- a/doc/postgis-manual.rst +++ /dev/null @@ -1,43 +0,0 @@ -========================== -Setting up a PostGIS table -========================== - -.. note:: The extension will generally set up the table automatically for you, - and also running the ``initdb`` command will have the same effect. This - section just describes what's going on for those who want to know more. - -To be able to store geometries and perform spatial operations, PostGIS_ -needs to work with geometry fields. Geometry fields should always be -added via the ``AddGeometryColumn`` function:: - - CREATE TABLE package_extent( - package_id text PRIMARY KEY - ); - - ALTER TABLE package_extent OWNER TO ckan_default; - - SELECT AddGeometryColumn('package_extent','the_geom', 4326, 'GEOMETRY', 2); - -This will add a geometry column in the ``package_extent`` table called -``the_geom``, with the spatial reference system EPSG:4326. The stored -geometries will be polygons, with 2 dimensions (The CKAN table uses the -GEOMETRY type to support multiple geometry types). - -Have a look a the table definition, and see how PostGIS has created -some constraints to ensure that the geometries follow the parameters -defined in the geometry column creation:: - - # \d package_extent - - Table "public.package_extent" - Column | Type | Modifiers - ------------+----------+----------- - package_id | text | not null - the_geom | geometry | - Indexes: - "package_extent_pkey" PRIMARY KEY, btree (package_id) - Check constraints: - "enforce_dims_the_geom" CHECK (st_ndims(the_geom) = 2) - "enforce_srid_the_geom" CHECK (st_srid(the_geom) = 4326) - -.. _PostGIS: http://postgis.org diff --git a/doc/spatial-search.rst b/doc/spatial-search.rst index 4aa14a2e..50b3d406 100644 --- a/doc/spatial-search.rst +++ b/doc/spatial-search.rst @@ -84,15 +84,10 @@ The following table summarizes the different spatial search backends: +------------------------+---------------+-------------------------------------+-----------------------------------------------------------+-------------------------------------------+ | ``solr-spatial-field`` | >= 4.x | Bounding Box, Point and Polygon [1] | Not implemented | Good | +------------------------+---------------+-------------------------------------+-----------------------------------------------------------+-------------------------------------------+ -| ``postgis`` | >= 1.3 | Bounding Box | Partial, only spatial sorting supported [2] | Poor | -+------------------------+---------------+-------------------------------------+-----------------------------------------------------------+-------------------------------------------+ [1] Requires JTS -[2] Needs ``ckanext.spatial.use_postgis_sorting`` set to True - - We recommend to use the ``solr`` backend whenever possible. Here are more details about the available options: @@ -145,17 +140,9 @@ details about the available options: -* ``postgis`` - This is the original implementation of the spatial search. It - does not require any change in the Solr schema and can run on Solr 1.x, - but it is not as efficient as the previous ones. Basically the bounding - box based query is performed in PostGIS first, and the ids of the matched - datasets are added as a filter to the Solr request. This, apart from being - much less efficient, can led to issues on Solr due to size of the requests - (See `Solr configuration issues on legacy PostGIS backend`_). There is - support for a spatial ranking on this backend (setting - ``ckanext.spatial.use_postgis_sorting`` to True on the ini file), but - it can not be combined with any other filtering. +.. note:: The old ``postgis`` search backend is deprecated and will be removed in future versions of the extension. + You should migrate to one of the other backends instead but if you need to keep using it for a while see :ref:`legacy_postgis`. + Spatial Search Widget @@ -241,65 +228,6 @@ For adding the map to the main body, add this to the main dataset page template You need to load the ``spatial_metadata`` plugin to use these snippets. -Legacy Search -------------- - -Solr configuration issues on legacy PostGIS backend -+++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. warning:: - - If you find any of the issues described in this section it is strongly - recommended that you consider switching to one of the Solr based backends - which are much more efficient. These notes are just kept for informative - purposes. - - -If using Spatial Query functionality then there is an additional SOLR/Lucene -setting that should be used to set the limit on number of datasets searchable -with a spatial value. - -The setting is ``maxBooleanClauses`` in the solrconfig.xml and the value is the -number of datasets spatially searchable. The default is ``1024`` and this could -be increased to say ``16384``. For a SOLR single core this will probably be at -`/etc/solr/conf/solrconfig.xml`. For a multiple core set-up, there will me -several solrconfig.xml files a couple of levels below `/etc/solr`. For that -case, *all* of the cores' `solrconfig.xml` should have this setting at the new -value. - -Example:: - - 16384 - -This setting is needed because PostGIS spatial query results are fed into SOLR -using a Boolean expression, and the parser for that has a limit. So if your -spatial area contains more than the limit (of which the default is 1024) then -you will get this error:: - - Dataset search error: ('SOLR returned an error running query... - -and in the SOLR logs you see:: - - too many boolean clauses ... Caused by: - org.apache.lucene.search.BooleanQuery$TooManyClauses: maxClauseCount is set to - 1024 - - -Legacy API -++++++++++ - -The extension adds the following call to the CKAN search API, which returns -datasets with an extent that intersects with the bounding box provided:: - - /api/2/search/dataset/geo?bbox={minx,miny,maxx,maxy}[&crs={srid}] - -If the bounding box coordinates are not in the same projection as the one -defined in the database, a CRS must be provided, in one of the following forms: - -- `urn:ogc:def:crs:EPSG::4326` -- EPSG:4326 -- 4326 - .. _action API: http://docs.ckan.org/en/latest/apiv3.html .. _edismax: http://wiki.apache.org/solr/ExtendedDisMax .. _JTS: http://www.vividsolutions.com/jts/JTSHome.htm From eebd3954a141799e146f794ae4122c22cc4bed5b Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 13:19:14 +0200 Subject: [PATCH 19/66] Separate functions for normalizing and fitting a bbox `normalize_bbox()` just parses incoming coordinates into a normalized dict `fit_bbox()` will translate any outlying coordinates to ensure they fit the -180, -90, 180, 90 extent --- ckanext/spatial/lib/__init__.py | 66 ++++++++++++++++++++--- ckanext/spatial/tests/lib/test_spatial.py | 54 +++++++++++++------ 2 files changed, 97 insertions(+), 23 deletions(-) diff --git a/ckanext/spatial/lib/__init__.py b/ckanext/spatial/lib/__init__.py index 6462a85b..de238fe3 100644 --- a/ckanext/spatial/lib/__init__.py +++ b/ckanext/spatial/lib/__init__.py @@ -24,21 +24,27 @@ def get_srid(crs): return int(srid) -def validate_bbox(bbox_values): +def normalize_bbox(bbox_values): """ - Ensures a bbox is expressed in a standard dict. + Ensures a bbox is expressed in a standard dict bbox_values may be: a string: "-4.96,55.70,-3.78,56.43" or a list [-4.96, 55.70, -3.78, 56.43] or a list of strings ["-4.96", "55.70", "-3.78", "56.43"] - and returns a dict: - {'minx': -4.96, - 'miny': 55.70, - 'maxx': -3.78, - 'maxy': 56.43} - Any problems and it returns None. + ordered as MinX, MinY, MaxX, MaxY. + + Returns a dict with the keys: + + { + "minx": -4.96, + "miny": 55.70, + "maxx": -3.78, + "maxy": 56.43 + } + + If there are any problems parsing the input it returns None. """ if isinstance(bbox_values, six.string_types): @@ -56,4 +62,48 @@ def validate_bbox(bbox_values): except ValueError: return None + bbox = fit_bbox(bbox) + return bbox + + +def fit_bbox(bbox_dict): + """ + Ensures that all coordinates in a bounding box + fall within -180, -90, 180, 90 degrees + + Accepts a dict with the following keys: + + { + "minx": -4.96, + "miny": 55.70, + "maxx": -3.78, + "maxy": 56.43 + } + + """ + + def _adjust_longitude(value): + if value < -180 or value > 180: + value = value % 360 + if value < -180: + value = 360 + value + elif value > 180: + value = -360 + value + return value + + def _adjust_latitude(value): + if value < -90 or value > 90: + value = value % 180 + if value < -90: + value = 180 + value + elif value > 90: + value = -180 + value + return value + + return { + "minx": _adjust_longitude(bbox_dict["minx"]), + "maxx": _adjust_longitude(bbox_dict["maxx"]), + "miny": _adjust_latitude(bbox_dict["miny"]), + "maxy": _adjust_latitude(bbox_dict["maxy"]), + } diff --git a/ckanext/spatial/tests/lib/test_spatial.py b/ckanext/spatial/tests/lib/test_spatial.py index 423a7996..8c259798 100644 --- a/ckanext/spatial/tests/lib/test_spatial.py +++ b/ckanext/spatial/tests/lib/test_spatial.py @@ -1,21 +1,45 @@ -from ckanext.spatial.lib import validate_bbox +from ckanext.spatial.lib import normalize_bbox, fit_bbox -class TestValidateBbox(object): - bbox_dict = {"minx": -4.96, "miny": 55.70, "maxx": -3.78, "maxy": 56.43} +bbox_dict = {"minx": -4.96, "miny": 55.70, "maxx": -3.78, "maxy": 56.43} - def test_string(self): - res = validate_bbox("-4.96,55.70,-3.78,56.43") - assert res == self.bbox_dict - def test_list(self): - res = validate_bbox([-4.96, 55.70, -3.78, 56.43]) - assert res == self.bbox_dict +def test_string(): + res = normalize_bbox("-4.96,55.70,-3.78,56.43") + assert res == bbox_dict - def test_bad(self): - res = validate_bbox([-4.96, 55.70, -3.78]) - assert res is None - def test_bad_2(self): - res = validate_bbox("random") - assert res is None +def test_list(): + res = normalize_bbox([-4.96, 55.70, -3.78, 56.43]) + assert res == bbox_dict + + +def test_list_of_strings(): + res = normalize_bbox(["-4.96", "55.70", "-3.78", "56.43"]) + assert res == bbox_dict + + +def test_bad(): + res = normalize_bbox([-4.96, 55.70, -3.78]) + assert res is None + + +def test_bad_2(): + res = normalize_bbox("random") + assert res is None + + +def test_fit_within_bounds(): + + assert fit_bbox(bbox_dict) == bbox_dict + + +def test_fit_out_of_bounds(): + + bbox_dict = {"minx": -185, "miny": -95, "maxx": 195, "maxy": 95} + assert fit_bbox(bbox_dict) == { + "minx": 175, + "miny": 85, + "maxx": -165, + "maxy": -85, + } From 916d5c92bae55bf135652bae035443a0fb51aded Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 13:21:18 +0200 Subject: [PATCH 20/66] Update Bbox based search (`solr` backend) Use the `bbox` field in Solr, and the relevant query syntax --- ckanext/spatial/plugin/__init__.py | 71 ++++++++++++------------------ ckanext/spatial/tests/test_api.py | 2 +- 2 files changed, 29 insertions(+), 44 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 788927c6..df0bf58f 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -18,7 +18,7 @@ from ckanext.spatial.plugin.pylons_plugin import ( SpatialQueryMixin, HarvestMetadataApiMixin ) - +from ckanext.spatial.lib import normalize_bbox, fit_bbox config = tk.config @@ -161,11 +161,10 @@ class SpatialQuery(SpatialQueryMixin, p.SingletonPlugin): def configure(self, config): - self.search_backend = config.get('ckanext.spatial.search_backend', 'postgis') - if self.search_backend != 'postgis' and not tk.check_ckan_version('2.0.1'): - msg = 'The Solr backends for the spatial search require CKAN 2.0.1 or higher. ' + \ - 'Please upgrade CKAN or select the \'postgis\' backend.' - raise tk.CkanVersionException(msg) + self.search_backend = config.get("ckanext.spatial.search_backend", "solr") + + if self.search_backend == "postgis": + log.warning("The `postgis` spatial search backend is deprecated and will be removed in future versions") # IPackageController @@ -179,31 +178,26 @@ def after_search(self, search_results, search_params): return self.after_dataset_search(search_results, search_params) def before_dataset_index(self, pkg_dict): - import shapely - import shapely.geometry + import shapely.geometry if pkg_dict.get('extras_spatial', None) and self.search_backend in ('solr', 'solr-spatial-field'): try: geometry = json.loads(pkg_dict['extras_spatial']) - except ValueError as e: + + shape = shapely.geometry.shape(geometry) + except (AttributeError, ValueError): log.error('Geometry not valid GeoJSON, not indexing') return pkg_dict if self.search_backend == 'solr': - # Only bbox supported for this backend - if not (geometry['type'] == 'Polygon' - and len(geometry['coordinates']) == 1 - and len(geometry['coordinates'][0]) == 5): - log.error('Solr backend only supports bboxes (Polygons with 5 points), ignoring geometry {0}'.format(pkg_dict['extras_spatial'])) - return pkg_dict - - coords = geometry['coordinates'] - pkg_dict['maxy'] = max(coords[0][2][1], coords[0][0][1]) - pkg_dict['miny'] = min(coords[0][2][1], coords[0][0][1]) - pkg_dict['maxx'] = max(coords[0][2][0], coords[0][0][0]) - pkg_dict['minx'] = min(coords[0][2][0], coords[0][0][0]) - pkg_dict['bbox_area'] = (pkg_dict['maxx'] - pkg_dict['minx']) * \ - (pkg_dict['maxy'] - pkg_dict['miny']) + # We always index the envelope of the geometry regardless of + # if it's an actual bounding box (polygon) + + bounds = shape.bounds + bbox = fit_bbox(normalize_bbox(list(bounds))) + + pkg_dict["spatial_bbox"] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})".format( + **bbox) elif self.search_backend == 'solr-spatial-field': wkt = None @@ -236,42 +230,33 @@ def before_dataset_index(self, pkg_dict): pkg_dict['spatial_geom'] = wkt - # Coupled resources are URL -> uuid links, they are not needed in SOLR - # and might be huge if there are lot of coupled resources - if pkg_dict.get('coupled-resource'): - pkg_dict.pop('coupled-resource') - - # spatial field is geojson coordinate data, not need in SOLR either - if pkg_dict.get('spatial'): - pkg_dict.pop('spatial') - return pkg_dict def before_dataset_search(self, search_params): - from ckanext.spatial.lib import validate_bbox from ckan.lib.search import SearchError if search_params.get('extras', None) and search_params['extras'].get('ext_bbox', None): - bbox = validate_bbox(search_params['extras']['ext_bbox']) + bbox = normalize_bbox(search_params['extras']['ext_bbox']) if not bbox: raise SearchError('Wrong bounding box provided') - # Adjust easting values - while (bbox['minx'] < -180): - bbox['minx'] += 360 - bbox['maxx'] += 360 - while (bbox['minx'] > 180): - bbox['minx'] -= 360 - bbox['maxx'] -= 360 + bbox = fit_bbox(bbox) if self.search_backend == 'solr': - search_params = self._params_for_solr_search(bbox, search_params) + if not search_params.get("fq_list"): + search_params["fq_list"] = [] + + search_params["fq_list"].append( + "{{!field f=spatial_bbox}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))".format( + **bbox) + ) + + #search_params = self._params_for_solr_search(bbox, search_params) elif self.search_backend == 'solr-spatial-field': search_params = self._params_for_solr_spatial_field_search(bbox, search_params) elif self.search_backend == 'postgis': search_params = self._params_for_postgis_search(bbox, search_params) - return search_params def _params_for_solr_search(self, bbox, search_params): diff --git a/ckanext/spatial/tests/test_api.py b/ckanext/spatial/tests/test_api.py index 2985059c..5d9287c4 100644 --- a/ckanext/spatial/tests/test_api.py +++ b/ckanext/spatial/tests/test_api.py @@ -16,8 +16,8 @@ } # TODO: migrate to Solr -@pytest.mark.skip(reason="These tests need to be migrated to Solr") @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") +@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr") class TestAction(SpatialTestBase): def test_spatial_query(self): From a68e6239f05e8cbe8058fe61e77864fd488f7e13 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 15:58:51 +0200 Subject: [PATCH 21/66] No postgis backend no more --- test.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/test.ini b/test.ini index d52610be..b1786434 100644 --- a/test.ini +++ b/test.ini @@ -19,7 +19,6 @@ ckan.spatial.srid = 4326 ckan.spatial.default_map_extent=-6.88,49.74,0.50,59.2 ckan.spatial.testing = true ckan.spatial.validator.profiles = iso19139,constraints,gemini2 -ckanext.spatial.search_backend = postgis ckan.harvest.mq.type = redis # Logging configuration From d7e55b729023a36ae7cb871f978c3f63b10e9270 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 16:46:46 +0200 Subject: [PATCH 22/66] cleanup --- ckanext/spatial/plugin/__init__.py | 67 +++++------------------------- ckanext/spatial/tests/test_api.py | 5 +-- 2 files changed, 12 insertions(+), 60 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index df0bf58f..1a76e482 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -4,11 +4,16 @@ import six import geojson + +import shapely.geometry + import ckantoolkit as tk from ckan import plugins as p +from ckan.lib.search import SearchError from ckan.lib.helpers import json +from ckanext.spatial.lib import normalize_bbox, fit_bbox if tk.check_ckan_version(min_version="2.9.0"): from ckanext.spatial.plugin.flask_plugin import ( @@ -18,7 +23,6 @@ from ckanext.spatial.plugin.pylons_plugin import ( SpatialQueryMixin, HarvestMetadataApiMixin ) -from ckanext.spatial.lib import normalize_bbox, fit_bbox config = tk.config @@ -147,10 +151,11 @@ def check_spatial_extra(self, dataset_dict, update=False): def get_helpers(self): from ckanext.spatial import helpers as spatial_helpers return { - 'get_reference_date' : spatial_helpers.get_reference_date, - 'get_responsible_party': spatial_helpers.get_responsible_party, - 'get_common_map_config' : spatial_helpers.get_common_map_config, - } + "get_reference_date": spatial_helpers.get_reference_date, + "get_responsible_party": spatial_helpers.get_responsible_party, + "get_common_map_config": spatial_helpers.get_common_map_config, + } + class SpatialQuery(SpatialQueryMixin, p.SingletonPlugin): @@ -179,7 +184,6 @@ def after_search(self, search_results, search_params): def before_dataset_index(self, pkg_dict): - import shapely.geometry if pkg_dict.get('extras_spatial', None) and self.search_backend in ('solr', 'solr-spatial-field'): try: geometry = json.loads(pkg_dict['extras_spatial']) @@ -233,7 +237,6 @@ def before_dataset_index(self, pkg_dict): return pkg_dict def before_dataset_search(self, search_params): - from ckan.lib.search import SearchError if search_params.get('extras', None) and search_params['extras'].get('ext_bbox', None): @@ -252,62 +255,12 @@ def before_dataset_search(self, search_params): **bbox) ) - #search_params = self._params_for_solr_search(bbox, search_params) elif self.search_backend == 'solr-spatial-field': search_params = self._params_for_solr_spatial_field_search(bbox, search_params) elif self.search_backend == 'postgis': search_params = self._params_for_postgis_search(bbox, search_params) return search_params - def _params_for_solr_search(self, bbox, search_params): - ''' - This will add the following parameters to the query: - - defType - edismax (We need to define EDisMax to use bf) - bf - {function} A boost function to influence the score (thus - influencing the sorting). The algorithm can be basically defined as: - - 2 * X / Q + T - - Where X is the intersection between the query area Q and the - target geometry T. It gives a ratio from 0 to 1 where 0 means - no overlap at all and 1 a perfect fit - - fq - Adds a filter that force the value returned by the previous - function to be between 0 and 1, effectively applying the - spatial filter. - - ''' - - variables =dict( - x11=bbox['minx'], - x12=bbox['maxx'], - y11=bbox['miny'], - y12=bbox['maxy'], - x21='minx', - x22='maxx', - y21='miny', - y22='maxy', - area_search = abs(bbox['maxx'] - bbox['minx']) * abs(bbox['maxy'] - bbox['miny']) - ) - - bf = '''div( - mul( - mul(max(0, sub(min({x12},{x22}) , max({x11},{x21}))), - max(0, sub(min({y12},{y22}) , max({y11},{y21}))) - ), - 2), - add({area_search}, mul(sub({y22}, {y21}), sub({x22}, {x21}))) - )'''.format(**variables).replace('\n','').replace(' ','') - - search_params['fq_list'] = search_params.get('fq_list', []) - search_params['fq_list'].append('{!frange incl=false l=0 u=1}%s' % bf) - - search_params['bf'] = bf - search_params['defType'] = 'edismax' - - return search_params - def _params_for_solr_spatial_field_search(self, bbox, search_params): ''' This will add an fq filter with the form: diff --git a/ckanext/spatial/tests/test_api.py b/ckanext/spatial/tests/test_api.py index 5d9287c4..c438b051 100644 --- a/ckanext/spatial/tests/test_api.py +++ b/ckanext/spatial/tests/test_api.py @@ -15,11 +15,10 @@ "dateline2": '{"type":"Polygon","coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}', } -# TODO: migrate to Solr + @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") @pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr") -class TestAction(SpatialTestBase): - +class TestBBoxSearch(SpatialTestBase): def test_spatial_query(self): dataset = factories.Dataset( extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] From fc091043c6788b3c8824ff445f4ef023b8bb840c Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 16:47:09 +0200 Subject: [PATCH 23/66] Use the spatial Solr image in tests, test 2.9 onwards only --- .github/workflows/test.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd17d66b..7a0ca02f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,13 @@ jobs: needs: lint strategy: matrix: - ckan-version: [master, 2.9, 2.9-py2, 2.8, 2.7] + include: + - ckan-version: "2.10" + solr-image: "2.10-spatial" + - ckan-version: 2.9 + solr-image: 2.9-solr8-spatial + - ckan-version: 2.9-py2 + solr-image: 2.9-py2-solr8-spatial fail-fast: false name: CKAN ${{ matrix.ckan-version }} @@ -26,7 +32,7 @@ jobs: image: openknowledge/ckan-dev:${{ matrix.ckan-version }} services: solr: - image: ckan/ckan-solr-dev:${{ matrix.ckan-version }} + image: ckan/ckan-solr:${{ matrix.solr-image }} postgres: image: ckan/ckan-postgres-dev:${{ matrix.ckan-version }} env: From de7c85b59d20cc6ba22aff4d2e2afc0ef55ecd70 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 17:08:58 +0200 Subject: [PATCH 24/66] Re-add lost logic to remove fields from the index --- ckanext/spatial/plugin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 1a76e482..eae1b02c 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -244,7 +244,7 @@ def before_dataset_search(self, search_params): if not bbox: raise SearchError('Wrong bounding box provided') - bbox = fit_bbox(bbox) + #bbox = fit_bbox(bbox) if self.search_backend == 'solr': if not search_params.get("fq_list"): From d0a722551dba6511e1fce42f3588ee1bca661b87 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 31 Aug 2022 17:10:49 +0200 Subject: [PATCH 25/66] Don't fit bbox in normalize function --- ckanext/spatial/lib/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ckanext/spatial/lib/__init__.py b/ckanext/spatial/lib/__init__.py index de238fe3..fd0cd41d 100644 --- a/ckanext/spatial/lib/__init__.py +++ b/ckanext/spatial/lib/__init__.py @@ -62,8 +62,6 @@ def normalize_bbox(bbox_values): except ValueError: return None - bbox = fit_bbox(bbox) - return bbox From 95c7b671329791309ee87040e86d3ccd25dac04c Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 1 Sep 2022 11:44:54 +0200 Subject: [PATCH 26/66] PostGIS' and Solr's logic for wrapping bboxes is different --- ckanext/spatial/plugin/__init__.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index eae1b02c..b621dd68 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -244,9 +244,10 @@ def before_dataset_search(self, search_params): if not bbox: raise SearchError('Wrong bounding box provided') - #bbox = fit_bbox(bbox) - if self.search_backend == 'solr': + + bbox = fit_bbox(bbox) + if not search_params.get("fq_list"): search_params["fq_list"] = [] @@ -256,6 +257,9 @@ def before_dataset_search(self, search_params): ) elif self.search_backend == 'solr-spatial-field': + + bbox = fit_bbox(bbox) + search_params = self._params_for_solr_spatial_field_search(bbox, search_params) elif self.search_backend == 'postgis': search_params = self._params_for_postgis_search(bbox, search_params) @@ -278,6 +282,14 @@ def _params_for_postgis_search(self, bbox, search_params): from ckanext.spatial.postgis.model import bbox_query, bbox_query_ordered from ckan.lib.search import SearchError + # Adjust easting values + while (bbox['minx'] < -180): + bbox['minx'] += 360 + bbox['maxx'] += 360 + while (bbox['minx'] > 180): + bbox['minx'] -= 360 + bbox['maxx'] -= 360 + # Note: This will be deprecated at some point in favour of the # Solr 4 spatial sorting capabilities if search_params.get('sort') == 'spatial desc' and \ From 9cb4fb5fb3e98174c0631379cd703c434bf1ca42 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 1 Sep 2022 12:00:34 +0200 Subject: [PATCH 27/66] Move query tests to own file --- ckanext/spatial/tests/base.py | 2 - ckanext/spatial/tests/test_api.py | 143 ------------------ ckanext/spatial/tests/test_spatial_search.py | 150 +++++++++++++++++++ 3 files changed, 150 insertions(+), 145 deletions(-) create mode 100644 ckanext/spatial/tests/test_spatial_search.py diff --git a/ckanext/spatial/tests/base.py b/ckanext/spatial/tests/base.py index aee2fc70..dc7ae0c6 100644 --- a/ckanext/spatial/tests/base.py +++ b/ckanext/spatial/tests/base.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -import pytest - geojson_examples = { "point": '{"type":"Point","coordinates":[100.0,0.0]}', "point_2": '{"type":"Point","coordinates":[20,10]}', diff --git a/ckanext/spatial/tests/test_api.py b/ckanext/spatial/tests/test_api.py index c438b051..20d16e89 100644 --- a/ckanext/spatial/tests/test_api.py +++ b/ckanext/spatial/tests/test_api.py @@ -1,152 +1,9 @@ import pytest from ckan.model import Session -from ckan.lib.search import SearchError - -import ckan.tests.helpers as helpers -import ckan.tests.factories as factories from ckanext.spatial.tests.base import SpatialTestBase -extents = { - "nz": '{"type":"Polygon","coordinates":[[[174,-38],[176,-38],[176,-40],[174,-40],[174,-38]]]}', - "ohio": '{"type": "Polygon","coordinates": [[[-84,38],[-84,40],[-80,42],[-80,38],[-84,38]]]}', - "dateline": '{"type":"Polygon","coordinates":[[[169,70],[169,60],[192,60],[192,70],[169,70]]]}', - "dateline2": '{"type":"Polygon","coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}', -} - - -@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") -@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr") -class TestBBoxSearch(SpatialTestBase): - def test_spatial_query(self): - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "-180,-90,180,90"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_outside_bbox(self): - - factories.Dataset( - extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "-10,-20,10,20"} - ) - - assert result["count"] == 0 - - def test_spatial_query_wrong_bbox(self): - with pytest.raises(SearchError): - helpers.call_action( - "package_search", - extras={"ext_bbox": "-10,-20,10,a"}, - ) - - def test_spatial_query_nz(self): - dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "56,-54,189,-28"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_nz_wrap(self): - dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) - result = helpers.call_action( - "package_search", extras={"ext_bbox": "-203,-54,-167,-28"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_ohio(self): - - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["ohio"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "-110,37,-78,53"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_ohio_wrap(self): - - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["ohio"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "258,37,281,51"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_dateline_1(self): - - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "-197,56,-128,70"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_dateline_2(self): - - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "162,54,237,70"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_dateline_3(self): - - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline2"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "-197,56,-128,70"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - def test_spatial_query_dateline_4(self): - - dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline2"]}] - ) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "162,54,237,70"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - @pytest.mark.usefixtures( "with_plugins", diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py new file mode 100644 index 00000000..2e26d108 --- /dev/null +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -0,0 +1,150 @@ +import pytest + +from ckan.lib.search import SearchError + +import ckan.tests.helpers as helpers +import ckan.tests.factories as factories + +from ckanext.spatial.tests.base import SpatialTestBase + +extents = { + "nz": '{"type":"Polygon","coordinates":[[[174,-38],[176,-38],[176,-40],[174,-40],[174,-38]]]}', + "ohio": '{"type": "Polygon","coordinates": [[[-84,38],[-84,40],[-80,42],[-80,38],[-84,38]]]}', + "dateline": '{"type":"Polygon","coordinates":[[[169,70],[169,60],[192,60],[192,70],[169,70]]]}', + "dateline2": '{"type":"Polygon","coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}', +} + + +@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") +@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr") +class TestBBoxSearch(SpatialTestBase): + def test_spatial_query(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_outside_bbox(self): + + factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-10,-20,10,20"} + ) + + assert result["count"] == 0 + + def test_spatial_query_wrong_bbox(self): + with pytest.raises(SearchError): + helpers.call_action( + "package_search", + extras={"ext_bbox": "-10,-20,10,a"}, + ) + + def test_spatial_query_nz(self): + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "56,-54,189,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_nz_wrap(self): + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-203,-54,-167,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_ohio(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["ohio"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-110,37,-78,53"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_ohio_wrap(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["ohio"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "258,37,281,51"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_1(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-197,56,-128,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_2(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "162,54,237,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_3(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline2"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-197,56,-128,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_4(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline2"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "162,54,237,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + + From 65de1d0e77028f950bf5e7e8f80a18ee3bca90a3 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 1 Sep 2022 14:33:49 +0200 Subject: [PATCH 28/66] Refactor search backend handling --- ckanext/spatial/plugin/__init__.py | 135 ++++++++++++++++++----------- 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index b621dd68..149df188 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -28,6 +28,14 @@ log = getLogger(__name__) +DEFAULT_SEARCH_BACKEND = "solr" +ALLOWED_SEARCH_BACKENDS = [ + "solr", + "solr-spatial-field", + "postgis", # Deprecated: will be removed in the next version +] + + class SpatialMetadata(p.SingletonPlugin): @@ -160,16 +168,31 @@ def get_helpers(self): class SpatialQuery(SpatialQueryMixin, p.SingletonPlugin): p.implements(p.IPackageController, inherit=True) - p.implements(p.IConfigurable, inherit=True) + p.implements(p.IConfigurer, inherit=True) - search_backend = None + def _get_search_backend(self): + search_backend = config.get( + "ckanext.spatial.search_backend", DEFAULT_SEARCH_BACKEND) - def configure(self, config): + if search_backend not in ALLOWED_SEARCH_BACKENDS: + raise ValueError( + "Unknown spatial search backend: {}. Allowed values are: {}".format( + search_backend, ALLOWED_SEARCH_BACKENDS) + ) - self.search_backend = config.get("ckanext.spatial.search_backend", "solr") + if search_backend == "postgis": + log.warning( + "The `postgis` spatial search backend is deprecated" + "and will be removed in future versions" + ) - if self.search_backend == "postgis": - log.warning("The `postgis` spatial search backend is deprecated and will be removed in future versions") + return search_backend + + # IConfigure + + def update_config(self, config): + + self._get_search_backend() # IPackageController @@ -184,67 +207,79 @@ def after_search(self, search_results, search_params): def before_dataset_index(self, pkg_dict): - if pkg_dict.get('extras_spatial', None) and self.search_backend in ('solr', 'solr-spatial-field'): - try: - geometry = json.loads(pkg_dict['extras_spatial']) + search_backend = self._get_search_backend() - shape = shapely.geometry.shape(geometry) - except (AttributeError, ValueError): - log.error('Geometry not valid GeoJSON, not indexing') - return pkg_dict + if search_backend not in ('solr', 'solr-spatial-field'): + return pkg_dict - if self.search_backend == 'solr': - # We always index the envelope of the geometry regardless of - # if it's an actual bounding box (polygon) + if not pkg_dict.get('extras_spatial'): + return pkg_dict - bounds = shape.bounds - bbox = fit_bbox(normalize_bbox(list(bounds))) + try: + geometry = json.loads(pkg_dict['extras_spatial']) - pkg_dict["spatial_bbox"] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})".format( - **bbox) + shape = shapely.geometry.shape(geometry) + except (AttributeError, ValueError): + log.error('Geometry not valid GeoJSON, not indexing') + return pkg_dict - elif self.search_backend == 'solr-spatial-field': - wkt = None + if search_backend == 'solr': + # We always index the envelope of the geometry regardless of + # if it's an actual bounding box (polygon) - # Check potential problems with bboxes - if geometry['type'] == 'Polygon' \ - and len(geometry['coordinates']) == 1 \ - and len(geometry['coordinates'][0]) == 5: + bounds = shape.bounds + bbox = fit_bbox(normalize_bbox(list(bounds))) - # Check wrong bboxes (4 same points) - xs = [p[0] for p in geometry['coordinates'][0]] - ys = [p[1] for p in geometry['coordinates'][0]] + pkg_dict["spatial_bbox"] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})".format( + **bbox) - if xs.count(xs[0]) == 5 and ys.count(ys[0]) == 5: - wkt = 'POINT({x} {y})'.format(x=xs[0], y=ys[0]) - else: - # Check if coordinates are defined counter-clockwise, - # otherwise we'll get wrong results from Solr - lr = shapely.geometry.polygon.LinearRing(geometry['coordinates'][0]) - lr_coords = list(lr.coords) if lr.is_ccw else reversed(list(lr.coords)) - polygon = shapely.geometry.polygon.Polygon(lr_coords) - wkt = polygon.wkt - - if not wkt: - shape = shapely.geometry.shape(geometry) - if not shape.is_valid: - log.error('Wrong geometry, not indexing') - return pkg_dict - wkt = shape.wkt - - pkg_dict['spatial_geom'] = wkt + elif search_backend == 'solr-spatial-field': + wkt = None + + # Check potential problems with bboxes + if geometry['type'] == 'Polygon' \ + and len(geometry['coordinates']) == 1 \ + and len(geometry['coordinates'][0]) == 5: + + # Check wrong bboxes (4 same points) + xs = [p[0] for p in geometry['coordinates'][0]] + ys = [p[1] for p in geometry['coordinates'][0]] + + if xs.count(xs[0]) == 5 and ys.count(ys[0]) == 5: + wkt = 'POINT({x} {y})'.format(x=xs[0], y=ys[0]) + else: + # Check if coordinates are defined counter-clockwise, + # otherwise we'll get wrong results from Solr + + import ipdb; ipdb.set_trace() + lr = shapely.geometry.polygon.LinearRing(geometry['coordinates'][0]) + lr_coords = list(lr.coords) if lr.is_ccw else reversed(list(lr.coords)) + polygon = shapely.geometry.polygon.Polygon(lr_coords) + wkt = polygon.wkt + + if not wkt: + shape = shapely.geometry.shape(geometry) + import ipdb; ipdb.set_trace() + if not shape.is_valid: + log.error('Wrong geometry, not indexing') + return pkg_dict + wkt = shape.wkt + + pkg_dict['spatial_geom'] = wkt return pkg_dict def before_dataset_search(self, search_params): + search_backend = self._get_search_backend() + if search_params.get('extras', None) and search_params['extras'].get('ext_bbox', None): bbox = normalize_bbox(search_params['extras']['ext_bbox']) if not bbox: raise SearchError('Wrong bounding box provided') - if self.search_backend == 'solr': + if search_backend == 'solr': bbox = fit_bbox(bbox) @@ -256,12 +291,12 @@ def before_dataset_search(self, search_params): **bbox) ) - elif self.search_backend == 'solr-spatial-field': + elif search_backend == 'solr-spatial-field': bbox = fit_bbox(bbox) search_params = self._params_for_solr_spatial_field_search(bbox, search_params) - elif self.search_backend == 'postgis': + elif search_backend == 'postgis': search_params = self._params_for_postgis_search(bbox, search_params) return search_params From 4a4c36f4e2c25153e80cda5297bff3e96a0e759b Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 1 Sep 2022 14:34:29 +0200 Subject: [PATCH 29/66] Spatial field tests Still 2 failing --- ckanext/spatial/plugin/__init__.py | 3 - ckanext/spatial/tests/base.py | 9 + ckanext/spatial/tests/data/altafulla.geojson | 1 + ckanext/spatial/tests/data/hawaii.geojson | 1 + ckanext/spatial/tests/test_spatial_search.py | 275 +++++++++++++++++++ 5 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 ckanext/spatial/tests/data/altafulla.geojson create mode 100644 ckanext/spatial/tests/data/hawaii.geojson diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 149df188..0cd8378f 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -250,8 +250,6 @@ def before_dataset_index(self, pkg_dict): else: # Check if coordinates are defined counter-clockwise, # otherwise we'll get wrong results from Solr - - import ipdb; ipdb.set_trace() lr = shapely.geometry.polygon.LinearRing(geometry['coordinates'][0]) lr_coords = list(lr.coords) if lr.is_ccw else reversed(list(lr.coords)) polygon = shapely.geometry.polygon.Polygon(lr_coords) @@ -259,7 +257,6 @@ def before_dataset_index(self, pkg_dict): if not wkt: shape = shapely.geometry.shape(geometry) - import ipdb; ipdb.set_trace() if not shape.is_valid: log.error('Wrong geometry, not indexing') return pkg_dict diff --git a/ckanext/spatial/tests/base.py b/ckanext/spatial/tests/base.py index dc7ae0c6..064a0295 100644 --- a/ckanext/spatial/tests/base.py +++ b/ckanext/spatial/tests/base.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +import os + geojson_examples = { "point": '{"type":"Point","coordinates":[100.0,0.0]}', "point_2": '{"type":"Point","coordinates":[20,10]}', @@ -23,3 +25,10 @@ class SpatialTestBase(object): db_srid = 4326 geojson_examples = geojson_examples + + def read_file(self, path): + + file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), path) + + with open(file_path, "r") as f: + return f.read() diff --git a/ckanext/spatial/tests/data/altafulla.geojson b/ckanext/spatial/tests/data/altafulla.geojson new file mode 100644 index 00000000..71a1f679 --- /dev/null +++ b/ckanext/spatial/tests/data/altafulla.geojson @@ -0,0 +1 @@ +{ "type": "MultiPolygon", "coordinates": [ [ [ [ 1.36285, 41.14643 ], [ 1.36512, 41.14967 ], [ 1.36656, 41.15016 ], [ 1.36787, 41.15036 ], [ 1.37021, 41.15141 ], [ 1.37172, 41.15457 ], [ 1.37326, 41.15776 ], [ 1.37285, 41.15823 ], [ 1.37324, 41.15899 ], [ 1.3751, 41.16005 ], [ 1.37595, 41.16193 ], [ 1.37684, 41.16201 ], [ 1.37842, 41.16258 ], [ 1.37816, 41.16386 ], [ 1.37814, 41.164 ], [ 1.37823, 41.16445 ], [ 1.37645, 41.16523 ], [ 1.3767, 41.16618 ], [ 1.37686, 41.16672 ], [ 1.37712, 41.16828 ], [ 1.37744, 41.16861 ], [ 1.37811, 41.1699 ], [ 1.37823, 41.17035 ], [ 1.37841, 41.17054 ], [ 1.37893, 41.17082 ], [ 1.37941, 41.1711 ], [ 1.38006, 41.17133 ], [ 1.3802, 41.17188 ], [ 1.38053, 41.17208 ], [ 1.38092, 41.17218 ], [ 1.38172, 41.17229 ], [ 1.38208, 41.17247 ], [ 1.38314, 41.17285 ], [ 1.38385, 41.17322 ], [ 1.38446, 41.1734 ], [ 1.38575, 41.1735 ], [ 1.38633, 41.17338 ], [ 1.38724, 41.17295 ], [ 1.38752, 41.17296 ], [ 1.38876, 41.17244 ], [ 1.38883, 41.17216 ], [ 1.38923, 41.17189 ], [ 1.39044, 41.17145 ], [ 1.39073, 41.17114 ], [ 1.39139, 41.17088 ], [ 1.39183, 41.17058 ], [ 1.3922, 41.1702 ], [ 1.39272, 41.16998 ], [ 1.39432, 41.1698 ], [ 1.39468, 41.16971 ], [ 1.39504, 41.169 ], [ 1.39512, 41.16848 ], [ 1.39566, 41.16728 ], [ 1.39539, 41.16695 ], [ 1.39521, 41.16672 ], [ 1.39407, 41.16546 ], [ 1.39304, 41.16516 ], [ 1.39276, 41.16417 ], [ 1.39252, 41.16169 ], [ 1.39223, 41.16069 ], [ 1.39215, 41.16042 ], [ 1.39194, 41.15995 ], [ 1.39145, 41.15934 ], [ 1.39095, 41.1587 ], [ 1.3904, 41.1577 ], [ 1.39051, 41.15655 ], [ 1.3904, 41.15493 ], [ 1.39039, 41.15489 ], [ 1.38996, 41.15255 ], [ 1.38957, 41.1513 ], [ 1.38973, 41.15002 ], [ 1.38965, 41.14966 ], [ 1.3893, 41.14924 ], [ 1.38879, 41.14883 ], [ 1.38835, 41.14825 ], [ 1.38819, 41.14789 ], [ 1.38814, 41.14751 ], [ 1.38819, 41.14683 ], [ 1.38766, 41.14564 ], [ 1.38756, 41.14495 ], [ 1.38725, 41.14448 ], [ 1.38701, 41.14376 ], [ 1.38525, 41.14099 ], [ 1.38708, 41.14086 ], [ 1.38731, 41.14087 ], [ 1.38791, 41.14092 ], [ 1.38846, 41.14088 ], [ 1.38842, 41.1407 ], [ 1.38838, 41.14045 ], [ 1.39062, 41.13781 ], [ 1.39156, 41.13672 ], [ 1.3899, 41.13265 ], [ 1.38982, 41.13244 ], [ 1.38954, 41.13251 ], [ 1.38924, 41.13248 ], [ 1.38896, 41.13238 ], [ 1.38886, 41.13239 ], [ 1.3886, 41.13243 ], [ 1.38852, 41.13244 ], [ 1.38846, 41.13242 ], [ 1.38833, 41.13235 ], [ 1.38827, 41.13234 ], [ 1.38802, 41.13239 ], [ 1.38774, 41.13249 ], [ 1.38765, 41.1325 ], [ 1.38738, 41.13254 ], [ 1.38705, 41.13257 ], [ 1.38674, 41.13256 ], [ 1.38647, 41.13249 ], [ 1.3862, 41.13248 ], [ 1.38591, 41.13253 ], [ 1.38567, 41.13264 ], [ 1.38549, 41.13278 ], [ 1.38547, 41.13284 ], [ 1.38557, 41.13302 ], [ 1.38575, 41.13318 ], [ 1.38592, 41.13334 ], [ 1.38596, 41.1334 ], [ 1.38598, 41.13346 ], [ 1.38585, 41.13367 ], [ 1.3856, 41.13383 ], [ 1.38539, 41.13396 ], [ 1.38514, 41.13405 ], [ 1.38488, 41.13414 ], [ 1.38465, 41.13421 ], [ 1.38439, 41.1343 ], [ 1.38411, 41.13438 ], [ 1.38384, 41.13441 ], [ 1.38352, 41.13443 ], [ 1.38322, 41.13444 ], [ 1.38294, 41.13444 ], [ 1.38267, 41.13443 ], [ 1.38239, 41.13443 ], [ 1.38211, 41.13441 ], [ 1.3818, 41.13438 ], [ 1.38151, 41.13437 ], [ 1.38123, 41.13434 ], [ 1.38096, 41.1343 ], [ 1.38065, 41.13425 ], [ 1.3804, 41.13422 ], [ 1.38016, 41.13419 ], [ 1.37989, 41.13414 ], [ 1.3791, 41.13402 ], [ 1.37836, 41.13391 ], [ 1.37748, 41.13373 ], [ 1.37664, 41.13355 ], [ 1.37611, 41.13344 ], [ 1.37545, 41.13327 ], [ 1.37476, 41.1331 ], [ 1.37435, 41.13294 ], [ 1.37386, 41.13278 ], [ 1.37326, 41.13253 ], [ 1.3733, 41.13264 ], [ 1.37522, 41.1384 ], [ 1.36729, 41.14019 ], [ 1.36661, 41.14019 ], [ 1.36583, 41.14019 ], [ 1.365, 41.14019 ], [ 1.36469, 41.1402 ], [ 1.36425, 41.1402 ], [ 1.36357, 41.14019 ], [ 1.36283, 41.14019 ], [ 1.36211, 41.14021 ], [ 1.36154, 41.1402 ], [ 1.361, 41.14018 ], [ 1.3611, 41.14098 ], [ 1.36114, 41.14131 ], [ 1.36121, 41.14174 ], [ 1.36128, 41.14201 ], [ 1.36135, 41.14223 ], [ 1.36153, 41.14281 ], [ 1.36176, 41.14335 ], [ 1.36183, 41.14346 ], [ 1.36191, 41.14356 ], [ 1.36197, 41.14378 ], [ 1.36199, 41.14401 ], [ 1.36202, 41.14429 ], [ 1.36209, 41.14459 ], [ 1.36218, 41.14492 ], [ 1.36223, 41.14522 ], [ 1.36227, 41.14539 ], [ 1.3623, 41.14553 ], [ 1.36233, 41.14564 ], [ 1.36238, 41.14576 ], [ 1.36244, 41.14587 ], [ 1.3625, 41.146 ], [ 1.36259, 41.14613 ], [ 1.36266, 41.14621 ], [ 1.36273, 41.1463 ], [ 1.36285, 41.14643 ] ] ] ] } diff --git a/ckanext/spatial/tests/data/hawaii.geojson b/ckanext/spatial/tests/data/hawaii.geojson new file mode 100644 index 00000000..7d962000 --- /dev/null +++ b/ckanext/spatial/tests/data/hawaii.geojson @@ -0,0 +1 @@ +{ "type": "MultiPolygon", "coordinates": [ [ [ [ -156.04965, 19.78045 ], [ -156.00627, 19.81758 ], [ -155.97665, 19.85053 ], [ -155.94925, 19.85703 ], [ -155.91566, 19.88713 ], [ -155.89253, 19.93216 ], [ -155.85659, 19.96889 ], [ -155.83195, 19.98278 ], [ -155.82547, 20.02594 ], [ -155.85038, 20.06251 ], [ -155.89065, 20.12358 ], [ -155.90278, 20.17707 ], [ -155.89066, 20.25524 ], [ -155.85329, 20.27155 ], [ -155.79888, 20.25411 ], [ -155.737, 20.22277 ], [ -155.70433, 20.19169 ], [ -155.63746, 20.15305 ], [ -155.59803, 20.12454 ], [ -155.55893, 20.13157 ], [ -155.50256, 20.11416 ], [ -155.38758, 20.06712 ], [ -155.27032, 20.01452 ], [ -155.16663, 19.93789 ], [ -155.12462, 19.89729 ], [ -155.08634, 19.8554 ], [ -155.09122, 19.77637 ], [ -155.08712, 19.72801 ], [ -155.04538, 19.73982 ], [ -155.00642, 19.73929 ], [ -154.9811, 19.69069 ], [ -154.97434, 19.6332 ], [ -154.94711, 19.60486 ], [ -154.85262, 19.54917 ], [ -154.81442, 19.53009 ], [ -154.81601, 19.50065 ], [ -154.87662, 19.43322 ], [ -154.94419, 19.38185 ], [ -155.02054, 19.33132 ], [ -155.11327, 19.29061 ], [ -155.15963, 19.26837 ], [ -155.20589, 19.26091 ], [ -155.26462, 19.27421 ], [ -155.31337, 19.2507 ], [ -155.36063, 19.20893 ], [ -155.3907, 19.20117 ], [ -155.45352, 19.15195 ], [ -155.50528, 19.13791 ], [ -155.55533, 19.06938 ], [ -155.5907, 19.00767 ], [ -155.61397, 18.9704 ], [ -155.63805, 18.94172 ], [ -155.67201, 18.91747 ], [ -155.72604, 18.96944 ], [ -155.80611, 19.01397 ], [ -155.88155, 19.03664 ], [ -155.91422, 19.09915 ], [ -155.91207, 19.17911 ], [ -155.90256, 19.25843 ], [ -155.89084, 19.2989 ], [ -155.8887, 19.34803 ], [ -155.90909, 19.41545 ], [ -155.92473, 19.45391 ], [ -155.95149, 19.48665 ], [ -155.96935, 19.55596 ], [ -155.97821, 19.60816 ], [ -155.99773, 19.64282 ], [ -156.02898, 19.6501 ], [ -156.03333, 19.66923 ], [ -156.06436, 19.73077 ], [ -156.04965, 19.78045 ] ] ], [ [ [ -157.05913, 20.91341 ], [ -157.01, 20.92976 ], [ -156.93753, 20.92527 ], [ -156.87312, 20.89468 ], [ -156.83705, 20.86357 ], [ -156.80847, 20.8204 ], [ -156.83832, 20.76458 ], [ -156.89029, 20.74485 ], [ -156.90908, 20.73953 ], [ -156.96789, 20.73508 ], [ -156.99068, 20.7759 ], [ -156.99183, 20.8266 ], [ -157.01091, 20.85448 ], [ -157.05966, 20.88463 ], [ -157.05913, 20.91341 ] ] ], [ [ [ -156.67047, 20.55991 ], [ -156.61073, 20.59377 ], [ -156.56714, 20.60489 ], [ -156.54303, 20.58011 ], [ -156.53964, 20.52764 ], [ -156.58624, 20.51171 ], [ -156.66881, 20.50474 ], [ -156.70227, 20.53245 ], [ -156.67047, 20.55991 ] ] ], [ [ [ -156.69989, 20.92063 ], [ -156.6809, 20.98026 ], [ -156.61958, 21.02779 ], [ -156.56277, 21.01617 ], [ -156.51871, 20.95466 ], [ -156.48105, 20.8982 ], [ -156.4033, 20.91583 ], [ -156.33282, 20.94645 ], [ -156.24255, 20.93784 ], [ -156.19471, 20.89197 ], [ -156.13267, 20.86137 ], [ -156.05979, 20.81054 ], [ -156.00353, 20.79555 ], [ -155.98541, 20.74424 ], [ -156.00187, 20.69806 ], [ -156.04379, 20.6649 ], [ -156.1299, 20.62752 ], [ -156.21026, 20.62852 ], [ -156.28439, 20.59649 ], [ -156.37763, 20.57843 ], [ -156.43187, 20.59814 ], [ -156.44367, 20.65602 ], [ -156.45844, 20.73668 ], [ -156.46224, 20.75395 ], [ -156.47356, 20.79076 ], [ -156.50603, 20.79946 ], [ -156.53775, 20.77841 ], [ -156.55462, 20.7861 ], [ -156.63179, 20.82124 ], [ -156.6878, 20.89072 ], [ -156.69989, 20.92063 ] ] ], [ [ [ -157.27722, 21.15843 ], [ -157.2497, 21.1844 ], [ -157.26069, 21.22568 ], [ -157.20212, 21.2193 ], [ -157.12821, 21.20149 ], [ -157.03999, 21.19091 ], [ -157.01427, 21.20069 ], [ -156.98403, 21.2122 ], [ -156.96285, 21.21213 ], [ -156.92111, 21.16907 ], [ -156.91786, 21.16902 ], [ -156.84159, 21.16793 ], [ -156.74223, 21.17621 ], [ -156.70911, 21.15865 ], [ -156.73934, 21.11134 ], [ -156.8022, 21.06709 ], [ -156.87714, 21.0493 ], [ -156.95387, 21.06613 ], [ -157.02617, 21.08901 ], [ -157.08066, 21.10198 ], [ -157.17161, 21.0907 ], [ -157.25253, 21.08767 ], [ -157.31075, 21.10163 ], [ -157.27722, 21.15843 ] ] ], [ [ [ -158.23219, 21.58381 ], [ -158.12561, 21.58674 ], [ -158.07989, 21.6281 ], [ -158.05069, 21.67122 ], [ -157.9923, 21.708 ], [ -157.96863, 21.7127 ], [ -157.92459, 21.65118 ], [ -157.87735, 21.57528 ], [ -157.83695, 21.52994 ], [ -157.84549, 21.46675 ], [ -157.8139, 21.4403 ], [ -157.76457, 21.46133 ], [ -157.72251, 21.45922 ], [ -157.72432, 21.40331 ], [ -157.7106, 21.3585 ], [ -157.6518, 21.3139 ], [ -157.65503, 21.30939 ], [ -157.67307, 21.2842 ], [ -157.7001, 21.264 ], [ -157.7572, 21.278 ], [ -157.77994, 21.26525 ], [ -157.8096, 21.2577 ], [ -157.85105, 21.28453 ], [ -157.89, 21.3065 ], [ -157.95074, 21.31251 ], [ -157.98153, 21.3159 ], [ -158.0245, 21.3093 ], [ -158.0883, 21.2988 ], [ -158.1033, 21.2979 ], [ -158.12937, 21.34482 ], [ -158.13093, 21.34896 ], [ -158.1403, 21.3738 ], [ -158.1792, 21.4043 ], [ -158.18265, 21.43007 ], [ -158.233, 21.4876 ], [ -158.23117, 21.52386 ], [ -158.27768, 21.57879 ], [ -158.23219, 21.58381 ] ] ], [ [ [ -160.24961, 21.81514 ], [ -160.22896, 21.88912 ], [ -160.19396, 21.92239 ], [ -160.13705, 21.94863 ], [ -160.12226, 21.96288 ], [ -160.11275, 21.99525 ], [ -160.07212, 22.00333 ], [ -160.05854, 21.99638 ], [ -160.05113, 21.98106 ], [ -160.07029, 21.96395 ], [ -160.08579, 21.92729 ], [ -160.07907, 21.89608 ], [ -160.12428, 21.87679 ], [ -160.15609, 21.86793 ], [ -160.1748, 21.84692 ], [ -160.18978, 21.82245 ], [ -160.20585, 21.77952 ], [ -160.23037, 21.78967 ], [ -160.24961, 21.81514 ] ] ], [ [ [ -159.78375, 22.0649 ], [ -159.74525, 22.09751 ], [ -159.73054, 22.13995 ], [ -159.70553, 22.15932 ], [ -159.61165, 22.20139 ], [ -159.58106, 22.22349 ], [ -159.54392, 22.2217 ], [ -159.51076, 22.20355 ], [ -159.48794, 22.22951 ], [ -159.43171, 22.22002 ], [ -159.40247, 22.2326 ], [ -159.36151, 22.21409 ], [ -159.31229, 22.18308 ], [ -159.29301, 22.12296 ], [ -159.31828, 22.06142 ], [ -159.33449, 22.0417 ], [ -159.33256, 21.99935 ], [ -159.33768, 21.95117 ], [ -159.38527, 21.91244 ], [ -159.44487, 21.86863 ], [ -159.52692, 21.88389 ], [ -159.57452, 21.89281 ], [ -159.60328, 21.89225 ], [ -159.64977, 21.93385 ], [ -159.7078, 21.96123 ], [ -159.7548, 21.97777 ], [ -159.7867, 22.0188 ], [ -159.78375, 22.0649 ] ] ] ] } diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index 2e26d108..eea98485 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -49,6 +49,45 @@ def test_spatial_query_wrong_bbox(self): extras={"ext_bbox": "-10,-20,10,a"}, ) + def test_spatial_real_multipolygon_inside_extent_no_intersect(self): + """ + Testing this scenario, will return a result as the whole extent of + the two polygons was indexed: + + xxxxxxxx + xxx xx xxxx + x Polygon 1 x x xxxx + x xxx xx xx + x xx x xx + xxxxxxxx xx xxx + ┌────────┐ xxxx x + │ Search │ xx xx + │ BBox │ xx x + │ │ x Polygon 2 xx + └────────┘ xx x + xx xxxx + x xxxxx + x xxxx + xxxx xxxx + xxx + + """ + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/hawaii.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-156.570,19.959,-155.960,20.444"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + def test_spatial_query_nz(self): dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) @@ -147,4 +186,240 @@ def test_spatial_query_dateline_4(self): assert result["results"][0]["id"] == dataset["id"] +@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") +@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr-spatial-field") +class TestSpatialFieldSearch(SpatialTestBase): + def test_spatial_query_point(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_polygon(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["polygon"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_real_polygon(self): + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/altafulla.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_real_multipolygon(self): + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/hawaii.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_real_multipolygon_inside_extent_no_intersect(self): + """ + Testing this scenario, should not return results: + + xxxxxxxx + xxx xx xxxx + x Polygon 1 x x xxxx + x xxx xx xx + x xx x xx + xxxxxxxx xx xxx + ┌────────┐ xxxx x + │ Search │ xx xx + │ BBox │ xx x + │ │ x Polygon 2 xx + └────────┘ xx x + xx xxxx + x xxxxx + x xxxx + xxxx xxxx + xxx + + """ + factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/hawaii.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-156.570,19.959,-155.960,20.444"} + ) + + assert result["count"] == 0 + + def test_spatial_polygon_holes(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["polygon_holes"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_multipolygon(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["multipolygon"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_outside_bbox(self): + + factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-10,-20,10,20"} + ) + + assert result["count"] == 0 + + def test_spatial_query_wrong_bbox(self): + with pytest.raises(SearchError): + helpers.call_action( + "package_search", + extras={"ext_bbox": "-10,-20,10,a"}, + ) + + def test_spatial_query_nz(self): + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "56,-54,189,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_nz_wrap(self): + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-203,-54,-167,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_ohio(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["ohio"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-110,37,-78,53"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_ohio_wrap(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["ohio"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "258,37,281,51"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_1(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-197,56,-128,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_2(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "162,54,237,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_3(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline2"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-197,56,-128,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_query_dateline_4(self): + + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": extents["dateline2"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "162,54,237,70"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] From c4fd9ce21a27d28cdad6828c5453b04256dcca3d Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 5 Sep 2022 12:08:22 +0200 Subject: [PATCH 30/66] Re-add lost logic to remove fields from the index (take 2) --- ckanext/spatial/plugin/__init__.py | 11 +++++++++++ ckanext/spatial/tests/test_spatial_search.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 0cd8378f..72fdf2c8 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -264,6 +264,17 @@ def before_dataset_index(self, pkg_dict): pkg_dict['spatial_geom'] = wkt + # Coupled resources are URL -> uuid links, they are not needed in SOLR + # and might be huge if there are lot of coupled resources + if pkg_dict.get('coupled-resource'): + pkg_dict.pop('coupled-resource', None) + pkg_dict.pop('extras_coupled-resource', None) + + # spatial field is geojson coordinate data, not needed in SOLR either + if pkg_dict.get('spatial'): + pkg_dict.pop('spatial', None) + pkg_dict.pop('extras_spatial', None) + return pkg_dict def before_dataset_search(self, search_params): diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index eea98485..ce3f6720 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import pytest from ckan.lib.search import SearchError @@ -88,6 +90,23 @@ def test_spatial_real_multipolygon_inside_extent_no_intersect(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] + def test_spatial_polygon_split_across_antimeridian(self): + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/chukot.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "175,61,179,64"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + def test_spatial_query_nz(self): dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) From 07a028f06f9b5a0263431926d7ba9b93c9f1f6e5 Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 5 Sep 2022 12:36:56 +0200 Subject: [PATCH 31/66] Handle some antimeridian cases --- ckanext/spatial/lib/__init__.py | 20 +++++++ ckanext/spatial/plugin/__init__.py | 11 ++-- ckanext/spatial/tests/test_spatial_search.py | 55 ++++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/ckanext/spatial/lib/__init__.py b/ckanext/spatial/lib/__init__.py index fd0cd41d..2e841981 100644 --- a/ckanext/spatial/lib/__init__.py +++ b/ckanext/spatial/lib/__init__.py @@ -105,3 +105,23 @@ def _adjust_latitude(value): "miny": _adjust_latitude(bbox_dict["miny"]), "maxy": _adjust_latitude(bbox_dict["maxy"]), } + + +def fit_linear_ring(lr): + + bbox = { + "minx": lr[0][0], + "maxx": lr[2][0], + "miny": lr[1][1], + "maxy": lr[0][1], + } + + bbox = fit_bbox(bbox) + + return [ + (bbox["minx"], bbox["maxy"]), + (bbox["minx"], bbox["miny"]), + (bbox["maxx"], bbox["miny"]), + (bbox["maxx"], bbox["maxy"]), + (bbox["minx"], bbox["maxy"]), + ] diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 72fdf2c8..62fa377a 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -13,7 +13,7 @@ from ckan.lib.search import SearchError from ckan.lib.helpers import json -from ckanext.spatial.lib import normalize_bbox, fit_bbox +from ckanext.spatial.lib import normalize_bbox, fit_bbox, fit_linear_ring if tk.check_ckan_version(min_version="2.9.0"): from ckanext.spatial.plugin.flask_plugin import ( @@ -36,7 +36,6 @@ ] - class SpatialMetadata(p.SingletonPlugin): p.implements(p.IPackageController, inherit=True) @@ -251,8 +250,12 @@ def before_dataset_index(self, pkg_dict): # Check if coordinates are defined counter-clockwise, # otherwise we'll get wrong results from Solr lr = shapely.geometry.polygon.LinearRing(geometry['coordinates'][0]) - lr_coords = list(lr.coords) if lr.is_ccw else reversed(list(lr.coords)) - polygon = shapely.geometry.polygon.Polygon(lr_coords) + lr_coords = ( + list(lr.coords) if lr.is_ccw + else reversed(list(lr.coords)) + ) + polygon = shapely.geometry.polygon.Polygon( + fit_linear_ring(lr_coords)) wkt = polygon.wkt if not wkt: diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index ce3f6720..a446fa91 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -107,6 +107,29 @@ def test_spatial_polygon_split_across_antimeridian(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] + def test_spatial_polygon_split_across_antimeridian_outside_bbox(self): + """ + This test passes because as the geometry passes the antemeridian, the + extent generated to be index is (-180, miny, 180, maxy). Sites needing to + deal with this scenario should use the `solr-spatial-field` backend. + See ``TestSpatialFieldSearch.test_spatial_polygon_split_across_antimeridian_outside_bbox`` + """ + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/chukot.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "0,61,15,64"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + def test_spatial_query_nz(self): dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) @@ -442,3 +465,35 @@ def test_spatial_query_dateline_4(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] + def test_spatial_polygon_split_across_antimeridian(self): + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/chukot.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "175,61,179,64"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_spatial_polygon_split_across_antimeridian_outside_bbox(self): + factories.Dataset( + extras=[ + { + "key": "spatial", + "value": self.read_file("data/chukot.geojson") + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "0,61,15,64"} + ) + + assert result["count"] == 0 From 244b2ba20f78c2d897ae64b1b218d379fdf339c4 Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 5 Sep 2022 15:05:02 +0200 Subject: [PATCH 32/66] More antimeridian fixes --- ckanext/spatial/lib/__init__.py | 4 +- ckanext/spatial/plugin/__init__.py | 8 ++- ckanext/spatial/tests/data/chukot.geojson | 1 + ckanext/spatial/tests/test_spatial_search.py | 73 ++++++++++++++------ 4 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 ckanext/spatial/tests/data/chukot.geojson diff --git a/ckanext/spatial/lib/__init__.py b/ckanext/spatial/lib/__init__.py index 2e841981..2e3fcaa4 100644 --- a/ckanext/spatial/lib/__init__.py +++ b/ckanext/spatial/lib/__init__.py @@ -112,8 +112,8 @@ def fit_linear_ring(lr): bbox = { "minx": lr[0][0], "maxx": lr[2][0], - "miny": lr[1][1], - "maxy": lr[0][1], + "miny": lr[0][1], + "maxy": lr[2][1], } bbox = fit_bbox(bbox) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 62fa377a..081ad597 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -252,7 +252,7 @@ def before_dataset_index(self, pkg_dict): lr = shapely.geometry.polygon.LinearRing(geometry['coordinates'][0]) lr_coords = ( list(lr.coords) if lr.is_ccw - else reversed(list(lr.coords)) + else list(reversed(list(lr.coords))) ) polygon = shapely.geometry.polygon.Polygon( fit_linear_ring(lr_coords)) @@ -263,6 +263,12 @@ def before_dataset_index(self, pkg_dict): if not shape.is_valid: log.error('Wrong geometry, not indexing') return pkg_dict + if shape.bounds[0] < -180 or shape.bounds[2] > 180: + import ipdb; ipdb.set_trace() + log.error(""" +Geometries outside the -180, -90, 180, 90 boundaries are not supported, +you need to split the geometry in order to fit the parts. Not indexing""") + return pkg_dict wkt = shape.wkt pkg_dict['spatial_geom'] = wkt diff --git a/ckanext/spatial/tests/data/chukot.geojson b/ckanext/spatial/tests/data/chukot.geojson new file mode 100644 index 00000000..030b6ccc --- /dev/null +++ b/ckanext/spatial/tests/data/chukot.geojson @@ -0,0 +1 @@ +{ "type": "MultiPolygon", "coordinates": [ [ [ [ -173.1974, 64.2578 ], [ -173.1943, 64.2578 ], [ -173.1943, 64.2568 ], [ -173.1974, 64.2568 ], [ -173.1974, 64.2578 ] ] ], [ [ [ -173.3099, 64.3141 ], [ -173.3068, 64.3141 ], [ -173.3068, 64.313 ], [ -173.3099, 64.313 ], [ -173.3099, 64.3141 ] ] ], [ [ [ -172.975, 64.4437 ], [ -172.9766, 64.4453 ], [ -172.9714, 64.4453 ], [ -172.9714, 64.4443 ], [ -172.975, 64.4437 ] ] ], [ [ [ -172.9641, 64.4474 ], [ -172.9609, 64.4474 ], [ -172.9609, 64.4464 ], [ -172.9641, 64.4464 ], [ -172.9641, 64.4474 ] ] ], [ [ [ -172.2505, 64.412 ], [ -172.2401, 64.4078 ], [ -172.238, 64.4026 ], [ -172.2437, 64.4042 ], [ -172.25, 64.4042 ], [ -172.2542, 64.4021 ], [ -172.2583, 64.4042 ], [ -172.2771, 64.4042 ], [ -172.2812, 64.4083 ], [ -172.2786, 64.412 ], [ -172.2734, 64.413 ], [ -172.2786, 64.4182 ], [ -172.2708, 64.4208 ], [ -172.2641, 64.4172 ], [ -172.2568, 64.4161 ], [ -172.2505, 64.412 ] ] ], [ [ [ -172.2828, 64.4203 ], [ -172.2797, 64.4203 ], [ -172.2797, 64.4193 ], [ -172.2828, 64.4193 ], [ -172.2828, 64.4203 ] ] ], [ [ [ -172.3667, 64.4729 ], [ -172.3682, 64.4745 ], [ -172.363, 64.4745 ], [ -172.363, 64.4734 ], [ -172.3667, 64.4729 ] ] ], [ [ [ -172.6604, 64.6375 ], [ -172.6479, 64.6354 ], [ -172.6417, 64.6396 ], [ -172.6354, 64.6375 ], [ -172.625, 64.6417 ], [ -172.6151, 64.6391 ], [ -172.6042, 64.6333 ], [ -172.5917, 64.6333 ], [ -172.5875, 64.6312 ], [ -172.5708, 64.6312 ], [ -172.5505, 64.638 ], [ -172.5479, 64.6417 ], [ -172.5437, 64.6396 ], [ -172.5333, 64.6396 ], [ -172.5167, 64.6458 ], [ -172.5063, 64.6458 ], [ -172.4937, 64.6417 ], [ -172.4875, 64.6438 ], [ -172.4734, 64.6391 ], [ -172.4745, 64.6359 ], [ -172.4687, 64.6354 ], [ -172.463, 64.6328 ], [ -172.4641, 64.6297 ], [ -172.4583, 64.6292 ], [ -172.4484, 64.6245 ], [ -172.4495, 64.6214 ], [ -172.4463, 64.6193 ], [ -172.4495, 64.6151 ], [ -172.4463, 64.613 ], [ -172.4516, 64.612 ], [ -172.4547, 64.6068 ], [ -172.4661, 64.6057 ], [ -172.4687, 64.6021 ], [ -172.475, 64.6042 ], [ -172.4891, 64.5995 ], [ -172.4859, 64.5964 ], [ -172.4958, 64.5917 ], [ -172.5036, 64.5922 ], [ -172.5125, 64.5979 ], [ -172.5203, 64.5974 ], [ -172.5172, 64.5943 ], [ -172.5208, 64.5917 ], [ -172.5333, 64.5938 ], [ -172.5417, 64.5979 ], [ -172.5625, 64.5979 ], [ -172.5667, 64.5958 ], [ -172.5771, 64.5958 ], [ -172.5813, 64.5938 ], [ -172.5958, 64.5938 ], [ -172.6, 64.5958 ], [ -172.6438, 64.5958 ], [ -172.6479, 64.5979 ], [ -172.6542, 64.5979 ], [ -172.6583, 64.5958 ], [ -172.6625, 64.5979 ], [ -172.6708, 64.5979 ], [ -172.6766, 64.6005 ], [ -172.6792, 64.6062 ], [ -172.6776, 64.6099 ], [ -172.6807, 64.6109 ], [ -172.6776, 64.6162 ], [ -172.6859, 64.6203 ], [ -172.6917, 64.6188 ], [ -172.7187, 64.6188 ], [ -172.7229, 64.6167 ], [ -172.7287, 64.6193 ], [ -172.7312, 64.6229 ], [ -172.7307, 64.6245 ], [ -172.7255, 64.6255 ], [ -172.7234, 64.6276 ], [ -172.7266, 64.6307 ], [ -172.7214, 64.6318 ], [ -172.7161, 64.6391 ], [ -172.7109, 64.6401 ], [ -172.7088, 64.6422 ], [ -172.7078, 64.6453 ], [ -172.7026, 64.6464 ], [ -172.6995, 64.6495 ], [ -172.6917, 64.6521 ], [ -172.6859, 64.6495 ], [ -172.687, 64.6464 ], [ -172.6797, 64.6432 ], [ -172.6766, 64.6401 ], [ -172.6604, 64.6375 ] ] ], [ [ [ -172.9349, 64.4557 ], [ -172.9318, 64.4557 ], [ -172.9318, 64.4547 ], [ -172.9349, 64.4547 ], [ -172.9349, 64.4557 ] ] ], [ [ [ -172.9463, 64.4547 ], [ -172.9557, 64.4547 ], [ -172.9547, 64.4578 ], [ -172.9578, 64.4599 ], [ -172.95, 64.4604 ], [ -172.9401, 64.4578 ], [ -172.9463, 64.4547 ] ] ], [ [ [ -172.888, 64.4776 ], [ -172.8958, 64.4771 ], [ -172.8974, 64.4786 ], [ -172.8896, 64.4792 ], [ -172.888, 64.4776 ] ] ], [ [ [ -172.9833, 64.8208 ], [ -172.9974, 64.8266 ], [ -172.9937, 64.8292 ], [ -172.9771, 64.8292 ], [ -172.9755, 64.8307 ], [ -172.9787, 64.8328 ], [ -172.9766, 64.8349 ], [ -172.9672, 64.8349 ], [ -172.9682, 64.8318 ], [ -172.9651, 64.8297 ], [ -172.9693, 64.8234 ], [ -172.9792, 64.8229 ], [ -172.9833, 64.8208 ] ] ], [ [ [ -172.5375, 64.6854 ], [ -172.5417, 64.6854 ], [ -172.5432, 64.687 ], [ -172.5359, 64.687 ], [ -172.5375, 64.6854 ] ] ], [ [ [ -172.5536, 64.687 ], [ -172.5505, 64.687 ], [ -172.5505, 64.6859 ], [ -172.5536, 64.6859 ], [ -172.5536, 64.687 ] ] ], [ [ [ -172.3771, 64.8313 ], [ -172.3708, 64.8292 ], [ -172.3604, 64.8292 ], [ -172.3562, 64.8271 ], [ -172.3521, 64.8292 ], [ -172.3479, 64.8271 ], [ -172.3375, 64.8292 ], [ -172.3292, 64.825 ], [ -172.3146, 64.825 ], [ -172.3104, 64.8271 ], [ -172.3042, 64.825 ], [ -172.3, 64.8271 ], [ -172.2937, 64.825 ], [ -172.2896, 64.8271 ], [ -172.2833, 64.8271 ], [ -172.2734, 64.8245 ], [ -172.2703, 64.8214 ], [ -172.263, 64.8203 ], [ -172.2578, 64.8172 ], [ -172.2505, 64.8161 ], [ -172.2516, 64.813 ], [ -172.2443, 64.812 ], [ -172.2432, 64.8089 ], [ -172.2359, 64.8078 ], [ -172.2292, 64.8042 ], [ -172.2208, 64.8042 ], [ -172.1917, 64.7958 ], [ -172.1771, 64.7958 ], [ -172.1708, 64.7917 ], [ -172.1604, 64.7917 ], [ -172.1521, 64.7875 ], [ -172.138, 64.7849 ], [ -172.1333, 64.7813 ], [ -172.1292, 64.7813 ], [ -172.1083, 64.775 ], [ -172.0979, 64.775 ], [ -172.0937, 64.7729 ], [ -172.0896, 64.7729 ], [ -172.0859, 64.7703 ], [ -172.0849, 64.7672 ], [ -172.0776, 64.7662 ], [ -172.063, 64.7568 ], [ -172.0708, 64.7542 ], [ -172.0771, 64.7562 ], [ -172.0813, 64.7542 ], [ -172.1062, 64.7542 ], [ -172.1104, 64.7583 ], [ -172.125, 64.7583 ], [ -172.1286, 64.7557 ], [ -172.1214, 64.7516 ], [ -172.125, 64.7479 ], [ -172.1562, 64.7479 ], [ -172.1708, 64.7438 ], [ -172.1938, 64.7438 ], [ -172.1979, 64.7417 ], [ -172.2021, 64.7438 ], [ -172.2167, 64.7438 ], [ -172.2208, 64.7417 ], [ -172.2292, 64.7417 ], [ -172.2333, 64.7396 ], [ -172.2375, 64.7417 ], [ -172.2422, 64.738 ], [ -172.2474, 64.737 ], [ -172.2505, 64.7318 ], [ -172.2599, 64.7328 ], [ -172.263, 64.7297 ], [ -172.2745, 64.7266 ], [ -172.2755, 64.7234 ], [ -172.2995, 64.7161 ], [ -172.3026, 64.713 ], [ -172.3104, 64.7104 ], [ -172.3167, 64.7125 ], [ -172.3396, 64.7042 ], [ -172.3437, 64.7063 ], [ -172.3542, 64.7021 ], [ -172.4042, 64.7021 ], [ -172.4083, 64.7042 ], [ -172.4146, 64.7042 ], [ -172.4187, 64.7021 ], [ -172.4292, 64.7021 ], [ -172.4333, 64.7042 ], [ -172.4396, 64.7042 ], [ -172.4438, 64.7021 ], [ -172.4812, 64.7 ], [ -172.4958, 64.6958 ], [ -172.5021, 64.6958 ], [ -172.5099, 64.6932 ], [ -172.5167, 64.6875 ], [ -172.5224, 64.688 ], [ -172.5213, 64.6932 ], [ -172.5229, 64.6937 ], [ -172.5437, 64.6937 ], [ -172.5479, 64.6917 ], [ -172.5583, 64.6917 ], [ -172.5661, 64.6891 ], [ -172.5641, 64.6859 ], [ -172.5568, 64.6849 ], [ -172.5562, 64.6833 ], [ -172.5568, 64.6818 ], [ -172.5646, 64.6792 ], [ -172.5854, 64.6792 ], [ -172.587, 64.6807 ], [ -172.5818, 64.6828 ], [ -172.588, 64.687 ], [ -172.5979, 64.6896 ], [ -172.637, 64.6922 ], [ -172.6318, 64.6943 ], [ -172.6349, 64.6974 ], [ -172.6318, 64.7005 ], [ -172.6286, 64.7078 ], [ -172.6172, 64.7109 ], [ -172.612, 64.7182 ], [ -172.6068, 64.7193 ], [ -172.6037, 64.7224 ], [ -172.5984, 64.7234 ], [ -172.5964, 64.7255 ], [ -172.5974, 64.7328 ], [ -172.5849, 64.7516 ], [ -172.5734, 64.7547 ], [ -172.5713, 64.7578 ], [ -172.5745, 64.7599 ], [ -172.563, 64.763 ], [ -172.5599, 64.7682 ], [ -172.5547, 64.7693 ], [ -172.5557, 64.7724 ], [ -172.5443, 64.7755 ], [ -172.5432, 64.7786 ], [ -172.5255, 64.7839 ], [ -172.5286, 64.787 ], [ -172.5234, 64.788 ], [ -172.5182, 64.7932 ], [ -172.513, 64.7943 ], [ -172.512, 64.7974 ], [ -172.5068, 64.7984 ], [ -172.5047, 64.8005 ], [ -172.5078, 64.8037 ], [ -172.5016, 64.812 ], [ -172.4937, 64.8146 ], [ -172.4875, 64.8146 ], [ -172.4833, 64.8125 ], [ -172.4792, 64.8146 ], [ -172.4583, 64.8146 ], [ -172.4505, 64.8172 ], [ -172.4474, 64.8203 ], [ -172.4396, 64.8208 ], [ -172.4318, 64.8234 ], [ -172.4286, 64.8266 ], [ -172.4208, 64.8292 ], [ -172.4146, 64.8292 ], [ -172.4042, 64.8333 ], [ -172.3979, 64.8333 ], [ -172.3938, 64.8313 ], [ -172.3771, 64.8313 ] ] ], [ [ [ -172.7641, 64.8224 ], [ -172.7609, 64.8224 ], [ -172.7609, 64.8214 ], [ -172.7641, 64.8214 ], [ -172.7641, 64.8224 ] ] ], [ [ [ -172.8109, 64.8339 ], [ -172.8188, 64.8333 ], [ -172.8203, 64.8349 ], [ -172.8125, 64.8354 ], [ -172.8109, 64.8339 ] ] ], [ [ [ -172.5412, 64.9005 ], [ -172.5412, 64.9016 ], [ -172.538, 64.9016 ], [ -172.538, 64.9005 ], [ -172.5412, 64.9005 ] ] ], [ [ [ -172.15, 65.125 ], [ -172.1463, 65.1245 ], [ -172.1443, 65.1214 ], [ -172.15, 65.1208 ], [ -172.1557, 65.1234 ], [ -172.1542, 65.125 ], [ -172.15, 65.125 ] ] ], [ [ [ -172.2495, 65.2161 ], [ -172.2464, 65.2161 ], [ -172.2464, 65.2151 ], [ -172.2495, 65.2151 ], [ -172.2495, 65.2161 ] ] ], [ [ [ 178.7964, 64.6391 ], [ 178.8021, 64.6417 ], [ 178.8078, 64.6422 ], [ 178.8161, 64.6464 ], [ 178.8109, 64.6484 ], [ 178.8141, 64.6516 ], [ 178.8005, 64.6526 ], [ 178.8037, 64.6557 ], [ 178.7818, 64.663 ], [ 178.7786, 64.6661 ], [ 178.7693, 64.6693 ], [ 178.7604, 64.675 ], [ 178.7542, 64.6729 ], [ 178.75, 64.675 ], [ 178.7458, 64.6729 ], [ 178.7354, 64.6729 ], [ 178.7312, 64.675 ], [ 178.725, 64.6729 ], [ 178.7208, 64.675 ], [ 178.7063, 64.6708 ], [ 178.6812, 64.6729 ], [ 178.6771, 64.6708 ], [ 178.6583, 64.6708 ], [ 178.6484, 64.6682 ], [ 178.6417, 64.6646 ], [ 178.6229, 64.6646 ], [ 178.6187, 64.6625 ], [ 178.6125, 64.6625 ], [ 178.5943, 64.6578 ], [ 178.5792, 64.65 ], [ 178.5604, 64.65 ], [ 178.5562, 64.6521 ], [ 178.55, 64.65 ], [ 178.5458, 64.6521 ], [ 178.5417, 64.65 ], [ 178.5271, 64.65 ], [ 178.5229, 64.6479 ], [ 178.4937, 64.6479 ], [ 178.4896, 64.6458 ], [ 178.4646, 64.6458 ], [ 178.4604, 64.6438 ], [ 178.4312, 64.6438 ], [ 178.4271, 64.6417 ], [ 178.4229, 64.6438 ], [ 178.4187, 64.6417 ], [ 178.4083, 64.6417 ], [ 178.4021, 64.6458 ], [ 178.3958, 64.6458 ], [ 178.3943, 64.6443 ], [ 178.3995, 64.6432 ], [ 178.4016, 64.6401 ], [ 178.3938, 64.6396 ], [ 178.3896, 64.6375 ], [ 178.3771, 64.6375 ], [ 178.3729, 64.6354 ], [ 178.3375, 64.6354 ], [ 178.3313, 64.6396 ], [ 178.3208, 64.6396 ], [ 178.3193, 64.6412 ], [ 178.3224, 64.6432 ], [ 178.3109, 64.6464 ], [ 178.3109, 64.6495 ], [ 178.3141, 64.6516 ], [ 178.3062, 64.6542 ], [ 178.3021, 64.6521 ], [ 178.2917, 64.6521 ], [ 178.2771, 64.6563 ], [ 178.2646, 64.6563 ], [ 178.2604, 64.6583 ], [ 178.2437, 64.6583 ], [ 178.2396, 64.6604 ], [ 178.225, 64.6604 ], [ 178.2208, 64.6625 ], [ 178.2104, 64.6604 ], [ 178.2063, 64.6625 ], [ 178.1979, 64.6625 ], [ 178.1938, 64.6646 ], [ 178.1833, 64.6646 ], [ 178.1792, 64.6667 ], [ 178.1729, 64.6667 ], [ 178.1687, 64.6646 ], [ 178.15, 64.6646 ], [ 178.1458, 64.6667 ], [ 178.1396, 64.6667 ], [ 178.1354, 64.6646 ], [ 178.1208, 64.6646 ], [ 178.1167, 64.6625 ], [ 178.1062, 64.6625 ], [ 178.0854, 64.6563 ], [ 178.0813, 64.6563 ], [ 178.0703, 64.6505 ], [ 178.0562, 64.6479 ], [ 178.0479, 64.6438 ], [ 178.0333, 64.6417 ], [ 178.025, 64.6375 ], [ 178.0146, 64.6375 ], [ 178.013, 64.6391 ], [ 178.0188, 64.6417 ], [ 178.0245, 64.6422 ], [ 178.0255, 64.6453 ], [ 178.0328, 64.6464 ], [ 178.0328, 64.6495 ], [ 178.0297, 64.6547 ], [ 178.0333, 64.6583 ], [ 178.0307, 64.6661 ], [ 178.0286, 64.6682 ], [ 178.0234, 64.6693 ], [ 178.0224, 64.6724 ], [ 178.0146, 64.6729 ], [ 178.0042, 64.6771 ], [ 177.9979, 64.6771 ], [ 177.9875, 64.6813 ], [ 177.9812, 64.6792 ], [ 177.9771, 64.6813 ], [ 177.9563, 64.6813 ], [ 177.9521, 64.6833 ], [ 177.9083, 64.6833 ], [ 177.9042, 64.6813 ], [ 177.8979, 64.6833 ], [ 177.8938, 64.6813 ], [ 177.8792, 64.6813 ], [ 177.875, 64.6792 ], [ 177.8521, 64.6792 ], [ 177.8479, 64.6771 ], [ 177.8375, 64.6771 ], [ 177.8333, 64.6792 ], [ 177.8167, 64.6792 ], [ 177.8125, 64.6813 ], [ 177.7958, 64.6813 ], [ 177.7917, 64.6833 ], [ 177.7604, 64.6833 ], [ 177.7458, 64.6875 ], [ 177.7146, 64.6875 ], [ 177.7104, 64.6896 ], [ 177.7042, 64.6896 ], [ 177.6859, 64.6943 ], [ 177.6833, 64.6979 ], [ 177.6771, 64.6979 ], [ 177.6729, 64.6958 ], [ 177.6687, 64.6979 ], [ 177.6625, 64.6958 ], [ 177.6375, 64.6958 ], [ 177.6359, 64.6974 ], [ 177.6458, 64.7021 ], [ 177.6563, 64.6979 ], [ 177.6625, 64.7 ], [ 177.6724, 64.7005 ], [ 177.6766, 64.7026 ], [ 177.6755, 64.7078 ], [ 177.6787, 64.7099 ], [ 177.6766, 64.7141 ], [ 177.6714, 64.7151 ], [ 177.6703, 64.7182 ], [ 177.6563, 64.7188 ], [ 177.6484, 64.7214 ], [ 177.6474, 64.7245 ], [ 177.6422, 64.7255 ], [ 177.6422, 64.7286 ], [ 177.6453, 64.7307 ], [ 177.6411, 64.7391 ], [ 177.6359, 64.7401 ], [ 177.6391, 64.7432 ], [ 177.6307, 64.7495 ], [ 177.6214, 64.7526 ], [ 177.6182, 64.7557 ], [ 177.6042, 64.7604 ], [ 177.5562, 64.7604 ], [ 177.5521, 64.7583 ], [ 177.5458, 64.7604 ], [ 177.5417, 64.7583 ], [ 177.5339, 64.7589 ], [ 177.5339, 64.762 ], [ 177.5412, 64.7651 ], [ 177.5401, 64.7682 ], [ 177.5458, 64.7708 ], [ 177.5578, 64.7714 ], [ 177.5568, 64.7745 ], [ 177.5682, 64.7797 ], [ 177.5641, 64.7911 ], [ 177.5526, 64.7943 ], [ 177.5557, 64.7974 ], [ 177.5479, 64.7979 ], [ 177.5375, 64.8021 ], [ 177.525, 64.8021 ], [ 177.5208, 64.8042 ], [ 177.5167, 64.8021 ], [ 177.4979, 64.8021 ], [ 177.4937, 64.8 ], [ 177.4833, 64.8 ], [ 177.4708, 64.7937 ], [ 177.4604, 64.7937 ], [ 177.4557, 64.7995 ], [ 177.4516, 64.8016 ], [ 177.4271, 64.8042 ], [ 177.4255, 64.8057 ], [ 177.4287, 64.8078 ], [ 177.4193, 64.8089 ], [ 177.4162, 64.812 ], [ 177.4109, 64.813 ], [ 177.4104, 64.8146 ], [ 177.4125, 64.8167 ], [ 177.4271, 64.8167 ], [ 177.4312, 64.8187 ], [ 177.4937, 64.8187 ], [ 177.5349, 64.8401 ], [ 177.5349, 64.8432 ], [ 177.5333, 64.8438 ], [ 177.525, 64.8438 ], [ 177.5104, 64.8479 ], [ 177.5042, 64.8479 ], [ 177.5016, 64.8516 ], [ 177.4937, 64.8521 ], [ 177.4901, 64.8547 ], [ 177.4932, 64.8568 ], [ 177.4922, 64.8599 ], [ 177.4995, 64.8651 ], [ 177.4984, 64.8703 ], [ 177.5057, 64.8734 ], [ 177.5021, 64.8812 ], [ 177.5057, 64.8839 ], [ 177.5047, 64.887 ], [ 177.5078, 64.888 ], [ 177.5089, 64.8912 ], [ 177.512, 64.8932 ], [ 177.5068, 64.8943 ], [ 177.5047, 64.8974 ], [ 177.5078, 64.8984 ], [ 177.5089, 64.9016 ], [ 177.5224, 64.9089 ], [ 177.5193, 64.9203 ], [ 177.5234, 64.9224 ], [ 177.5354, 64.9229 ], [ 177.5396, 64.9208 ], [ 177.5562, 64.9208 ], [ 177.5745, 64.9255 ], [ 177.5729, 64.9271 ], [ 177.5667, 64.9271 ], [ 177.5542, 64.9229 ], [ 177.55, 64.925 ], [ 177.5333, 64.925 ], [ 177.5292, 64.9271 ], [ 177.5188, 64.9271 ], [ 177.5104, 64.9229 ], [ 177.5047, 64.9224 ], [ 177.5042, 64.9187 ], [ 177.5057, 64.9151 ], [ 177.5, 64.9125 ], [ 177.4896, 64.9167 ], [ 177.4542, 64.9167 ], [ 177.45, 64.9146 ], [ 177.4312, 64.9146 ], [ 177.4271, 64.9125 ], [ 177.4104, 64.9125 ], [ 177.4063, 64.9167 ], [ 177.3854, 64.9167 ], [ 177.3813, 64.9146 ], [ 177.3521, 64.9146 ], [ 177.3479, 64.9125 ], [ 177.3438, 64.9146 ], [ 177.3375, 64.9125 ], [ 177.3333, 64.9125 ], [ 177.3292, 64.9146 ], [ 177.3229, 64.9125 ], [ 177.3083, 64.9125 ], [ 177.2937, 64.9167 ], [ 177.2708, 64.9167 ], [ 177.2604, 64.9208 ], [ 177.2542, 64.9208 ], [ 177.2464, 64.9234 ], [ 177.2432, 64.9287 ], [ 177.2339, 64.9297 ], [ 177.237, 64.9328 ], [ 177.2318, 64.9349 ], [ 177.2432, 64.9401 ], [ 177.2464, 64.9474 ], [ 177.2495, 64.9484 ], [ 177.2484, 64.9516 ], [ 177.2516, 64.9526 ], [ 177.2495, 64.9599 ], [ 177.2443, 64.962 ], [ 177.2474, 64.963 ], [ 177.2484, 64.9661 ], [ 177.2557, 64.9672 ], [ 177.2505, 64.9703 ], [ 177.2536, 64.9714 ], [ 177.2526, 64.9745 ], [ 177.2563, 64.9771 ], [ 177.2536, 64.9828 ], [ 177.2443, 64.9838 ], [ 177.2412, 64.987 ], [ 177.2318, 64.9901 ], [ 177.2245, 65.0036 ], [ 177.2182, 65.0078 ], [ 177.1979, 65.0104 ], [ 177.1771, 65.0167 ], [ 177.15, 65.0167 ], [ 177.1458, 65.0188 ], [ 177.1417, 65.0167 ], [ 177.1354, 65.0167 ], [ 177.125, 65.0208 ], [ 177.1172, 65.0214 ], [ 177.1162, 65.0245 ], [ 177.1068, 65.0255 ], [ 177.1078, 65.0286 ], [ 177.1037, 65.0307 ], [ 177.0854, 65.0354 ], [ 177.0729, 65.0354 ], [ 177.0583, 65.0396 ], [ 177.0521, 65.0375 ], [ 177.0479, 65.0375 ], [ 177.0437, 65.0396 ], [ 177.0167, 65.0396 ], [ 177.0151, 65.0411 ], [ 177.0182, 65.0422 ], [ 177.0172, 65.0474 ], [ 177.0203, 65.0495 ], [ 177.0167, 65.0521 ], [ 177.0089, 65.0526 ], [ 177.012, 65.0557 ], [ 177.0026, 65.0568 ], [ 176.9979, 65.0604 ], [ 176.9812, 65.0625 ], [ 176.9771, 65.0646 ], [ 176.9604, 65.0667 ], [ 176.9563, 65.0687 ], [ 176.8854, 65.0687 ], [ 176.8729, 65.0646 ], [ 176.8479, 65.0625 ], [ 176.8146, 65.0521 ], [ 176.8083, 65.0521 ], [ 176.8042, 65.05 ], [ 176.7896, 65.05 ], [ 176.7771, 65.0458 ], [ 176.7646, 65.0437 ], [ 176.7563, 65.0396 ], [ 176.7505, 65.0391 ], [ 176.7287, 65.0276 ], [ 176.7214, 65.0266 ], [ 176.7203, 65.0234 ], [ 176.7104, 65.0208 ], [ 176.6958, 65.0208 ], [ 176.675, 65.0146 ], [ 176.6646, 65.0146 ], [ 176.6604, 65.0125 ], [ 176.6417, 65.0125 ], [ 176.6375, 65.0104 ], [ 176.6271, 65.0104 ], [ 176.6146, 65.0062 ], [ 176.6026, 65.0068 ], [ 176.5911, 65.0286 ], [ 176.5859, 65.0297 ], [ 176.5838, 65.0318 ], [ 176.587, 65.0349 ], [ 176.5776, 65.0359 ], [ 176.575, 65.0396 ], [ 176.5625, 65.0396 ], [ 176.5521, 65.0437 ], [ 176.5479, 65.0417 ], [ 176.5271, 65.0417 ], [ 176.5229, 65.0396 ], [ 176.5063, 65.0458 ], [ 176.5, 65.0458 ], [ 176.4974, 65.0495 ], [ 176.4922, 65.0505 ], [ 176.487, 65.0578 ], [ 176.4818, 65.0589 ], [ 176.4849, 65.062 ], [ 176.4714, 65.063 ], [ 176.4745, 65.0651 ], [ 176.4729, 65.0667 ], [ 176.4667, 65.0646 ], [ 176.4547, 65.0651 ], [ 176.4563, 65.0708 ], [ 176.4521, 65.075 ], [ 176.4417, 65.075 ], [ 176.4167, 65.0667 ], [ 176.4089, 65.0672 ], [ 176.412, 65.0703 ], [ 176.4068, 65.0714 ], [ 176.4104, 65.075 ], [ 176.4068, 65.0828 ], [ 176.4141, 65.0859 ], [ 176.413, 65.0911 ], [ 176.4245, 65.0964 ], [ 176.4234, 65.0995 ], [ 176.4307, 65.1005 ], [ 176.4318, 65.1036 ], [ 176.4495, 65.113 ], [ 176.4505, 65.1162 ], [ 176.4646, 65.1188 ], [ 176.4703, 65.1214 ], [ 176.4714, 65.1245 ], [ 176.4745, 65.1255 ], [ 176.4776, 65.1307 ], [ 176.4849, 65.1318 ], [ 176.4859, 65.1349 ], [ 176.4932, 65.138 ], [ 176.4964, 65.1412 ], [ 176.5057, 65.1422 ], [ 176.5047, 65.1453 ], [ 176.512, 65.1484 ], [ 176.513, 65.1516 ], [ 176.5328, 65.1568 ], [ 176.5495, 65.1651 ], [ 176.5484, 65.1682 ], [ 176.5682, 65.1776 ], [ 176.5672, 65.1807 ], [ 176.5703, 65.1818 ], [ 176.5693, 65.1849 ], [ 176.5734, 65.187 ], [ 176.5917, 65.1875 ], [ 176.5964, 65.1818 ], [ 176.6042, 65.1792 ], [ 176.6187, 65.1792 ], [ 176.6229, 65.1813 ], [ 176.6479, 65.1813 ], [ 176.6578, 65.1839 ], [ 176.6563, 65.1854 ], [ 176.6313, 65.1854 ], [ 176.6271, 65.1875 ], [ 176.6146, 65.1875 ], [ 176.6104, 65.1896 ], [ 176.6042, 65.1875 ], [ 176.6, 65.1917 ], [ 176.5771, 65.1937 ], [ 176.563, 65.1891 ], [ 176.5641, 65.1859 ], [ 176.5609, 65.1849 ], [ 176.5599, 65.1818 ], [ 176.5484, 65.1766 ], [ 176.5453, 65.1734 ], [ 176.5359, 65.1724 ], [ 176.5271, 65.1667 ], [ 176.5151, 65.1661 ], [ 176.5141, 65.163 ], [ 176.5036, 65.1568 ], [ 176.4943, 65.1557 ], [ 176.4912, 65.1526 ], [ 176.4839, 65.1495 ], [ 176.4828, 65.1464 ], [ 176.4755, 65.1432 ], [ 176.4724, 65.138 ], [ 176.4568, 65.1307 ], [ 176.4536, 65.1276 ], [ 176.4375, 65.125 ], [ 176.4292, 65.1208 ], [ 176.4208, 65.1208 ], [ 176.4109, 65.1162 ], [ 176.4203, 65.112 ], [ 176.4187, 65.1104 ], [ 176.413, 65.1099 ], [ 176.4182, 65.1078 ], [ 176.4151, 65.1057 ], [ 176.4162, 65.1026 ], [ 176.4068, 65.1016 ], [ 176.4036, 65.0984 ], [ 176.3963, 65.0953 ], [ 176.3953, 65.0922 ], [ 176.3901, 65.0891 ], [ 176.3917, 65.0833 ], [ 176.388, 65.0807 ], [ 176.3896, 65.0771 ], [ 176.3859, 65.0745 ], [ 176.3875, 65.0708 ], [ 176.3839, 65.0672 ], [ 176.3891, 65.0661 ], [ 176.3859, 65.063 ], [ 176.3896, 65.0583 ], [ 176.4125, 65.0583 ], [ 176.4203, 65.0557 ], [ 176.425, 65.0458 ], [ 176.4214, 65.0432 ], [ 176.4224, 65.038 ], [ 176.4182, 65.0359 ], [ 176.4083, 65.0354 ], [ 176.4042, 65.0333 ], [ 176.3833, 65.0333 ], [ 176.3583, 65.025 ], [ 176.3521, 65.0292 ], [ 176.3375, 65.0292 ], [ 176.3229, 65.025 ], [ 176.3125, 65.025 ], [ 176.3021, 65.0292 ], [ 176.2917, 65.0292 ], [ 176.2734, 65.0339 ], [ 176.2682, 65.0432 ], [ 176.2583, 65.0437 ], [ 176.2542, 65.0458 ], [ 176.2437, 65.0458 ], [ 176.2396, 65.0437 ], [ 176.2354, 65.0437 ], [ 176.2312, 65.0458 ], [ 176.2229, 65.0458 ], [ 176.2188, 65.0479 ], [ 176.2104, 65.0437 ], [ 176.1812, 65.0437 ], [ 176.1771, 65.0417 ], [ 176.1667, 65.0458 ], [ 176.1458, 65.0458 ], [ 176.1443, 65.0453 ], [ 176.1443, 65.0422 ], [ 176.1625, 65.0417 ], [ 176.1667, 65.0396 ], [ 176.2021, 65.0396 ], [ 176.2063, 65.0417 ], [ 176.2417, 65.0417 ], [ 176.2458, 65.0396 ], [ 176.2583, 65.0396 ], [ 176.263, 65.0339 ], [ 176.2682, 65.0328 ], [ 176.2693, 65.0297 ], [ 176.2729, 65.0271 ], [ 176.3, 65.0271 ], [ 176.3146, 65.0208 ], [ 176.325, 65.0208 ], [ 176.3292, 65.0229 ], [ 176.3354, 65.0229 ], [ 176.3391, 65.0203 ], [ 176.3422, 65.013 ], [ 176.35, 65.0125 ], [ 176.3542, 65.0104 ], [ 176.3813, 65.0104 ], [ 176.3932, 65.0172 ], [ 176.3943, 65.0203 ], [ 176.3974, 65.0214 ], [ 176.3963, 65.0245 ], [ 176.4047, 65.0286 ], [ 176.4146, 65.0313 ], [ 176.425, 65.0313 ], [ 176.4375, 65.0354 ], [ 176.4479, 65.0354 ], [ 176.4521, 65.0333 ], [ 176.4599, 65.0328 ], [ 176.463, 65.0297 ], [ 176.4708, 65.0271 ], [ 176.4828, 65.0266 ], [ 176.4797, 65.0245 ], [ 176.4807, 65.0214 ], [ 176.4776, 65.0203 ], [ 176.4787, 65.0151 ], [ 176.4729, 65.0146 ], [ 176.4714, 65.013 ], [ 176.4807, 65.012 ], [ 176.4807, 65.0089 ], [ 176.4776, 65.0068 ], [ 176.4828, 65.0057 ], [ 176.4875, 65.0021 ], [ 176.4953, 65.0016 ], [ 176.4984, 64.9964 ], [ 176.5036, 64.9953 ], [ 176.5047, 64.9922 ], [ 176.5161, 64.9891 ], [ 176.5193, 64.9838 ], [ 176.5271, 64.9833 ], [ 176.5313, 64.9812 ], [ 176.5396, 64.9812 ], [ 176.5437, 64.9792 ], [ 176.5542, 64.9792 ], [ 176.5583, 64.9812 ], [ 176.5667, 64.9812 ], [ 176.5708, 64.9833 ], [ 176.5875, 64.9833 ], [ 176.6083, 64.9771 ], [ 176.6833, 64.9771 ], [ 176.6896, 64.9812 ], [ 176.7104, 64.9812 ], [ 176.7146, 64.9833 ], [ 176.7292, 64.9833 ], [ 176.7417, 64.9875 ], [ 176.7563, 64.9875 ], [ 176.7604, 64.9896 ], [ 176.7833, 64.9917 ], [ 176.8062, 64.9979 ], [ 176.825, 64.9979 ], [ 176.8292, 65.0 ], [ 176.8438, 65.0 ], [ 176.8562, 65.0042 ], [ 176.8682, 65.0047 ], [ 176.8807, 65.0109 ], [ 176.8797, 65.0162 ], [ 176.8854, 65.0167 ], [ 176.8922, 65.0203 ], [ 176.9021, 65.0229 ], [ 176.9167, 65.0229 ], [ 176.9208, 65.025 ], [ 176.925, 65.0229 ], [ 176.9312, 65.025 ], [ 176.9354, 65.0229 ], [ 176.9417, 65.025 ], [ 176.9458, 65.0229 ], [ 176.95, 65.025 ], [ 176.975, 65.025 ], [ 176.9812, 65.0229 ], [ 176.987, 65.0255 ], [ 176.9859, 65.0286 ], [ 176.9953, 65.0297 ], [ 176.9984, 65.0328 ], [ 177.0063, 65.0354 ], [ 177.0078, 65.0349 ], [ 177.0224, 65.0036 ], [ 177.0193, 65.0016 ], [ 177.0193, 64.9984 ], [ 177.0245, 64.9974 ], [ 177.0297, 64.9901 ], [ 177.0349, 64.987 ], [ 177.037, 64.9818 ], [ 177.0339, 64.9797 ], [ 177.05, 64.9792 ], [ 177.0542, 64.9812 ], [ 177.0646, 64.9812 ], [ 177.0688, 64.9833 ], [ 177.0786, 64.9786 ], [ 177.0838, 64.9672 ], [ 177.1016, 64.962 ], [ 177.1026, 64.9589 ], [ 177.1104, 64.9563 ], [ 177.1313, 64.9563 ], [ 177.1479, 64.95 ], [ 177.1542, 64.95 ], [ 177.1583, 64.9479 ], [ 177.1708, 64.9479 ], [ 177.1724, 64.9464 ], [ 177.1693, 64.9453 ], [ 177.1708, 64.9437 ], [ 177.1812, 64.9437 ], [ 177.1859, 64.9401 ], [ 177.1938, 64.9375 ], [ 177.2057, 64.937 ], [ 177.212, 64.9328 ], [ 177.2104, 64.9292 ], [ 177.2109, 64.9255 ], [ 177.2224, 64.9224 ], [ 177.2151, 64.9172 ], [ 177.2417, 64.9083 ], [ 177.2667, 64.9083 ], [ 177.2708, 64.9104 ], [ 177.275, 64.9104 ], [ 177.2828, 64.9078 ], [ 177.2813, 64.9063 ], [ 177.2755, 64.9057 ], [ 177.2766, 64.9005 ], [ 177.2693, 64.8974 ], [ 177.2729, 64.8896 ], [ 177.2693, 64.887 ], [ 177.2703, 64.8839 ], [ 177.2609, 64.8828 ], [ 177.2661, 64.8807 ], [ 177.263, 64.8786 ], [ 177.2641, 64.8755 ], [ 177.2609, 64.8745 ], [ 177.2609, 64.8714 ], [ 177.263, 64.8693 ], [ 177.2682, 64.8682 ], [ 177.2713, 64.863 ], [ 177.2745, 64.862 ], [ 177.2724, 64.8588 ], [ 177.2651, 64.8557 ], [ 177.2661, 64.8505 ], [ 177.2589, 64.8474 ], [ 177.2589, 64.8443 ], [ 177.2641, 64.8432 ], [ 177.2661, 64.8411 ], [ 177.2661, 64.838 ], [ 177.263, 64.8359 ], [ 177.2682, 64.8349 ], [ 177.2745, 64.8307 ], [ 177.2734, 64.8276 ], [ 177.2854, 64.8229 ], [ 177.3125, 64.8229 ], [ 177.3203, 64.8203 ], [ 177.3214, 64.8172 ], [ 177.3292, 64.8146 ], [ 177.337, 64.8141 ], [ 177.3432, 64.8099 ], [ 177.3401, 64.8078 ], [ 177.3411, 64.8047 ], [ 177.3354, 64.8021 ], [ 177.3297, 64.8016 ], [ 177.3286, 64.7984 ], [ 177.3214, 64.7974 ], [ 177.3224, 64.7943 ], [ 177.3167, 64.7917 ], [ 177.3104, 64.7937 ], [ 177.2771, 64.7937 ], [ 177.2688, 64.7896 ], [ 177.2583, 64.7896 ], [ 177.25, 64.7854 ], [ 177.2359, 64.7828 ], [ 177.2349, 64.7797 ], [ 177.225, 64.775 ], [ 177.1979, 64.7708 ], [ 177.1771, 64.7646 ], [ 177.1693, 64.7651 ], [ 177.1604, 64.7708 ], [ 177.1354, 64.7708 ], [ 177.1313, 64.7688 ], [ 177.1208, 64.7688 ], [ 177.1167, 64.7667 ], [ 177.1125, 64.7667 ], [ 177.1042, 64.7625 ], [ 177.0984, 64.762 ], [ 177.0891, 64.7568 ], [ 177.0792, 64.7542 ], [ 177.0755, 64.7568 ], [ 177.0703, 64.7662 ], [ 177.0547, 64.7714 ], [ 177.05, 64.775 ], [ 177.0422, 64.7755 ], [ 177.0396, 64.7792 ], [ 177.0333, 64.7792 ], [ 177.0292, 64.7813 ], [ 176.9937, 64.7813 ], [ 176.9896, 64.7792 ], [ 176.9714, 64.7797 ], [ 176.9703, 64.7849 ], [ 176.9682, 64.787 ], [ 176.963, 64.788 ], [ 176.9609, 64.7922 ], [ 176.9682, 64.7963 ], [ 176.9667, 64.8 ], [ 176.9672, 64.8037 ], [ 176.9693, 64.8078 ], [ 176.9724, 64.8089 ], [ 176.9672, 64.8109 ], [ 176.963, 64.8172 ], [ 176.963, 64.8203 ], [ 176.9661, 64.8224 ], [ 176.9521, 64.8229 ], [ 176.9495, 64.8266 ], [ 176.9417, 64.8271 ], [ 176.9214, 64.8339 ], [ 176.9187, 64.8375 ], [ 176.9083, 64.8375 ], [ 176.9042, 64.8354 ], [ 176.8813, 64.8333 ], [ 176.8417, 64.8229 ], [ 176.825, 64.8229 ], [ 176.8234, 64.8245 ], [ 176.8266, 64.8266 ], [ 176.8214, 64.8276 ], [ 176.8161, 64.837 ], [ 176.8104, 64.8354 ], [ 176.7917, 64.8438 ], [ 176.7854, 64.8438 ], [ 176.7813, 64.8458 ], [ 176.7604, 64.8458 ], [ 176.7563, 64.8479 ], [ 176.7417, 64.8479 ], [ 176.7375, 64.8458 ], [ 176.7271, 64.8458 ], [ 176.7229, 64.8438 ], [ 176.7167, 64.8458 ], [ 176.7125, 64.8438 ], [ 176.7047, 64.8464 ], [ 176.7021, 64.85 ], [ 176.6938, 64.85 ], [ 176.6854, 64.8542 ], [ 176.6792, 64.8542 ], [ 176.6667, 64.85 ], [ 176.6589, 64.8495 ], [ 176.6599, 64.8464 ], [ 176.6526, 64.8453 ], [ 176.6318, 64.8349 ], [ 176.6245, 64.8297 ], [ 176.6083, 64.8292 ], [ 176.5932, 64.8214 ], [ 176.5859, 64.8203 ], [ 176.587, 64.8172 ], [ 176.5714, 64.8099 ], [ 176.5724, 64.8068 ], [ 176.563, 64.8057 ], [ 176.5599, 64.8026 ], [ 176.55, 64.7979 ], [ 176.5458, 64.8 ], [ 176.5396, 64.8 ], [ 176.5354, 64.7979 ], [ 176.5292, 64.8 ], [ 176.525, 64.7979 ], [ 176.5188, 64.7979 ], [ 176.5146, 64.8 ], [ 176.4812, 64.8 ], [ 176.4771, 64.7979 ], [ 176.4667, 64.7979 ], [ 176.4625, 64.7958 ], [ 176.4521, 64.7958 ], [ 176.4505, 64.7963 ], [ 176.4505, 64.7995 ], [ 176.462, 64.8089 ], [ 176.4609, 64.8161 ], [ 176.4682, 64.8203 ], [ 176.462, 64.8328 ], [ 176.4599, 64.8349 ], [ 176.4547, 64.8359 ], [ 176.4536, 64.8391 ], [ 176.4484, 64.8401 ], [ 176.4453, 64.8453 ], [ 176.4375, 64.8458 ], [ 176.4312, 64.85 ], [ 176.425, 64.85 ], [ 176.4146, 64.8542 ], [ 176.4083, 64.8542 ], [ 176.3917, 64.8604 ], [ 176.3708, 64.8604 ], [ 176.3667, 64.8625 ], [ 176.3604, 64.8625 ], [ 176.3557, 64.8662 ], [ 176.3438, 64.8708 ], [ 176.325, 64.8729 ], [ 176.3146, 64.8771 ], [ 176.3042, 64.8771 ], [ 176.2979, 64.8812 ], [ 176.2875, 64.8812 ], [ 176.2771, 64.8854 ], [ 176.2458, 64.8854 ], [ 176.2443, 64.887 ], [ 176.2474, 64.8891 ], [ 176.2404, 64.8917 ], [ 176.2326, 64.8604 ], [ 176.2391, 64.8557 ], [ 176.2375, 64.85 ], [ 176.2417, 64.8417 ], [ 176.238, 64.838 ], [ 176.2464, 64.8214 ], [ 176.2578, 64.8203 ], [ 176.2547, 64.8172 ], [ 176.2667, 64.8125 ], [ 176.275, 64.8125 ], [ 176.2792, 64.8104 ], [ 176.287, 64.8099 ], [ 176.2839, 64.8068 ], [ 176.2891, 64.8037 ], [ 176.2875, 64.8 ], [ 176.2901, 64.7943 ], [ 176.2974, 64.7891 ], [ 176.2943, 64.7859 ], [ 176.3016, 64.7807 ], [ 176.2984, 64.7786 ], [ 176.3005, 64.7734 ], [ 176.3057, 64.7724 ], [ 176.3089, 64.7672 ], [ 176.3141, 64.7662 ], [ 176.3109, 64.763 ], [ 176.3286, 64.7578 ], [ 176.3286, 64.7547 ], [ 176.3255, 64.7526 ], [ 176.337, 64.7495 ], [ 176.338, 64.7464 ], [ 176.3432, 64.7453 ], [ 176.3464, 64.7401 ], [ 176.3516, 64.7391 ], [ 176.3547, 64.7338 ], [ 176.3599, 64.7328 ], [ 176.362, 64.7307 ], [ 176.3589, 64.7276 ], [ 176.3641, 64.7266 ], [ 176.3651, 64.7234 ], [ 176.3766, 64.7203 ], [ 176.3776, 64.7172 ], [ 176.3891, 64.7141 ], [ 176.3901, 64.7109 ], [ 176.3963, 64.7089 ], [ 176.412, 64.7078 ], [ 176.4089, 64.7047 ], [ 176.4141, 64.7037 ], [ 176.4151, 64.7005 ], [ 176.4203, 64.6995 ], [ 176.4234, 64.6964 ], [ 176.4287, 64.6953 ], [ 176.4255, 64.6922 ], [ 176.4307, 64.6911 ], [ 176.4318, 64.688 ], [ 176.437, 64.6859 ], [ 176.4271, 64.6813 ], [ 176.4172, 64.6807 ], [ 176.4187, 64.6792 ], [ 176.4266, 64.6787 ], [ 176.4234, 64.6766 ], [ 176.4245, 64.6734 ], [ 176.4172, 64.6682 ], [ 176.4182, 64.6651 ], [ 176.4125, 64.6625 ], [ 176.4042, 64.6625 ], [ 176.4, 64.6604 ], [ 176.3922, 64.6599 ], [ 176.3859, 64.6557 ], [ 176.3849, 64.6526 ], [ 176.3776, 64.6516 ], [ 176.3786, 64.6484 ], [ 176.3714, 64.6474 ], [ 176.3604, 64.6417 ], [ 176.3562, 64.6417 ], [ 176.3521, 64.6396 ], [ 176.3375, 64.6396 ], [ 176.3333, 64.6375 ], [ 176.3292, 64.6396 ], [ 176.3229, 64.6375 ], [ 176.3188, 64.6396 ], [ 176.3042, 64.6396 ], [ 176.3, 64.6375 ], [ 176.2667, 64.6354 ], [ 176.2568, 64.6328 ], [ 176.25, 64.6292 ], [ 176.2443, 64.6286 ], [ 176.2432, 64.6255 ], [ 176.237, 64.6214 ], [ 176.225, 64.6208 ], [ 176.2151, 64.6182 ], [ 176.2161, 64.6151 ], [ 176.2083, 64.6146 ], [ 176.2005, 64.6099 ], [ 176.1995, 64.6068 ], [ 176.1901, 64.6016 ], [ 176.187, 64.5943 ], [ 176.1797, 64.5911 ], [ 176.1807, 64.588 ], [ 176.1589, 64.5766 ], [ 176.1599, 64.5734 ], [ 176.1568, 64.5724 ], [ 176.1557, 64.5693 ], [ 176.1526, 64.5682 ], [ 176.1536, 64.5651 ], [ 176.1505, 64.5641 ], [ 176.1516, 64.5609 ], [ 176.1458, 64.5583 ], [ 176.1354, 64.5583 ], [ 176.1313, 64.5563 ], [ 176.1271, 64.5563 ], [ 176.1151, 64.5495 ], [ 176.1203, 64.5463 ], [ 176.1172, 64.5453 ], [ 176.1141, 64.5401 ], [ 176.1109, 64.5391 ], [ 176.1078, 64.5339 ], [ 176.1005, 64.5307 ], [ 176.0995, 64.5276 ], [ 176.0943, 64.5245 ], [ 176.0911, 64.5172 ], [ 176.0755, 64.512 ], [ 176.0766, 64.5068 ], [ 176.0714, 64.5036 ], [ 176.0703, 64.5005 ], [ 176.0646, 64.4958 ], [ 176.0688, 64.4917 ], [ 176.0745, 64.4943 ], [ 176.0734, 64.4974 ], [ 176.0891, 64.5047 ], [ 176.088, 64.5078 ], [ 176.0953, 64.5109 ], [ 176.0943, 64.5141 ], [ 176.0974, 64.5151 ], [ 176.0964, 64.5182 ], [ 176.1042, 64.5188 ], [ 176.1172, 64.5089 ], [ 176.1266, 64.5078 ], [ 176.1229, 64.5042 ], [ 176.1266, 64.4974 ], [ 176.1234, 64.4943 ], [ 176.1286, 64.4838 ], [ 176.1229, 64.4812 ], [ 176.1172, 64.4807 ], [ 176.1182, 64.4755 ], [ 176.1109, 64.4745 ], [ 176.1016, 64.4693 ], [ 176.0943, 64.4682 ], [ 176.0875, 64.4646 ], [ 176.0818, 64.4641 ], [ 176.0828, 64.4609 ], [ 176.0792, 64.4604 ], [ 176.0672, 64.4651 ], [ 176.0641, 64.4682 ], [ 176.0589, 64.4693 ], [ 176.0625, 64.4729 ], [ 176.0609, 64.4766 ], [ 176.0641, 64.4776 ], [ 176.063, 64.4828 ], [ 176.0682, 64.4859 ], [ 176.0682, 64.4891 ], [ 176.0651, 64.4891 ], [ 176.0568, 64.4849 ], [ 176.0578, 64.4818 ], [ 176.0547, 64.4807 ], [ 176.0537, 64.4776 ], [ 176.0505, 64.4755 ], [ 176.0537, 64.4724 ], [ 176.0568, 64.4651 ], [ 176.062, 64.4641 ], [ 176.0661, 64.4578 ], [ 176.0661, 64.4547 ], [ 176.063, 64.4537 ], [ 176.0641, 64.4505 ], [ 176.0583, 64.45 ], [ 176.0526, 64.4474 ], [ 176.0495, 64.4422 ], [ 176.0276, 64.4307 ], [ 176.025, 64.425 ], [ 176.0266, 64.4193 ], [ 176.0234, 64.4182 ], [ 176.0245, 64.4151 ], [ 176.0099, 64.4068 ], [ 176.0042, 64.4063 ], [ 175.9984, 64.4036 ], [ 175.9953, 64.3964 ], [ 175.9922, 64.3953 ], [ 175.9953, 64.3912 ], [ 175.9922, 64.388 ], [ 176.0016, 64.387 ], [ 176.0005, 64.3797 ], [ 176.0026, 64.3776 ], [ 176.0078, 64.3766 ], [ 176.0104, 64.3729 ], [ 176.0224, 64.3734 ], [ 176.0208, 64.375 ], [ 176.013, 64.3755 ], [ 176.0109, 64.3786 ], [ 176.0229, 64.3792 ], [ 176.0276, 64.3755 ], [ 176.0354, 64.3729 ], [ 176.0417, 64.3729 ], [ 176.0458, 64.3708 ], [ 176.0562, 64.3708 ], [ 176.0604, 64.3688 ], [ 176.0813, 64.3688 ], [ 176.0917, 64.3646 ], [ 176.1021, 64.3646 ], [ 176.1104, 64.3688 ], [ 176.1438, 64.3688 ], [ 176.1542, 64.3646 ], [ 176.1662, 64.3651 ], [ 176.1583, 64.3688 ], [ 176.1521, 64.3688 ], [ 176.1479, 64.3708 ], [ 176.1375, 64.3708 ], [ 176.1333, 64.3729 ], [ 176.1167, 64.3729 ], [ 176.1125, 64.3708 ], [ 176.1, 64.3708 ], [ 176.0938, 64.3688 ], [ 176.0896, 64.3708 ], [ 176.0729, 64.3729 ], [ 176.0625, 64.3771 ], [ 176.0547, 64.3776 ], [ 176.0578, 64.3807 ], [ 176.0526, 64.3818 ], [ 176.0516, 64.3849 ], [ 176.0479, 64.3875 ], [ 176.0417, 64.3875 ], [ 176.038, 64.3849 ], [ 176.0432, 64.3797 ], [ 176.0313, 64.3792 ], [ 176.0068, 64.388 ], [ 176.0083, 64.3917 ], [ 176.0063, 64.3958 ], [ 176.0099, 64.3984 ], [ 176.0089, 64.4016 ], [ 176.0161, 64.4047 ], [ 176.0193, 64.4078 ], [ 176.025, 64.4104 ], [ 176.0313, 64.4104 ], [ 176.0391, 64.3995 ], [ 176.0359, 64.3964 ], [ 176.0412, 64.3953 ], [ 176.0437, 64.3917 ], [ 176.0516, 64.3922 ], [ 176.0495, 64.3953 ], [ 176.0443, 64.3964 ], [ 176.0474, 64.3995 ], [ 176.0422, 64.4005 ], [ 176.0401, 64.4047 ], [ 176.0432, 64.4068 ], [ 176.0432, 64.4099 ], [ 176.038, 64.4109 ], [ 176.0396, 64.4146 ], [ 176.038, 64.4182 ], [ 176.0417, 64.4208 ], [ 176.0401, 64.4245 ], [ 176.0432, 64.4255 ], [ 176.0422, 64.4287 ], [ 176.0537, 64.4339 ], [ 176.0526, 64.437 ], [ 176.0609, 64.4411 ], [ 176.0682, 64.4422 ], [ 176.0755, 64.4474 ], [ 176.0833, 64.4479 ], [ 176.0922, 64.4537 ], [ 176.0995, 64.4547 ], [ 176.1187, 64.4625 ], [ 176.1245, 64.463 ], [ 176.1328, 64.4672 ], [ 176.1359, 64.4724 ], [ 176.1495, 64.4797 ], [ 176.1521, 64.4854 ], [ 176.1474, 64.4953 ], [ 176.1359, 64.4984 ], [ 176.1391, 64.5005 ], [ 176.138, 64.5036 ], [ 176.1453, 64.5078 ], [ 176.1401, 64.5089 ], [ 176.138, 64.513 ], [ 176.1359, 64.5182 ], [ 176.1391, 64.5193 ], [ 176.138, 64.5224 ], [ 176.1411, 64.5245 ], [ 176.1359, 64.5255 ], [ 176.1339, 64.5307 ], [ 176.1516, 64.5401 ], [ 176.1526, 64.5432 ], [ 176.1557, 64.5443 ], [ 176.1547, 64.5474 ], [ 176.1766, 64.5589 ], [ 176.1755, 64.562 ], [ 176.187, 64.5672 ], [ 176.1859, 64.5703 ], [ 176.2161, 64.5859 ], [ 176.2151, 64.5891 ], [ 176.2208, 64.5896 ], [ 176.2266, 64.5922 ], [ 176.2297, 64.5995 ], [ 176.2458, 64.6083 ], [ 176.2563, 64.6083 ], [ 176.2625, 64.6042 ], [ 176.2688, 64.6042 ], [ 176.2766, 64.6016 ], [ 176.2734, 64.5995 ], [ 176.275, 64.5979 ], [ 176.2854, 64.5979 ], [ 176.2896, 64.5958 ], [ 176.2979, 64.5958 ], [ 176.3021, 64.5938 ], [ 176.3375, 64.5938 ], [ 176.3417, 64.5917 ], [ 176.3458, 64.5938 ], [ 176.3562, 64.5938 ], [ 176.3745, 64.5984 ], [ 176.3813, 64.6021 ], [ 176.387, 64.6026 ], [ 176.3979, 64.6083 ], [ 176.4271, 64.6146 ], [ 176.4411, 64.6214 ], [ 176.4396, 64.625 ], [ 176.4422, 64.6286 ], [ 176.4516, 64.6297 ], [ 176.4505, 64.6328 ], [ 176.4536, 64.6339 ], [ 176.4526, 64.637 ], [ 176.4599, 64.6401 ], [ 176.4599, 64.6432 ], [ 176.4547, 64.6453 ], [ 176.462, 64.6464 ], [ 176.4609, 64.6495 ], [ 176.4682, 64.6547 ], [ 176.4667, 64.6583 ], [ 176.4693, 64.6641 ], [ 176.4734, 64.6661 ], [ 176.4833, 64.6667 ], [ 176.4875, 64.6687 ], [ 176.4937, 64.6687 ], [ 176.4979, 64.6667 ], [ 176.5021, 64.6687 ], [ 176.5375, 64.6687 ], [ 176.5417, 64.6667 ], [ 176.5599, 64.6641 ], [ 176.563, 64.6609 ], [ 176.575, 64.6563 ], [ 176.5938, 64.6542 ], [ 176.5964, 64.6505 ], [ 176.6104, 64.6458 ], [ 176.6167, 64.6458 ], [ 176.6333, 64.6396 ], [ 176.6396, 64.6396 ], [ 176.6422, 64.6359 ], [ 176.6662, 64.6286 ], [ 176.6672, 64.6255 ], [ 176.6766, 64.6245 ], [ 176.6797, 64.6214 ], [ 176.6849, 64.6203 ], [ 176.6859, 64.6172 ], [ 176.6953, 64.6162 ], [ 176.6943, 64.6109 ], [ 176.6995, 64.6099 ], [ 176.7026, 64.6026 ], [ 176.7078, 64.6016 ], [ 176.7109, 64.5984 ], [ 176.7203, 64.5953 ], [ 176.7234, 64.5922 ], [ 176.7287, 64.5911 ], [ 176.7307, 64.5859 ], [ 176.7276, 64.5839 ], [ 176.7328, 64.5828 ], [ 176.738, 64.5714 ], [ 176.7432, 64.5703 ], [ 176.7453, 64.5682 ], [ 176.7422, 64.5651 ], [ 176.7474, 64.5641 ], [ 176.7505, 64.5568 ], [ 176.7557, 64.5557 ], [ 176.7526, 64.5526 ], [ 176.7578, 64.5516 ], [ 176.7599, 64.5474 ], [ 176.7568, 64.5453 ], [ 176.7589, 64.5401 ], [ 176.7667, 64.5375 ], [ 176.7708, 64.5396 ], [ 176.7813, 64.5396 ], [ 176.7854, 64.5417 ], [ 176.7896, 64.5417 ], [ 176.8, 64.5479 ], [ 176.8062, 64.5479 ], [ 176.8245, 64.5526 ], [ 176.8391, 64.5609 ], [ 176.8401, 64.5641 ], [ 176.8464, 64.5682 ], [ 176.8641, 64.5714 ], [ 176.863, 64.5745 ], [ 176.8667, 64.5771 ], [ 176.863, 64.5839 ], [ 176.8667, 64.5875 ], [ 176.8651, 64.5932 ], [ 176.8786, 64.6005 ], [ 176.8776, 64.6036 ], [ 176.887, 64.6047 ], [ 176.8901, 64.6078 ], [ 176.9099, 64.6172 ], [ 176.9089, 64.6203 ], [ 176.913, 64.6224 ], [ 176.9287, 64.6255 ], [ 176.9297, 64.6286 ], [ 176.9359, 64.6328 ], [ 176.9526, 64.6412 ], [ 176.9599, 64.6422 ], [ 176.9708, 64.6479 ], [ 176.9766, 64.6484 ], [ 176.9964, 64.6599 ], [ 177.0036, 64.6609 ], [ 177.0047, 64.6641 ], [ 177.012, 64.6672 ], [ 177.0151, 64.6703 ], [ 177.0391, 64.6818 ], [ 177.0453, 64.6859 ], [ 177.0464, 64.6891 ], [ 177.0547, 64.6932 ], [ 177.062, 64.6943 ], [ 177.0693, 64.6995 ], [ 177.0786, 64.7005 ], [ 177.0859, 64.7057 ], [ 177.0896, 64.7063 ], [ 177.0938, 64.7042 ], [ 177.1104, 64.7042 ], [ 177.1172, 64.7005 ], [ 177.125, 64.7 ], [ 177.1292, 64.6979 ], [ 177.1354, 64.7 ], [ 177.15, 64.7 ], [ 177.1542, 64.7021 ], [ 177.1687, 64.6979 ], [ 177.1896, 64.6979 ], [ 177.2021, 64.7021 ], [ 177.2167, 64.7021 ], [ 177.2208, 64.7042 ], [ 177.2312, 64.7042 ], [ 177.2401, 64.7099 ], [ 177.2474, 64.7109 ], [ 177.2583, 64.7167 ], [ 177.2641, 64.7172 ], [ 177.2901, 64.7307 ], [ 177.2974, 64.7318 ], [ 177.3042, 64.7354 ], [ 177.3125, 64.7354 ], [ 177.3167, 64.7375 ], [ 177.3208, 64.7375 ], [ 177.3292, 64.7417 ], [ 177.337, 64.7422 ], [ 177.3359, 64.7453 ], [ 177.3458, 64.75 ], [ 177.35, 64.75 ], [ 177.3542, 64.7521 ], [ 177.3646, 64.7521 ], [ 177.3792, 64.7479 ], [ 177.3917, 64.7479 ], [ 177.4021, 64.7438 ], [ 177.4125, 64.7438 ], [ 177.4187, 64.7396 ], [ 177.4333, 64.7396 ], [ 177.4396, 64.7375 ], [ 177.4438, 64.7396 ], [ 177.4771, 64.7396 ], [ 177.4812, 64.7417 ], [ 177.4854, 64.7396 ], [ 177.5083, 64.7396 ], [ 177.5229, 64.7354 ], [ 177.5333, 64.7354 ], [ 177.5349, 64.7338 ], [ 177.5318, 64.7318 ], [ 177.537, 64.7307 ], [ 177.5328, 64.7276 ], [ 177.5255, 64.7266 ], [ 177.5286, 64.7151 ], [ 177.5213, 64.7141 ], [ 177.5224, 64.7109 ], [ 177.5193, 64.7099 ], [ 177.5182, 64.7068 ], [ 177.5151, 64.7057 ], [ 177.5161, 64.7026 ], [ 177.5089, 64.6974 ], [ 177.5099, 64.6943 ], [ 177.5068, 64.6932 ], [ 177.5078, 64.6901 ], [ 177.5005, 64.687 ], [ 177.4995, 64.6839 ], [ 177.4964, 64.6828 ], [ 177.4974, 64.6797 ], [ 177.4901, 64.6766 ], [ 177.4912, 64.6734 ], [ 177.4875, 64.6708 ], [ 177.4891, 64.6672 ], [ 177.4818, 64.662 ], [ 177.487, 64.6589 ], [ 177.4797, 64.6557 ], [ 177.4812, 64.6521 ], [ 177.4797, 64.6484 ], [ 177.4828, 64.6474 ], [ 177.4797, 64.6443 ], [ 177.4818, 64.6422 ], [ 177.487, 64.6412 ], [ 177.4839, 64.638 ], [ 177.4891, 64.637 ], [ 177.4901, 64.6339 ], [ 177.5016, 64.6297 ], [ 177.4958, 64.6292 ], [ 177.4901, 64.6266 ], [ 177.4891, 64.6234 ], [ 177.4859, 64.6224 ], [ 177.487, 64.6172 ], [ 177.4771, 64.6167 ], [ 177.4703, 64.613 ], [ 177.463, 64.612 ], [ 177.462, 64.6088 ], [ 177.4547, 64.6078 ], [ 177.4557, 64.6026 ], [ 177.4484, 64.5995 ], [ 177.4495, 64.5964 ], [ 177.4422, 64.5911 ], [ 177.4438, 64.5875 ], [ 177.4401, 64.5849 ], [ 177.4417, 64.5813 ], [ 177.4391, 64.5755 ], [ 177.4359, 64.5745 ], [ 177.437, 64.5714 ], [ 177.4339, 64.5703 ], [ 177.4328, 64.5672 ], [ 177.4276, 64.5641 ], [ 177.4307, 64.562 ], [ 177.4276, 64.5599 ], [ 177.4287, 64.5547 ], [ 177.4266, 64.5505 ], [ 177.4234, 64.5495 ], [ 177.425, 64.5458 ], [ 177.4214, 64.5432 ], [ 177.4224, 64.538 ], [ 177.4193, 64.537 ], [ 177.4203, 64.5318 ], [ 177.4151, 64.5286 ], [ 177.4167, 64.5229 ], [ 177.413, 64.5203 ], [ 177.4162, 64.513 ], [ 177.4125, 64.5104 ], [ 177.4167, 64.5021 ], [ 177.413, 64.4984 ], [ 177.4182, 64.4974 ], [ 177.4151, 64.4943 ], [ 177.4203, 64.4932 ], [ 177.4224, 64.4912 ], [ 177.4224, 64.488 ], [ 177.4193, 64.4859 ], [ 177.4245, 64.4849 ], [ 177.4172, 64.4797 ], [ 177.4224, 64.4786 ], [ 177.4318, 64.4714 ], [ 177.437, 64.4703 ], [ 177.4339, 64.4672 ], [ 177.4391, 64.4661 ], [ 177.4422, 64.4589 ], [ 177.4474, 64.4578 ], [ 177.4443, 64.4547 ], [ 177.4495, 64.4537 ], [ 177.4464, 64.4505 ], [ 177.4557, 64.4495 ], [ 177.4526, 64.4464 ], [ 177.4578, 64.4453 ], [ 177.4609, 64.438 ], [ 177.4661, 64.437 ], [ 177.463, 64.4339 ], [ 177.4682, 64.4328 ], [ 177.4745, 64.4287 ], [ 177.4734, 64.4234 ], [ 177.4807, 64.4182 ], [ 177.4859, 64.4089 ], [ 177.5016, 64.4036 ], [ 177.5047, 64.3984 ], [ 177.5229, 64.3958 ], [ 177.5276, 64.3922 ], [ 177.5328, 64.3912 ], [ 177.5339, 64.388 ], [ 177.5417, 64.3875 ], [ 177.5557, 64.3828 ], [ 177.5599, 64.3807 ], [ 177.5589, 64.3755 ], [ 177.5682, 64.3745 ], [ 177.5734, 64.3651 ], [ 177.5786, 64.3641 ], [ 177.5797, 64.3609 ], [ 177.5849, 64.3599 ], [ 177.5896, 64.3562 ], [ 177.5974, 64.3557 ], [ 177.6005, 64.3505 ], [ 177.6099, 64.3495 ], [ 177.6109, 64.3464 ], [ 177.6162, 64.3453 ], [ 177.6193, 64.3422 ], [ 177.6224, 64.3422 ], [ 177.6214, 64.3453 ], [ 177.625, 64.3458 ], [ 177.6292, 64.3417 ], [ 177.6354, 64.3438 ], [ 177.6396, 64.3396 ], [ 177.6521, 64.3396 ], [ 177.6536, 64.338 ], [ 177.6479, 64.3375 ], [ 177.6463, 64.3359 ], [ 177.6583, 64.3354 ], [ 177.6703, 64.3307 ], [ 177.6734, 64.3276 ], [ 177.6787, 64.3266 ], [ 177.6797, 64.3234 ], [ 177.6875, 64.3229 ], [ 177.7057, 64.3182 ], [ 177.7141, 64.312 ], [ 177.7151, 64.3089 ], [ 177.7203, 64.3078 ], [ 177.7234, 64.3026 ], [ 177.7328, 64.3016 ], [ 177.7391, 64.2995 ], [ 177.7401, 64.2963 ], [ 177.7453, 64.2953 ], [ 177.7422, 64.2922 ], [ 177.7474, 64.2911 ], [ 177.7505, 64.2859 ], [ 177.7599, 64.2849 ], [ 177.7693, 64.2776 ], [ 177.7745, 64.2766 ], [ 177.7755, 64.2734 ], [ 177.7807, 64.2724 ], [ 177.7839, 64.2672 ], [ 177.7917, 64.2667 ], [ 177.7995, 64.2641 ], [ 177.8005, 64.2609 ], [ 177.812, 64.2578 ], [ 177.813, 64.2547 ], [ 177.8182, 64.2536 ], [ 177.8193, 64.2505 ], [ 177.8245, 64.2495 ], [ 177.8318, 64.2443 ], [ 177.8432, 64.2412 ], [ 177.8443, 64.238 ], [ 177.8495, 64.237 ], [ 177.8526, 64.2338 ], [ 177.8646, 64.2333 ], [ 177.8693, 64.2297 ], [ 177.8771, 64.2292 ], [ 177.8854, 64.225 ], [ 177.8979, 64.225 ], [ 177.9021, 64.2229 ], [ 177.9083, 64.2229 ], [ 177.9125, 64.2188 ], [ 177.9187, 64.2208 ], [ 177.9229, 64.2188 ], [ 177.9292, 64.2188 ], [ 177.9396, 64.2146 ], [ 177.9474, 64.2141 ], [ 177.95, 64.2104 ], [ 177.9563, 64.2104 ], [ 177.9771, 64.2042 ], [ 177.9917, 64.2042 ], [ 178.0057, 64.2089 ], [ 178.0036, 64.2161 ], [ 177.9984, 64.2172 ], [ 178.0, 64.2188 ], [ 178.0146, 64.2188 ], [ 178.0188, 64.2167 ], [ 178.0313, 64.2167 ], [ 178.0354, 64.2146 ], [ 178.0396, 64.2167 ], [ 178.0437, 64.2146 ], [ 178.062, 64.212 ], [ 178.0641, 64.2099 ], [ 178.0609, 64.2078 ], [ 178.063, 64.2005 ], [ 178.0833, 64.1937 ], [ 178.0911, 64.1932 ], [ 178.0938, 64.1896 ], [ 178.1104, 64.1896 ], [ 178.1208, 64.1854 ], [ 178.1354, 64.1854 ], [ 178.1396, 64.1875 ], [ 178.1438, 64.1854 ], [ 178.1604, 64.1854 ], [ 178.1646, 64.1833 ], [ 178.2229, 64.1833 ], [ 178.2458, 64.175 ], [ 178.2474, 64.1766 ], [ 178.2453, 64.1787 ], [ 178.2401, 64.1797 ], [ 178.2391, 64.1849 ], [ 178.225, 64.1896 ], [ 178.2188, 64.1875 ], [ 178.2146, 64.1896 ], [ 178.1839, 64.1901 ], [ 178.1854, 64.1917 ], [ 178.2016, 64.1922 ], [ 178.1964, 64.1943 ], [ 178.1917, 64.2 ], [ 178.1818, 64.1974 ], [ 178.1807, 64.1922 ], [ 178.1792, 64.1917 ], [ 178.163, 64.1943 ], [ 178.1599, 64.1995 ], [ 178.1547, 64.2005 ], [ 178.1516, 64.2078 ], [ 178.1463, 64.2109 ], [ 178.1474, 64.2161 ], [ 178.1396, 64.2167 ], [ 178.1349, 64.2203 ], [ 178.1255, 64.2214 ], [ 178.1286, 64.2245 ], [ 178.1234, 64.2255 ], [ 178.1214, 64.2307 ], [ 178.1245, 64.2318 ], [ 178.1255, 64.2349 ], [ 178.1396, 64.2375 ], [ 178.1463, 64.2412 ], [ 178.1536, 64.2422 ], [ 178.1687, 64.25 ], [ 178.1979, 64.2562 ], [ 178.2063, 64.2604 ], [ 178.2167, 64.2604 ], [ 178.2432, 64.2672 ], [ 178.2474, 64.2693 ], [ 178.2464, 64.2724 ], [ 178.2557, 64.2734 ], [ 178.2589, 64.2766 ], [ 178.2713, 64.2828 ], [ 178.2786, 64.2839 ], [ 178.2818, 64.2891 ], [ 178.3161, 64.3068 ], [ 178.3172, 64.3099 ], [ 178.3266, 64.3151 ], [ 178.3276, 64.3182 ], [ 178.3349, 64.3214 ], [ 178.3307, 64.3287 ], [ 178.3255, 64.3297 ], [ 178.3328, 64.337 ], [ 178.3307, 64.3391 ], [ 178.3255, 64.3401 ], [ 178.3245, 64.3453 ], [ 178.3089, 64.3464 ], [ 178.312, 64.3495 ], [ 178.3068, 64.3505 ], [ 178.3057, 64.3536 ], [ 178.3005, 64.3547 ], [ 178.3042, 64.3583 ], [ 178.3005, 64.3651 ], [ 178.3037, 64.3682 ], [ 178.2984, 64.3693 ], [ 178.3016, 64.3724 ], [ 178.2964, 64.3745 ], [ 178.3021, 64.3771 ], [ 178.3057, 64.3766 ], [ 178.3068, 64.3714 ], [ 178.312, 64.3703 ], [ 178.3172, 64.363 ], [ 178.3224, 64.3599 ], [ 178.3276, 64.3464 ], [ 178.3391, 64.3432 ], [ 178.3411, 64.338 ], [ 178.338, 64.3359 ], [ 178.3432, 64.3349 ], [ 178.3401, 64.3318 ], [ 178.3495, 64.3287 ], [ 178.3547, 64.3214 ], [ 178.3599, 64.3203 ], [ 178.3568, 64.3182 ], [ 178.3568, 64.3151 ], [ 178.362, 64.3141 ], [ 178.363, 64.3109 ], [ 178.3682, 64.3099 ], [ 178.3703, 64.3078 ], [ 178.3646, 64.3042 ], [ 178.3443, 64.3037 ], [ 178.3443, 64.3005 ], [ 178.3495, 64.2995 ], [ 178.3464, 64.2963 ], [ 178.3542, 64.2958 ], [ 178.363, 64.3016 ], [ 178.3708, 64.3021 ], [ 178.3755, 64.2963 ], [ 178.3807, 64.2953 ], [ 178.3839, 64.2859 ], [ 178.3891, 64.2849 ], [ 178.3911, 64.2828 ], [ 178.388, 64.2807 ], [ 178.388, 64.2776 ], [ 178.3932, 64.2766 ], [ 178.3938, 64.275 ], [ 178.3917, 64.2729 ], [ 178.3797, 64.2745 ], [ 178.3776, 64.2724 ], [ 178.3755, 64.2651 ], [ 178.3875, 64.2604 ], [ 178.4042, 64.2604 ], [ 178.412, 64.2578 ], [ 178.413, 64.2526 ], [ 178.4151, 64.2505 ], [ 178.4203, 64.2495 ], [ 178.4234, 64.2401 ], [ 178.4287, 64.2391 ], [ 178.4333, 64.2312 ], [ 178.4328, 64.2297 ], [ 178.4255, 64.2286 ], [ 178.4255, 64.2255 ], [ 178.4287, 64.2203 ], [ 178.4255, 64.2182 ], [ 178.4255, 64.2151 ], [ 178.4333, 64.2146 ], [ 178.4375, 64.2167 ], [ 178.4417, 64.2146 ], [ 178.4495, 64.2141 ], [ 178.4547, 64.2047 ], [ 178.4599, 64.2037 ], [ 178.4599, 64.2005 ], [ 178.4568, 64.1984 ], [ 178.4682, 64.1953 ], [ 178.4755, 64.1797 ], [ 178.4807, 64.1787 ], [ 178.4818, 64.1755 ], [ 178.4891, 64.1703 ], [ 178.4818, 64.1672 ], [ 178.4912, 64.1661 ], [ 178.4922, 64.163 ], [ 178.4974, 64.162 ], [ 178.5005, 64.1568 ], [ 178.5057, 64.1557 ], [ 178.5068, 64.1505 ], [ 178.5089, 64.1484 ], [ 178.5141, 64.1474 ], [ 178.5109, 64.1443 ], [ 178.5161, 64.1432 ], [ 178.5172, 64.1401 ], [ 178.5224, 64.1391 ], [ 178.5255, 64.1318 ], [ 178.537, 64.1245 ], [ 178.5422, 64.1151 ], [ 178.5474, 64.1141 ], [ 178.5443, 64.1109 ], [ 178.5495, 64.1099 ], [ 178.5505, 64.1068 ], [ 178.5557, 64.1057 ], [ 178.5526, 64.1026 ], [ 178.5578, 64.1016 ], [ 178.5609, 64.0964 ], [ 178.5661, 64.0953 ], [ 178.5661, 64.0922 ], [ 178.563, 64.0901 ], [ 178.5724, 64.0891 ], [ 178.5693, 64.0859 ], [ 178.5714, 64.0839 ], [ 178.5807, 64.0807 ], [ 178.5797, 64.0776 ], [ 178.5828, 64.0755 ], [ 178.5797, 64.0734 ], [ 178.5849, 64.0724 ], [ 178.5901, 64.063 ], [ 178.5953, 64.0609 ], [ 178.5833, 64.0604 ], [ 178.5792, 64.0625 ], [ 178.5646, 64.0625 ], [ 178.5521, 64.0563 ], [ 178.5375, 64.0604 ], [ 178.5313, 64.0604 ], [ 178.5213, 64.0557 ], [ 178.5224, 64.0526 ], [ 178.5151, 64.0516 ], [ 178.5161, 64.0443 ], [ 178.5099, 64.0401 ], [ 178.5005, 64.0391 ], [ 178.4943, 64.0349 ], [ 178.4932, 64.0318 ], [ 178.4875, 64.0292 ], [ 178.4688, 64.0292 ], [ 178.4646, 64.0313 ], [ 178.4604, 64.0292 ], [ 178.4542, 64.0313 ], [ 178.45, 64.0292 ], [ 178.4458, 64.0313 ], [ 178.425, 64.0313 ], [ 178.4234, 64.0297 ], [ 178.4255, 64.0276 ], [ 178.4375, 64.0229 ], [ 178.4479, 64.025 ], [ 178.4557, 64.0214 ], [ 178.4458, 64.0208 ], [ 178.4375, 64.0167 ], [ 178.4255, 64.0162 ], [ 178.4182, 64.0109 ], [ 178.4026, 64.0036 ], [ 178.3995, 64.0005 ], [ 178.3901, 63.9995 ], [ 178.3854, 63.9958 ], [ 178.3604, 63.9958 ], [ 178.3589, 63.9953 ], [ 178.3599, 63.9922 ], [ 178.3583, 63.9917 ], [ 178.3542, 63.9917 ], [ 178.35, 63.9896 ], [ 178.3396, 63.9896 ], [ 178.3349, 63.9859 ], [ 178.3271, 63.9854 ], [ 178.3146, 63.9813 ], [ 178.3104, 63.9833 ], [ 178.3026, 63.9839 ], [ 178.3, 63.9875 ], [ 178.2875, 63.9875 ], [ 178.2833, 63.9896 ], [ 178.2792, 63.9896 ], [ 178.2724, 63.9859 ], [ 178.2583, 63.9833 ], [ 178.2568, 63.9818 ], [ 178.2729, 63.9813 ], [ 178.2813, 63.9854 ], [ 178.2854, 63.9833 ], [ 178.2958, 63.9833 ], [ 178.3062, 63.9792 ], [ 178.3125, 63.9792 ], [ 178.3167, 63.9771 ], [ 178.3271, 63.9771 ], [ 178.3333, 63.975 ], [ 178.3432, 63.9776 ], [ 178.3422, 63.9807 ], [ 178.3464, 63.9828 ], [ 178.3646, 63.9875 ], [ 178.3792, 63.9875 ], [ 178.3833, 63.9854 ], [ 178.3938, 63.9854 ], [ 178.4005, 63.9891 ], [ 178.4187, 63.9938 ], [ 178.4307, 63.9943 ], [ 178.4297, 63.9974 ], [ 178.4396, 64.0021 ], [ 178.4438, 64.0021 ], [ 178.4479, 64.0 ], [ 178.4542, 64.0021 ], [ 178.4588, 63.9964 ], [ 178.4641, 63.9953 ], [ 178.4609, 63.9922 ], [ 178.4688, 63.9917 ], [ 178.4708, 63.9896 ], [ 178.4688, 63.9875 ], [ 178.4547, 63.987 ], [ 178.4547, 63.9839 ], [ 178.4625, 63.9833 ], [ 178.4667, 63.9854 ], [ 178.475, 63.9854 ], [ 178.4787, 63.9828 ], [ 178.4807, 63.9776 ], [ 178.4729, 63.9771 ], [ 178.4583, 63.9813 ], [ 178.4542, 63.9792 ], [ 178.45, 63.9813 ], [ 178.4438, 63.9813 ], [ 178.4339, 63.9786 ], [ 178.4339, 63.9755 ], [ 178.4625, 63.975 ], [ 178.4693, 63.9714 ], [ 178.4771, 63.9708 ], [ 178.4875, 63.9667 ], [ 178.4917, 63.9688 ], [ 178.5021, 63.9688 ], [ 178.512, 63.9714 ], [ 178.5109, 63.9745 ], [ 178.5141, 63.9755 ], [ 178.5167, 63.9792 ], [ 178.5313, 63.9792 ], [ 178.5417, 63.975 ], [ 178.5495, 63.9745 ], [ 178.5521, 63.9708 ], [ 178.5599, 63.9703 ], [ 178.5568, 63.9672 ], [ 178.5661, 63.9661 ], [ 178.5693, 63.963 ], [ 178.5813, 63.9583 ], [ 178.5917, 63.9583 ], [ 178.6021, 63.9542 ], [ 178.6521, 63.9542 ], [ 178.6547, 63.9505 ], [ 178.6662, 63.9474 ], [ 178.6672, 63.9443 ], [ 178.675, 63.9417 ], [ 178.6792, 63.9417 ], [ 178.6833, 63.9437 ], [ 178.6953, 63.9432 ], [ 178.6974, 63.9401 ], [ 178.6812, 63.9396 ], [ 178.6797, 63.938 ], [ 178.6891, 63.937 ], [ 178.6917, 63.9333 ], [ 178.7057, 63.9328 ], [ 178.7099, 63.9245 ], [ 178.7099, 63.9214 ], [ 178.7021, 63.9208 ], [ 178.6979, 63.9229 ], [ 178.6938, 63.9229 ], [ 178.6839, 63.9172 ], [ 178.6891, 63.9161 ], [ 178.6917, 63.9125 ], [ 178.7021, 63.9125 ], [ 178.7063, 63.9146 ], [ 178.7141, 63.9141 ], [ 178.7109, 63.912 ], [ 178.7109, 63.9089 ], [ 178.7161, 63.9078 ], [ 178.7182, 63.9057 ], [ 178.7146, 63.9021 ], [ 178.7193, 63.8901 ], [ 178.7245, 63.8891 ], [ 178.7214, 63.8859 ], [ 178.7391, 63.8495 ], [ 178.7396, 63.8458 ], [ 178.738, 63.8422 ], [ 178.7432, 63.8391 ], [ 178.7401, 63.8359 ], [ 178.7453, 63.8349 ], [ 178.7422, 63.8318 ], [ 178.7474, 63.8307 ], [ 178.7484, 63.8276 ], [ 178.7536, 63.8266 ], [ 178.7464, 63.8214 ], [ 178.7484, 63.8172 ], [ 178.7578, 63.8161 ], [ 178.7547, 63.813 ], [ 178.7589, 63.8068 ], [ 178.762, 63.8057 ], [ 178.7609, 63.8005 ], [ 178.7661, 63.7995 ], [ 178.7625, 63.7958 ], [ 178.7667, 63.7875 ], [ 178.763, 63.7839 ], [ 178.7682, 63.7734 ], [ 178.7646, 63.7708 ], [ 178.7651, 63.7693 ], [ 178.7703, 63.7682 ], [ 178.7724, 63.7641 ], [ 178.7713, 63.7589 ], [ 178.7745, 63.7568 ], [ 178.7713, 63.7547 ], [ 178.7755, 63.7464 ], [ 178.7828, 63.7411 ], [ 178.7797, 63.738 ], [ 178.7849, 63.737 ], [ 178.7818, 63.7339 ], [ 178.787, 63.7328 ], [ 178.787, 63.7297 ], [ 178.7839, 63.7276 ], [ 178.7891, 63.7172 ], [ 178.7859, 63.7151 ], [ 178.7912, 63.7141 ], [ 178.7875, 63.7104 ], [ 178.7917, 63.7021 ], [ 178.788, 63.6984 ], [ 178.7932, 63.6891 ], [ 178.7901, 63.6859 ], [ 178.7932, 63.6849 ], [ 178.7917, 63.6813 ], [ 178.7953, 63.6734 ], [ 178.7922, 63.6724 ], [ 178.7912, 63.6693 ], [ 178.788, 63.6672 ], [ 178.7974, 63.6661 ], [ 178.7953, 63.6609 ], [ 178.7922, 63.6589 ], [ 178.7974, 63.6578 ], [ 178.7943, 63.6547 ], [ 178.7995, 63.6536 ], [ 178.7964, 63.6505 ], [ 178.8016, 63.6495 ], [ 178.7984, 63.6474 ], [ 178.7958, 63.6417 ], [ 178.8, 63.6312 ], [ 178.7979, 63.6271 ], [ 178.8042, 63.6125 ], [ 178.7974, 63.6068 ], [ 178.7937, 63.6063 ], [ 178.7922, 63.6078 ], [ 178.7953, 63.6099 ], [ 178.7891, 63.6141 ], [ 178.7734, 63.6193 ], [ 178.7713, 63.6224 ], [ 178.7786, 63.6234 ], [ 178.7792, 63.6271 ], [ 178.7724, 63.6391 ], [ 178.7646, 63.6396 ], [ 178.7563, 63.6437 ], [ 178.75, 63.6417 ], [ 178.7312, 63.6417 ], [ 178.7271, 63.6437 ], [ 178.7208, 63.6437 ], [ 178.7161, 63.6495 ], [ 178.7109, 63.6505 ], [ 178.7083, 63.6542 ], [ 178.6979, 63.6542 ], [ 178.6812, 63.6604 ], [ 178.6609, 63.6609 ], [ 178.6625, 63.6646 ], [ 178.6578, 63.6766 ], [ 178.6542, 63.6771 ], [ 178.65, 63.675 ], [ 178.6458, 63.6771 ], [ 178.6354, 63.6771 ], [ 178.6313, 63.675 ], [ 178.6214, 63.6745 ], [ 178.6266, 63.6714 ], [ 178.6193, 63.6703 ], [ 178.6203, 63.6651 ], [ 178.6172, 63.6641 ], [ 178.6172, 63.6609 ], [ 178.6193, 63.6589 ], [ 178.6245, 63.6578 ], [ 178.6255, 63.6526 ], [ 178.6333, 63.6521 ], [ 178.6411, 63.6495 ], [ 178.638, 63.6464 ], [ 178.6432, 63.6453 ], [ 178.6453, 63.6432 ], [ 178.6422, 63.6401 ], [ 178.6474, 63.637 ], [ 178.6484, 63.6339 ], [ 178.6536, 63.6328 ], [ 178.6609, 63.6276 ], [ 178.6724, 63.6245 ], [ 178.6792, 63.6208 ], [ 178.6854, 63.6229 ], [ 178.6896, 63.6208 ], [ 178.7042, 63.6208 ], [ 178.7224, 63.6255 ], [ 178.725, 63.6292 ], [ 178.7292, 63.6271 ], [ 178.7474, 63.6245 ], [ 178.7536, 63.6203 ], [ 178.7505, 63.6172 ], [ 178.7583, 63.6146 ], [ 178.7688, 63.6146 ], [ 178.7797, 63.6068 ], [ 178.7854, 63.6083 ], [ 178.7891, 63.6047 ], [ 178.7818, 63.6036 ], [ 178.7849, 63.6005 ], [ 178.7828, 63.5943 ], [ 178.7813, 63.5938 ], [ 178.7766, 63.5995 ], [ 178.763, 63.6036 ], [ 178.7505, 63.5974 ], [ 178.7516, 63.5922 ], [ 178.7437, 63.5875 ], [ 178.7333, 63.5875 ], [ 178.7188, 63.5917 ], [ 178.7083, 63.5917 ], [ 178.7026, 63.5891 ], [ 178.6995, 63.5839 ], [ 178.6839, 63.5807 ], [ 178.6812, 63.575 ], [ 178.6828, 63.5714 ], [ 178.675, 63.5708 ], [ 178.6708, 63.5729 ], [ 178.663, 63.5734 ], [ 178.662, 63.5766 ], [ 178.6536, 63.5828 ], [ 178.6443, 63.5839 ], [ 178.6417, 63.5896 ], [ 178.6443, 63.5932 ], [ 178.6474, 63.5943 ], [ 178.6463, 63.5995 ], [ 178.6495, 63.6005 ], [ 178.6495, 63.6036 ], [ 178.638, 63.6068 ], [ 178.638, 63.6099 ], [ 178.6411, 63.612 ], [ 178.637, 63.6182 ], [ 178.6276, 63.6193 ], [ 178.6255, 63.6245 ], [ 178.6286, 63.6255 ], [ 178.6286, 63.6286 ], [ 178.6234, 63.6297 ], [ 178.6187, 63.6333 ], [ 178.6125, 63.6333 ], [ 178.6083, 63.6354 ], [ 178.5979, 63.6354 ], [ 178.5901, 63.638 ], [ 178.5875, 63.6417 ], [ 178.5818, 63.6391 ], [ 178.5792, 63.6354 ], [ 178.5479, 63.6333 ], [ 178.5443, 63.6359 ], [ 178.5432, 63.6391 ], [ 178.538, 63.6401 ], [ 178.537, 63.6432 ], [ 178.5333, 63.6437 ], [ 178.5318, 63.6432 ], [ 178.5328, 63.6401 ], [ 178.5313, 63.6396 ], [ 178.5271, 63.6396 ], [ 178.5229, 63.6375 ], [ 178.5167, 63.6375 ], [ 178.5125, 63.6396 ], [ 178.5026, 63.6359 ], [ 178.5208, 63.6354 ], [ 178.5224, 63.6339 ], [ 178.5193, 63.6328 ], [ 178.5193, 63.6297 ], [ 178.5333, 63.6292 ], [ 178.5432, 63.6266 ], [ 178.5505, 63.613 ], [ 178.5557, 63.6109 ], [ 178.5526, 63.6099 ], [ 178.5537, 63.6047 ], [ 178.5484, 63.6016 ], [ 178.55, 63.5938 ], [ 178.5474, 63.5901 ], [ 178.5401, 63.5891 ], [ 178.5333, 63.5854 ], [ 178.525, 63.5854 ], [ 178.5125, 63.5792 ], [ 178.5047, 63.5818 ], [ 178.5026, 63.5859 ], [ 178.5141, 63.5922 ], [ 178.5125, 63.5958 ], [ 178.5146, 63.6 ], [ 178.513, 63.6036 ], [ 178.5161, 63.6057 ], [ 178.5109, 63.6068 ], [ 178.5109, 63.6099 ], [ 178.5182, 63.6109 ], [ 178.5266, 63.6161 ], [ 178.5146, 63.6167 ], [ 178.5063, 63.6125 ], [ 178.4896, 63.6188 ], [ 178.4812, 63.6188 ], [ 178.4771, 63.6167 ], [ 178.425, 63.6167 ], [ 178.4208, 63.6146 ], [ 178.4167, 63.6167 ], [ 178.4104, 63.6167 ], [ 178.3958, 63.6208 ], [ 178.3896, 63.6208 ], [ 178.3854, 63.6188 ], [ 178.3776, 63.6224 ], [ 178.3807, 63.6234 ], [ 178.3818, 63.6266 ], [ 178.387, 63.6307 ], [ 178.3687, 63.6333 ], [ 178.3646, 63.6354 ], [ 178.3562, 63.6354 ], [ 178.3505, 63.6328 ], [ 178.35, 63.6312 ], [ 178.3505, 63.6297 ], [ 178.3557, 63.6286 ], [ 178.3526, 63.6255 ], [ 178.3578, 63.6245 ], [ 178.3547, 63.6214 ], [ 178.3583, 63.6188 ], [ 178.3662, 63.6182 ], [ 178.3703, 63.6151 ], [ 178.3443, 63.6141 ], [ 178.3411, 63.6109 ], [ 178.3338, 63.6078 ], [ 178.3349, 63.6026 ], [ 178.3276, 63.5995 ], [ 178.3286, 63.5964 ], [ 178.3255, 63.5953 ], [ 178.3266, 63.5922 ], [ 178.3083, 63.5833 ], [ 178.3, 63.5833 ], [ 178.2958, 63.5812 ], [ 178.2875, 63.5812 ], [ 178.2776, 63.5786 ], [ 178.2792, 63.5771 ], [ 178.287, 63.5766 ], [ 178.2891, 63.5724 ], [ 178.2891, 63.5693 ], [ 178.2818, 63.5682 ], [ 178.2839, 63.563 ], [ 178.2932, 63.562 ], [ 178.3005, 63.5484 ], [ 178.312, 63.5453 ], [ 178.3172, 63.5359 ], [ 178.3276, 63.5297 ], [ 178.3328, 63.5286 ], [ 178.3401, 63.5234 ], [ 178.3479, 63.5208 ], [ 178.3646, 63.5208 ], [ 178.3687, 63.5187 ], [ 178.375, 63.5187 ], [ 178.3938, 63.5125 ], [ 178.3979, 63.5146 ], [ 178.4141, 63.5151 ], [ 178.4203, 63.5193 ], [ 178.4214, 63.5245 ], [ 178.4234, 63.5266 ], [ 178.4521, 63.5313 ], [ 178.4609, 63.537 ], [ 178.4682, 63.538 ], [ 178.4766, 63.5422 ], [ 178.4797, 63.5474 ], [ 178.4854, 63.5479 ], [ 178.4912, 63.5505 ], [ 178.4974, 63.5547 ], [ 178.4984, 63.5578 ], [ 178.5078, 63.563 ], [ 178.5109, 63.5703 ], [ 178.5141, 63.5714 ], [ 178.5188, 63.5771 ], [ 178.5292, 63.5771 ], [ 178.5333, 63.575 ], [ 178.5396, 63.575 ], [ 178.5437, 63.5708 ], [ 178.55, 63.5729 ], [ 178.5542, 63.5708 ], [ 178.5688, 63.5708 ], [ 178.5729, 63.5687 ], [ 178.5792, 63.5687 ], [ 178.5854, 63.5729 ], [ 178.6, 63.5729 ], [ 178.6042, 63.575 ], [ 178.6125, 63.575 ], [ 178.6167, 63.5771 ], [ 178.6354, 63.5771 ], [ 178.6396, 63.5792 ], [ 178.6474, 63.5745 ], [ 178.6505, 63.5693 ], [ 178.6599, 63.5682 ], [ 178.6589, 63.5651 ], [ 178.6724, 63.562 ], [ 178.6771, 63.5583 ], [ 178.6958, 63.5583 ], [ 178.7026, 63.562 ], [ 178.7125, 63.5646 ], [ 178.7208, 63.5646 ], [ 178.725, 63.5667 ], [ 178.7333, 63.5667 ], [ 178.7375, 63.5687 ], [ 178.7437, 63.5667 ], [ 178.7521, 63.5708 ], [ 178.7682, 63.5703 ], [ 178.7682, 63.5672 ], [ 178.7651, 63.5661 ], [ 178.7661, 63.563 ], [ 178.763, 63.562 ], [ 178.762, 63.5589 ], [ 178.7479, 63.5583 ], [ 178.7312, 63.55 ], [ 178.7229, 63.55 ], [ 178.7214, 63.5516 ], [ 178.7245, 63.5536 ], [ 178.7104, 63.5563 ], [ 178.7042, 63.5521 ], [ 178.688, 63.5495 ], [ 178.6849, 63.5464 ], [ 178.6792, 63.5458 ], [ 178.6734, 63.5432 ], [ 178.6708, 63.5375 ], [ 178.6724, 63.5339 ], [ 178.6604, 63.5333 ], [ 178.6589, 63.5328 ], [ 178.6599, 63.5297 ], [ 178.6568, 63.5286 ], [ 178.6583, 63.5271 ], [ 178.6687, 63.5271 ], [ 178.6792, 63.5229 ], [ 178.6938, 63.5229 ], [ 178.7078, 63.5276 ], [ 178.7068, 63.5307 ], [ 178.7245, 63.5401 ], [ 178.7234, 63.5453 ], [ 178.7333, 63.5479 ], [ 178.7396, 63.5479 ], [ 178.7453, 63.5453 ], [ 178.7479, 63.5417 ], [ 178.7443, 63.5318 ], [ 178.7474, 63.5307 ], [ 178.7458, 63.5271 ], [ 178.7526, 63.5109 ], [ 178.7604, 63.5083 ], [ 178.7708, 63.5083 ], [ 178.775, 63.5062 ], [ 178.7917, 63.5042 ], [ 178.7953, 63.5005 ], [ 178.788, 63.4995 ], [ 178.7854, 63.4958 ], [ 178.7713, 63.4953 ], [ 178.7672, 63.4932 ], [ 178.7661, 63.4901 ], [ 178.7563, 63.4875 ], [ 178.7437, 63.4875 ], [ 178.7396, 63.4854 ], [ 178.7146, 63.4875 ], [ 178.7088, 63.4839 ], [ 178.7109, 63.4797 ], [ 178.7146, 63.4771 ], [ 178.7266, 63.4776 ], [ 178.7312, 63.4833 ], [ 178.7458, 63.4792 ], [ 178.7521, 63.4792 ], [ 178.7563, 63.4813 ], [ 178.7724, 63.4807 ], [ 178.7786, 63.4766 ], [ 178.7807, 63.4714 ], [ 178.775, 63.4708 ], [ 178.7651, 63.4661 ], [ 178.7661, 63.4609 ], [ 178.7589, 63.4609 ], [ 178.7568, 63.4641 ], [ 178.7599, 63.4661 ], [ 178.7521, 63.4688 ], [ 178.7479, 63.4688 ], [ 178.7422, 63.4661 ], [ 178.7391, 63.4589 ], [ 178.7375, 63.4583 ], [ 178.7188, 63.4583 ], [ 178.7109, 63.4609 ], [ 178.7141, 63.463 ], [ 178.7125, 63.4646 ], [ 178.7021, 63.4646 ], [ 178.6964, 63.463 ], [ 178.6953, 63.4661 ], [ 178.6839, 63.4693 ], [ 178.6911, 63.4734 ], [ 178.6859, 63.4766 ], [ 178.6932, 63.4776 ], [ 178.6922, 63.4807 ], [ 178.6958, 63.4833 ], [ 178.6953, 63.4849 ], [ 178.6901, 63.4859 ], [ 178.687, 63.4932 ], [ 178.6854, 63.4938 ], [ 178.675, 63.4938 ], [ 178.6625, 63.4896 ], [ 178.6479, 63.4917 ], [ 178.6422, 63.4891 ], [ 178.6432, 63.4859 ], [ 178.6401, 63.4849 ], [ 178.637, 63.4797 ], [ 178.6297, 63.4766 ], [ 178.6313, 63.475 ], [ 178.6417, 63.475 ], [ 178.6516, 63.4776 ], [ 178.6604, 63.4833 ], [ 178.6667, 63.4833 ], [ 178.6745, 63.4807 ], [ 178.6766, 63.4734 ], [ 178.6734, 63.4724 ], [ 178.675, 63.4688 ], [ 178.6714, 63.4661 ], [ 178.6734, 63.463 ], [ 178.6828, 63.4599 ], [ 178.6891, 63.4557 ], [ 178.688, 63.4526 ], [ 178.6922, 63.4505 ], [ 178.7104, 63.45 ], [ 178.7125, 63.4479 ], [ 178.7057, 63.4422 ], [ 178.6922, 63.4411 ], [ 178.6943, 63.438 ], [ 178.6995, 63.437 ], [ 178.7021, 63.4313 ], [ 178.7016, 63.4276 ], [ 178.6875, 63.4271 ], [ 178.6792, 63.4229 ], [ 178.675, 63.425 ], [ 178.6708, 63.425 ], [ 178.6609, 63.4203 ], [ 178.6662, 63.4182 ], [ 178.6667, 63.4167 ], [ 178.663, 63.4141 ], [ 178.6641, 63.4089 ], [ 178.6479, 63.4083 ], [ 178.6438, 63.4104 ], [ 178.6354, 63.4104 ], [ 178.6255, 63.4078 ], [ 178.6255, 63.4047 ], [ 178.6396, 63.4 ], [ 178.6474, 63.3995 ], [ 178.6505, 63.3922 ], [ 178.6687, 63.3875 ], [ 178.6833, 63.3875 ], [ 178.6911, 63.3849 ], [ 178.688, 63.3818 ], [ 178.7, 63.3812 ], [ 178.7104, 63.375 ], [ 178.7167, 63.375 ], [ 178.7208, 63.3771 ], [ 178.7333, 63.3771 ], [ 178.7375, 63.3792 ], [ 178.7542, 63.3729 ], [ 178.7589, 63.3786 ], [ 178.762, 63.3797 ], [ 178.7651, 63.3849 ], [ 178.7708, 63.3875 ], [ 178.7766, 63.388 ], [ 178.7776, 63.3911 ], [ 178.7839, 63.3953 ], [ 178.8042, 63.3958 ], [ 178.8083, 63.3979 ], [ 178.8125, 63.3979 ], [ 178.8182, 63.4005 ], [ 178.8172, 63.4036 ], [ 178.8333, 63.4042 ], [ 178.8521, 63.4146 ], [ 178.8557, 63.412 ], [ 178.8568, 63.4068 ], [ 178.8646, 63.4063 ], [ 178.8687, 63.4042 ], [ 178.875, 63.4063 ], [ 178.8833, 63.4063 ], [ 178.8911, 63.4109 ], [ 178.8854, 63.4146 ], [ 178.8672, 63.4151 ], [ 178.8729, 63.4188 ], [ 178.887, 63.4214 ], [ 178.887, 63.4245 ], [ 178.8849, 63.4266 ], [ 178.8797, 63.4276 ], [ 178.8786, 63.4328 ], [ 178.8766, 63.4349 ], [ 178.8672, 63.4359 ], [ 178.8703, 63.438 ], [ 178.8651, 63.4401 ], [ 178.8682, 63.4432 ], [ 178.863, 63.4443 ], [ 178.862, 63.4474 ], [ 178.8568, 63.4484 ], [ 178.8547, 63.4536 ], [ 178.8578, 63.4557 ], [ 178.8505, 63.4609 ], [ 178.8537, 63.4641 ], [ 178.8401, 63.463 ], [ 178.8474, 63.4672 ], [ 178.8453, 63.4703 ], [ 178.8401, 63.4734 ], [ 178.8432, 63.4755 ], [ 178.8432, 63.4786 ], [ 178.838, 63.4797 ], [ 178.8349, 63.4849 ], [ 178.8297, 63.4859 ], [ 178.8266, 63.4932 ], [ 178.8193, 63.4984 ], [ 178.8224, 63.5016 ], [ 178.8172, 63.5026 ], [ 178.8172, 63.5057 ], [ 178.8203, 63.5078 ], [ 178.8089, 63.5109 ], [ 178.812, 63.513 ], [ 178.8068, 63.5151 ], [ 178.8099, 63.5182 ], [ 178.8005, 63.5193 ], [ 178.8037, 63.5214 ], [ 178.8026, 63.5245 ], [ 178.8062, 63.5271 ], [ 178.8037, 63.5328 ], [ 178.7984, 63.5359 ], [ 178.7995, 63.5391 ], [ 178.7964, 63.5411 ], [ 178.7995, 63.5422 ], [ 178.7984, 63.5474 ], [ 178.8005, 63.5516 ], [ 178.8037, 63.5536 ], [ 178.8016, 63.5578 ], [ 178.7964, 63.5609 ], [ 178.7943, 63.5641 ], [ 178.8016, 63.5672 ], [ 178.8005, 63.5703 ], [ 178.8037, 63.5724 ], [ 178.7984, 63.5734 ], [ 178.7943, 63.5818 ], [ 178.7943, 63.5849 ], [ 178.7974, 63.587 ], [ 178.7922, 63.5901 ], [ 178.7937, 63.5938 ], [ 178.7922, 63.5974 ], [ 178.8, 63.6 ], [ 178.8161, 63.5953 ], [ 178.813, 63.5932 ], [ 178.8182, 63.5828 ], [ 178.8146, 63.5792 ], [ 178.8182, 63.5724 ], [ 178.8151, 63.5693 ], [ 178.8203, 63.5682 ], [ 178.8208, 63.5667 ], [ 178.8172, 63.5641 ], [ 178.8188, 63.5604 ], [ 178.8151, 63.5568 ], [ 178.8203, 63.5557 ], [ 178.8224, 63.5536 ], [ 178.8224, 63.5505 ], [ 178.8193, 63.5495 ], [ 178.8214, 63.5464 ], [ 178.8266, 63.5453 ], [ 178.8229, 63.5417 ], [ 178.8286, 63.5307 ], [ 178.8255, 63.5276 ], [ 178.8286, 63.5266 ], [ 178.8276, 63.5234 ], [ 178.8328, 63.5203 ], [ 178.838, 63.5089 ], [ 178.8432, 63.5078 ], [ 178.8401, 63.5047 ], [ 178.8422, 63.5005 ], [ 178.8474, 63.4995 ], [ 178.8443, 63.4964 ], [ 178.8495, 63.4953 ], [ 178.8464, 63.4922 ], [ 178.8516, 63.4911 ], [ 178.8484, 63.488 ], [ 178.8537, 63.4849 ], [ 178.8526, 63.4797 ], [ 178.862, 63.4766 ], [ 178.8589, 63.4734 ], [ 178.8609, 63.4693 ], [ 178.8662, 63.4682 ], [ 178.863, 63.4661 ], [ 178.8651, 63.4589 ], [ 178.8703, 63.4578 ], [ 178.8734, 63.4526 ], [ 178.8849, 63.4495 ], [ 178.8813, 63.4458 ], [ 178.8839, 63.4401 ], [ 178.8891, 63.437 ], [ 178.8922, 63.4318 ], [ 178.8974, 63.4307 ], [ 178.8943, 63.4276 ], [ 178.8995, 63.4266 ], [ 178.9047, 63.4172 ], [ 178.9109, 63.413 ], [ 178.9162, 63.412 ], [ 178.913, 63.4089 ], [ 178.9224, 63.4078 ], [ 178.9193, 63.4057 ], [ 178.9193, 63.4026 ], [ 178.9245, 63.4016 ], [ 178.9276, 63.3964 ], [ 178.9328, 63.3953 ], [ 178.9339, 63.3922 ], [ 178.9391, 63.3911 ], [ 178.9422, 63.3859 ], [ 178.9474, 63.3849 ], [ 178.9484, 63.3797 ], [ 178.9505, 63.3776 ], [ 178.9557, 63.3766 ], [ 178.9588, 63.3672 ], [ 178.9609, 63.3651 ], [ 178.9682, 63.3661 ], [ 178.9672, 63.3609 ], [ 178.9766, 63.3599 ], [ 178.9734, 63.3578 ], [ 178.9787, 63.3557 ], [ 178.9755, 63.3526 ], [ 178.9849, 63.3516 ], [ 178.9792, 63.3479 ], [ 178.9714, 63.3505 ], [ 178.9703, 63.3536 ], [ 178.9625, 63.3563 ], [ 178.9521, 63.3563 ], [ 178.9443, 63.3589 ], [ 178.9401, 63.362 ], [ 178.9495, 63.3672 ], [ 178.9495, 63.3703 ], [ 178.9417, 63.3729 ], [ 178.9375, 63.3708 ], [ 178.9297, 63.3714 ], [ 178.9266, 63.3786 ], [ 178.9187, 63.3792 ], [ 178.9099, 63.3849 ], [ 178.9047, 63.3859 ], [ 178.9104, 63.3896 ], [ 178.9203, 63.3901 ], [ 178.9245, 63.3922 ], [ 178.9229, 63.3937 ], [ 178.8979, 63.3937 ], [ 178.8938, 63.3917 ], [ 178.8854, 63.3917 ], [ 178.8729, 63.3875 ], [ 178.8646, 63.3875 ], [ 178.8547, 63.3849 ], [ 178.8401, 63.3766 ], [ 178.8375, 63.3708 ], [ 178.8391, 63.3651 ], [ 178.8359, 63.3641 ], [ 178.837, 63.3589 ], [ 178.8338, 63.3578 ], [ 178.8391, 63.3557 ], [ 178.8359, 63.3526 ], [ 178.8443, 63.3464 ], [ 178.8495, 63.3453 ], [ 178.8505, 63.3422 ], [ 178.8583, 63.3396 ], [ 178.8646, 63.3396 ], [ 178.8734, 63.3339 ], [ 178.8813, 63.3312 ], [ 178.8875, 63.3312 ], [ 178.8995, 63.3266 ], [ 178.8974, 63.3193 ], [ 178.8943, 63.3182 ], [ 178.8917, 63.3146 ], [ 178.8943, 63.3068 ], [ 178.9036, 63.3047 ], [ 178.9078, 63.3068 ], [ 178.9109, 63.3141 ], [ 178.9167, 63.3167 ], [ 178.925, 63.3167 ], [ 178.9307, 63.3193 ], [ 178.9193, 63.3245 ], [ 178.9333, 63.3292 ], [ 178.9453, 63.3286 ], [ 178.9464, 63.3255 ], [ 178.95, 63.3229 ], [ 178.9688, 63.3229 ], [ 178.9766, 63.3203 ], [ 178.9625, 63.3104 ], [ 178.963, 63.3089 ], [ 178.9787, 63.3036 ], [ 178.9807, 63.3005 ], [ 178.9729, 63.3 ], [ 178.9688, 63.3021 ], [ 178.9557, 63.2943 ], [ 178.95, 63.2938 ], [ 178.9484, 63.2922 ], [ 178.9604, 63.2896 ], [ 178.9672, 63.2932 ], [ 178.9729, 63.2938 ], [ 178.9766, 63.2911 ], [ 178.9734, 63.288 ], [ 178.9787, 63.287 ], [ 178.9828, 63.2839 ], [ 178.9729, 63.2833 ], [ 178.9714, 63.2818 ], [ 178.9979, 63.2813 ], [ 179.0021, 63.2792 ], [ 179.0125, 63.2792 ], [ 179.0229, 63.275 ], [ 179.0307, 63.2745 ], [ 179.0276, 63.2714 ], [ 179.0354, 63.2708 ], [ 179.0458, 63.2667 ], [ 179.05, 63.2687 ], [ 179.0646, 63.2687 ], [ 179.0792, 63.2646 ], [ 179.1083, 63.2646 ], [ 179.1109, 63.2609 ], [ 179.1266, 63.2547 ], [ 179.1208, 63.2542 ], [ 179.1151, 63.2516 ], [ 179.112, 63.2464 ], [ 179.1089, 63.2453 ], [ 179.1068, 63.2422 ], [ 179.1167, 63.2438 ], [ 179.125, 63.2479 ], [ 179.1292, 63.2458 ], [ 179.1438, 63.2479 ], [ 179.1662, 63.2391 ], [ 179.1693, 63.2339 ], [ 179.1745, 63.2328 ], [ 179.1771, 63.2271 ], [ 179.1755, 63.2234 ], [ 179.1875, 63.2167 ], [ 179.1911, 63.2172 ], [ 179.1943, 63.2224 ], [ 179.1974, 63.2245 ], [ 179.1896, 63.2271 ], [ 179.1833, 63.2271 ], [ 179.1818, 63.2286 ], [ 179.1938, 63.2292 ], [ 179.1979, 63.2271 ], [ 179.2021, 63.2292 ], [ 179.2396, 63.2292 ], [ 179.2578, 63.2224 ], [ 179.2609, 63.2172 ], [ 179.2688, 63.2167 ], [ 179.2937, 63.2083 ], [ 179.3016, 63.2078 ], [ 179.2984, 63.2047 ], [ 179.3104, 63.2042 ], [ 179.3193, 63.1984 ], [ 179.3245, 63.1974 ], [ 179.3276, 63.1943 ], [ 179.337, 63.1932 ], [ 179.338, 63.188 ], [ 179.3432, 63.187 ], [ 179.3453, 63.1849 ], [ 179.3464, 63.1797 ], [ 179.3557, 63.1786 ], [ 179.3609, 63.1693 ], [ 179.3724, 63.1661 ], [ 179.3734, 63.1609 ], [ 179.3786, 63.1599 ], [ 179.3911, 63.1516 ], [ 179.3943, 63.1464 ], [ 179.4057, 63.1432 ], [ 179.4068, 63.1401 ], [ 179.4162, 63.1391 ], [ 179.4193, 63.1339 ], [ 179.4287, 63.1328 ], [ 179.4359, 63.1276 ], [ 179.4411, 63.1266 ], [ 179.4422, 63.1214 ], [ 179.4474, 63.1203 ], [ 179.4536, 63.1161 ], [ 179.4521, 63.1104 ], [ 179.4557, 63.1026 ], [ 179.4526, 63.1005 ], [ 179.4557, 63.0922 ], [ 179.4526, 63.0911 ], [ 179.4536, 63.088 ], [ 179.45, 63.0854 ], [ 179.4526, 63.0818 ], [ 179.462, 63.0807 ], [ 179.4583, 63.0771 ], [ 179.4588, 63.0755 ], [ 179.4641, 63.0745 ], [ 179.4646, 63.0729 ], [ 179.4625, 63.0687 ], [ 179.4667, 63.0583 ], [ 179.4641, 63.0547 ], [ 179.4542, 63.0542 ], [ 179.4417, 63.05 ], [ 179.4312, 63.05 ], [ 179.4287, 63.0536 ], [ 179.4208, 63.0542 ], [ 179.4, 63.0625 ], [ 179.3896, 63.0625 ], [ 179.3854, 63.0646 ], [ 179.3667, 63.0646 ], [ 179.3625, 63.0625 ], [ 179.3521, 63.0625 ], [ 179.3422, 63.0599 ], [ 179.3391, 63.0547 ], [ 179.3245, 63.0464 ], [ 179.3172, 63.0453 ], [ 179.3182, 63.0422 ], [ 179.3068, 63.037 ], [ 179.3042, 63.0333 ], [ 179.3057, 63.0297 ], [ 179.2984, 63.0266 ], [ 179.2995, 63.0234 ], [ 179.2964, 63.0224 ], [ 179.2932, 63.0151 ], [ 179.2854, 63.0104 ], [ 179.2776, 63.013 ], [ 179.2734, 63.0193 ], [ 179.2734, 63.0224 ], [ 179.2792, 63.0229 ], [ 179.2807, 63.0245 ], [ 179.2755, 63.0255 ], [ 179.2724, 63.0328 ], [ 179.2646, 63.0354 ], [ 179.2583, 63.0354 ], [ 179.2542, 63.0375 ], [ 179.2271, 63.0375 ], [ 179.2229, 63.0354 ], [ 179.2146, 63.0354 ], [ 179.2104, 63.0333 ], [ 179.1958, 63.0333 ], [ 179.1854, 63.0375 ], [ 179.1812, 63.0375 ], [ 179.1771, 63.0354 ], [ 179.1687, 63.0354 ], [ 179.1662, 63.0318 ], [ 179.162, 63.0297 ], [ 179.138, 63.0266 ], [ 179.1307, 63.0214 ], [ 179.125, 63.0208 ], [ 179.1193, 63.0182 ], [ 179.1182, 63.0151 ], [ 179.1109, 63.0141 ], [ 179.1037, 63.0089 ], [ 179.0964, 63.0078 ], [ 179.0953, 63.0047 ], [ 179.0901, 63.0016 ], [ 179.087, 62.9964 ], [ 179.0797, 62.9932 ], [ 179.0766, 62.9901 ], [ 179.063, 62.9891 ], [ 179.0609, 62.9859 ], [ 179.0661, 62.9828 ], [ 179.063, 62.9807 ], [ 179.0651, 62.9755 ], [ 179.0688, 62.9729 ], [ 179.0917, 62.9729 ], [ 179.1099, 62.9661 ], [ 179.1089, 62.963 ], [ 179.1104, 62.9625 ], [ 179.1229, 62.9625 ], [ 179.1438, 62.9688 ], [ 179.15, 62.9688 ], [ 179.1542, 62.9667 ], [ 179.1729, 62.9667 ], [ 179.1771, 62.9688 ], [ 179.1812, 62.9667 ], [ 179.1958, 62.9667 ], [ 179.1974, 62.9672 ], [ 179.1964, 62.9703 ], [ 179.2063, 62.9708 ], [ 179.2104, 62.9729 ], [ 179.212, 62.9714 ], [ 179.2068, 62.9672 ], [ 179.2229, 62.9667 ], [ 179.2245, 62.9672 ], [ 179.2234, 62.9703 ], [ 179.2266, 62.9724 ], [ 179.2214, 62.9734 ], [ 179.2203, 62.9766 ], [ 179.2151, 62.9797 ], [ 179.2182, 62.9828 ], [ 179.2141, 62.9891 ], [ 179.2088, 62.9901 ], [ 179.2125, 62.9938 ], [ 179.2099, 62.9995 ], [ 179.2047, 63.0005 ], [ 179.2026, 63.0047 ], [ 179.2042, 63.0104 ], [ 179.2021, 63.0146 ], [ 179.2057, 63.0172 ], [ 179.2026, 63.0266 ], [ 179.2182, 63.0297 ], [ 179.2229, 63.0333 ], [ 179.2375, 63.0333 ], [ 179.2391, 63.0328 ], [ 179.238, 63.0297 ], [ 179.2396, 63.0292 ], [ 179.2583, 63.0292 ], [ 179.2661, 63.0266 ], [ 179.2625, 63.0229 ], [ 179.2646, 63.0187 ], [ 179.2625, 63.0146 ], [ 179.2661, 63.0078 ], [ 179.263, 63.0047 ], [ 179.2724, 63.0016 ], [ 179.2693, 62.9995 ], [ 179.2713, 62.9964 ], [ 179.2792, 62.9979 ], [ 179.2807, 62.9953 ], [ 179.2792, 62.9938 ], [ 179.2734, 62.9932 ], [ 179.2734, 62.9901 ], [ 179.2786, 62.988 ], [ 179.2713, 62.9839 ], [ 179.2828, 62.9807 ], [ 179.2797, 62.9786 ], [ 179.2807, 62.9755 ], [ 179.2776, 62.9734 ], [ 179.2807, 62.9724 ], [ 179.2797, 62.9672 ], [ 179.2849, 62.9661 ], [ 179.287, 62.962 ], [ 179.2839, 62.9599 ], [ 179.287, 62.9505 ], [ 179.2839, 62.9484 ], [ 179.2891, 62.9474 ], [ 179.2901, 62.9443 ], [ 179.2953, 62.9432 ], [ 179.2922, 62.9401 ], [ 179.2995, 62.9349 ], [ 179.3005, 62.9318 ], [ 179.3057, 62.9307 ], [ 179.3089, 62.9255 ], [ 179.3141, 62.9245 ], [ 179.3151, 62.9214 ], [ 179.3203, 62.9203 ], [ 179.3224, 62.9151 ], [ 179.3188, 62.9125 ], [ 179.3193, 62.9109 ], [ 179.3245, 62.9099 ], [ 179.3214, 62.9068 ], [ 179.3276, 62.8964 ], [ 179.3338, 62.8922 ], [ 179.3391, 62.8911 ], [ 179.3401, 62.888 ], [ 179.3495, 62.8849 ], [ 179.3484, 62.8818 ], [ 179.3604, 62.8812 ], [ 179.3646, 62.8771 ], [ 179.4063, 62.8792 ], [ 179.4141, 62.8766 ], [ 179.4167, 62.8729 ], [ 179.4646, 62.8708 ], [ 179.4792, 62.8667 ], [ 179.5125, 62.8667 ], [ 179.5167, 62.8646 ], [ 179.5245, 62.8641 ], [ 179.5255, 62.8609 ], [ 179.5307, 62.8599 ], [ 179.5339, 62.8547 ], [ 179.5432, 62.8516 ], [ 179.5526, 62.8443 ], [ 179.5578, 62.8432 ], [ 179.5641, 62.8391 ], [ 179.5661, 62.8339 ], [ 179.5589, 62.8297 ], [ 179.5766, 62.8307 ], [ 179.5734, 62.8286 ], [ 179.5755, 62.8234 ], [ 179.5807, 62.8224 ], [ 179.5828, 62.8182 ], [ 179.5797, 62.8161 ], [ 179.5766, 62.8089 ], [ 179.5734, 62.8078 ], [ 179.5766, 62.8005 ], [ 179.5708, 62.7979 ], [ 179.5583, 62.7979 ], [ 179.5443, 62.7932 ], [ 179.5474, 62.787 ], [ 179.5443, 62.7849 ], [ 179.5443, 62.7818 ], [ 179.5479, 62.7792 ], [ 179.5557, 62.7786 ], [ 179.5568, 62.7755 ], [ 179.5672, 62.7714 ], [ 179.5792, 62.7708 ], [ 179.5932, 62.7661 ], [ 179.6005, 62.7609 ], [ 179.6099, 62.7599 ], [ 179.613, 62.7526 ], [ 179.6182, 62.7516 ], [ 179.6203, 62.7474 ], [ 179.6172, 62.7443 ], [ 179.6224, 62.7432 ], [ 179.6245, 62.7391 ], [ 179.6208, 62.7354 ], [ 179.6266, 62.7234 ], [ 179.6193, 62.7203 ], [ 179.6203, 62.7151 ], [ 179.6062, 62.7146 ], [ 179.6042, 62.7125 ], [ 179.6047, 62.7109 ], [ 179.6162, 62.7078 ], [ 179.6172, 62.7047 ], [ 179.6224, 62.7036 ], [ 179.6193, 62.7005 ], [ 179.6245, 62.6995 ], [ 179.6266, 62.6943 ], [ 179.6234, 62.6922 ], [ 179.6286, 62.6911 ], [ 179.6292, 62.6896 ], [ 179.6245, 62.6776 ], [ 179.6214, 62.6766 ], [ 179.6224, 62.6734 ], [ 179.6151, 62.6703 ], [ 179.6203, 62.6682 ], [ 179.6224, 62.6609 ], [ 179.6203, 62.6589 ], [ 179.613, 62.6578 ], [ 179.612, 62.6547 ], [ 179.6021, 62.6542 ], [ 179.5979, 62.6521 ], [ 179.5833, 62.6563 ], [ 179.5688, 62.6563 ], [ 179.5672, 62.6568 ], [ 179.5703, 62.6589 ], [ 179.5688, 62.6604 ], [ 179.5583, 62.6604 ], [ 179.5542, 62.6625 ], [ 179.5479, 62.6625 ], [ 179.5375, 62.6667 ], [ 179.5271, 62.6667 ], [ 179.5042, 62.6729 ], [ 179.4937, 62.6729 ], [ 179.4833, 62.6771 ], [ 179.4729, 62.6771 ], [ 179.4714, 62.6786 ], [ 179.4745, 62.6797 ], [ 179.4729, 62.6813 ], [ 179.4667, 62.6792 ], [ 179.4568, 62.6786 ], [ 179.4588, 62.6734 ], [ 179.475, 62.6729 ], [ 179.4812, 62.6667 ], [ 179.4917, 62.6667 ], [ 179.4958, 62.6646 ], [ 179.5063, 62.6646 ], [ 179.5104, 62.6625 ], [ 179.5167, 62.6625 ], [ 179.5182, 62.662 ], [ 179.5172, 62.6589 ], [ 179.5188, 62.6583 ], [ 179.5229, 62.6583 ], [ 179.5271, 62.6604 ], [ 179.5375, 62.6563 ], [ 179.5453, 62.6557 ], [ 179.5521, 62.6521 ], [ 179.5583, 62.6542 ], [ 179.5625, 62.65 ], [ 179.5688, 62.65 ], [ 179.5875, 62.6437 ], [ 179.5938, 62.6437 ], [ 179.5974, 62.6411 ], [ 179.5995, 62.6359 ], [ 179.5964, 62.6349 ], [ 179.5979, 62.6292 ], [ 179.5953, 62.6234 ], [ 179.5891, 62.6193 ], [ 179.5818, 62.6182 ], [ 179.5724, 62.613 ], [ 179.5667, 62.6125 ], [ 179.5625, 62.6146 ], [ 179.5437, 62.6146 ], [ 179.5396, 62.6125 ], [ 179.5354, 62.6146 ], [ 179.5167, 62.6146 ], [ 179.5125, 62.6125 ], [ 179.5083, 62.6125 ], [ 179.5036, 62.6089 ], [ 179.4896, 62.6063 ], [ 179.4797, 62.6016 ], [ 179.4766, 62.5984 ], [ 179.4588, 62.5891 ], [ 179.4599, 62.5859 ], [ 179.4526, 62.5849 ], [ 179.4495, 62.5797 ], [ 179.4438, 62.5771 ], [ 179.438, 62.5766 ], [ 179.4432, 62.5745 ], [ 179.4443, 62.5714 ], [ 179.4495, 62.5703 ], [ 179.4578, 62.5526 ], [ 179.4505, 62.5495 ], [ 179.4516, 62.5464 ], [ 179.4484, 62.5453 ], [ 179.45, 62.5417 ], [ 179.4464, 62.538 ], [ 179.4495, 62.5318 ], [ 179.4464, 62.5307 ], [ 179.4474, 62.5276 ], [ 179.4401, 62.5266 ], [ 179.4391, 62.5234 ], [ 179.4214, 62.5161 ], [ 179.4193, 62.5141 ], [ 179.4224, 62.512 ], [ 179.4203, 62.5089 ], [ 179.413, 62.5078 ], [ 179.4182, 62.5036 ], [ 179.4182, 62.5005 ], [ 179.4162, 62.4984 ], [ 179.4104, 62.4979 ], [ 179.4021, 62.4938 ], [ 179.388, 62.4911 ], [ 179.3891, 62.488 ], [ 179.3833, 62.4854 ], [ 179.3776, 62.4849 ], [ 179.3729, 62.4813 ], [ 179.3687, 62.4813 ], [ 179.3646, 62.4792 ], [ 179.3375, 62.4792 ], [ 179.3333, 62.4771 ], [ 179.3292, 62.4771 ], [ 179.3188, 62.4813 ], [ 179.3125, 62.4813 ], [ 179.3083, 62.4833 ], [ 179.2979, 62.4833 ], [ 179.2937, 62.4854 ], [ 179.2813, 62.4833 ], [ 179.2771, 62.4854 ], [ 179.2563, 62.4854 ], [ 179.2521, 62.4896 ], [ 179.2417, 62.4896 ], [ 179.2375, 62.4917 ], [ 179.2063, 62.4917 ], [ 179.1953, 62.4859 ], [ 179.1818, 62.4828 ], [ 179.1662, 62.4734 ], [ 179.1604, 62.4729 ], [ 179.1547, 62.4703 ], [ 179.1516, 62.4672 ], [ 179.1443, 62.4641 ], [ 179.1391, 62.4568 ], [ 179.1318, 62.4536 ], [ 179.1328, 62.4505 ], [ 179.1255, 62.4474 ], [ 179.1224, 62.4422 ], [ 179.1146, 62.4375 ], [ 179.1089, 62.4391 ], [ 179.1089, 62.4359 ], [ 179.1141, 62.4349 ], [ 179.1162, 62.4307 ], [ 179.1162, 62.4276 ], [ 179.113, 62.4255 ], [ 179.1182, 62.4245 ], [ 179.1151, 62.4224 ], [ 179.1172, 62.4172 ], [ 179.1328, 62.412 ], [ 179.1359, 62.4068 ], [ 179.15, 62.4021 ], [ 179.162, 62.4016 ], [ 179.1651, 62.3943 ], [ 179.1807, 62.3891 ], [ 179.1755, 62.3849 ], [ 179.1724, 62.3797 ], [ 179.1693, 62.3786 ], [ 179.1662, 62.3734 ], [ 179.1568, 62.3682 ], [ 179.1542, 62.3625 ], [ 179.1557, 62.3589 ], [ 179.1484, 62.3557 ], [ 179.1458, 62.3521 ], [ 179.1463, 62.3505 ], [ 179.1516, 62.3495 ], [ 179.1484, 62.3464 ], [ 179.1547, 62.3339 ], [ 179.1599, 62.3328 ], [ 179.1568, 62.3297 ], [ 179.1687, 62.325 ], [ 179.1766, 62.3245 ], [ 179.1828, 62.3203 ], [ 179.1807, 62.3172 ], [ 179.1734, 62.3141 ], [ 179.1745, 62.3089 ], [ 179.1651, 62.3036 ], [ 179.1662, 62.3005 ], [ 179.1568, 62.2953 ], [ 179.162, 62.2922 ], [ 179.1547, 62.2891 ], [ 179.1557, 62.2859 ], [ 179.1542, 62.2854 ], [ 179.1375, 62.2854 ], [ 179.1276, 62.2828 ], [ 179.1146, 62.275 ], [ 179.1021, 62.275 ], [ 179.0979, 62.2729 ], [ 179.0932, 62.2766 ], [ 179.0838, 62.2797 ], [ 179.0854, 62.2833 ], [ 179.0833, 62.2875 ], [ 179.087, 62.2911 ], [ 179.0818, 62.2922 ], [ 179.0771, 62.2958 ], [ 179.0609, 62.2964 ], [ 179.0589, 62.3005 ], [ 179.0547, 62.3026 ], [ 179.0599, 62.3047 ], [ 179.0604, 62.3063 ], [ 179.0578, 62.3099 ], [ 179.0547, 62.3109 ], [ 179.0557, 62.3161 ], [ 179.0505, 62.3172 ], [ 179.0537, 62.3203 ], [ 179.0443, 62.3214 ], [ 179.0474, 62.3245 ], [ 179.0422, 62.3276 ], [ 179.0432, 62.3307 ], [ 179.0339, 62.3318 ], [ 179.037, 62.3339 ], [ 179.037, 62.337 ], [ 179.0292, 62.3396 ], [ 179.0188, 62.3396 ], [ 179.0083, 62.3438 ], [ 178.9937, 62.3458 ], [ 178.9896, 62.3479 ], [ 178.9833, 62.3479 ], [ 178.9792, 62.35 ], [ 178.9688, 62.3479 ], [ 178.9563, 62.3521 ], [ 178.9354, 62.3521 ], [ 178.9125, 62.3583 ], [ 178.8979, 62.3583 ], [ 178.8938, 62.3604 ], [ 178.8859, 62.3609 ], [ 178.8813, 62.3646 ], [ 178.875, 62.3646 ], [ 178.8708, 62.3667 ], [ 178.8667, 62.3646 ], [ 178.8625, 62.3667 ], [ 178.8521, 62.3667 ], [ 178.8453, 62.3703 ], [ 178.8375, 62.3708 ], [ 178.8333, 62.3729 ], [ 178.8292, 62.3708 ], [ 178.8188, 62.3708 ], [ 178.7979, 62.3792 ], [ 178.7859, 62.3797 ], [ 178.7932, 62.3828 ], [ 178.7854, 62.3833 ], [ 178.7813, 62.3854 ], [ 178.7625, 62.3854 ], [ 178.7583, 62.3875 ], [ 178.7417, 62.3896 ], [ 178.7375, 62.3917 ], [ 178.7271, 62.3917 ], [ 178.7229, 62.3937 ], [ 178.7188, 62.3937 ], [ 178.7146, 62.3917 ], [ 178.7083, 62.3958 ], [ 178.6854, 62.3958 ], [ 178.6708, 62.4 ], [ 178.6604, 62.4 ], [ 178.6542, 62.4042 ], [ 178.6458, 62.4042 ], [ 178.6417, 62.4021 ], [ 178.6328, 62.4078 ], [ 178.6234, 62.4089 ], [ 178.6187, 62.4125 ], [ 178.6042, 62.4125 ], [ 178.6, 62.4146 ], [ 178.5958, 62.4125 ], [ 178.5854, 62.4125 ], [ 178.5729, 62.4167 ], [ 178.5562, 62.4167 ], [ 178.5537, 62.4203 ], [ 178.5417, 62.425 ], [ 178.5313, 62.425 ], [ 178.5271, 62.4271 ], [ 178.5167, 62.4271 ], [ 178.5125, 62.4292 ], [ 178.4917, 62.4292 ], [ 178.4521, 62.4396 ], [ 178.4479, 62.4375 ], [ 178.4438, 62.4375 ], [ 178.4292, 62.4417 ], [ 178.4229, 62.4417 ], [ 178.4083, 62.4479 ], [ 178.4021, 62.4479 ], [ 178.3917, 62.4521 ], [ 178.375, 62.4542 ], [ 178.3708, 62.4562 ], [ 178.3667, 62.4542 ], [ 178.3625, 62.4542 ], [ 178.3479, 62.4583 ], [ 178.3292, 62.4583 ], [ 178.3125, 62.4646 ], [ 178.2979, 62.4667 ], [ 178.2875, 62.4708 ], [ 178.2771, 62.4708 ], [ 178.2667, 62.475 ], [ 178.2563, 62.475 ], [ 178.2333, 62.4813 ], [ 178.2271, 62.4813 ], [ 178.2125, 62.4854 ], [ 178.2021, 62.4854 ], [ 178.1875, 62.4896 ], [ 178.1833, 62.4875 ], [ 178.1792, 62.4875 ], [ 178.1776, 62.488 ], [ 178.1787, 62.4911 ], [ 178.1589, 62.4943 ], [ 178.162, 62.4974 ], [ 178.1568, 62.4984 ], [ 178.1542, 62.5021 ], [ 178.1463, 62.5026 ], [ 178.1495, 62.5057 ], [ 178.1417, 62.5062 ], [ 178.1276, 62.5109 ], [ 178.1349, 62.5141 ], [ 178.1172, 62.5172 ], [ 178.1141, 62.5203 ], [ 178.1089, 62.5214 ], [ 178.1078, 62.5245 ], [ 178.0984, 62.5255 ], [ 178.1016, 62.5286 ], [ 178.0833, 62.5333 ], [ 178.075, 62.5333 ], [ 178.0708, 62.5313 ], [ 178.0646, 62.5333 ], [ 178.0583, 62.5333 ], [ 178.0542, 62.5354 ], [ 178.05, 62.5354 ], [ 178.0458, 62.5333 ], [ 178.0417, 62.5354 ], [ 178.0375, 62.5354 ], [ 178.0333, 62.5333 ], [ 178.0271, 62.5354 ], [ 178.0229, 62.5333 ], [ 178.0188, 62.5333 ], [ 178.0042, 62.5375 ], [ 178.0, 62.5354 ], [ 177.9958, 62.5354 ], [ 177.9917, 62.5375 ], [ 177.9708, 62.5375 ], [ 177.9667, 62.5396 ], [ 177.9625, 62.5375 ], [ 177.9583, 62.5396 ], [ 177.9396, 62.5396 ], [ 177.9354, 62.5417 ], [ 177.8958, 62.5417 ], [ 177.8917, 62.5438 ], [ 177.8875, 62.5438 ], [ 177.8833, 62.5417 ], [ 177.8687, 62.5458 ], [ 177.825, 62.5479 ], [ 177.8208, 62.55 ], [ 177.8021, 62.55 ], [ 177.7979, 62.5521 ], [ 177.7792, 62.5521 ], [ 177.775, 62.5542 ], [ 177.7375, 62.5542 ], [ 177.7333, 62.5563 ], [ 177.7271, 62.5563 ], [ 177.7229, 62.5542 ], [ 177.7188, 62.5563 ], [ 177.7, 62.5563 ], [ 177.6984, 62.5568 ], [ 177.6995, 62.5599 ], [ 177.6979, 62.5604 ], [ 177.6854, 62.5563 ], [ 177.6812, 62.5583 ], [ 177.6625, 62.5583 ], [ 177.6583, 62.5604 ], [ 177.6521, 62.5583 ], [ 177.6479, 62.5625 ], [ 177.6438, 62.5625 ], [ 177.6396, 62.5604 ], [ 177.5979, 62.5604 ], [ 177.5938, 62.5583 ], [ 177.5896, 62.5604 ], [ 177.5854, 62.5604 ], [ 177.5813, 62.5583 ], [ 177.5771, 62.5604 ], [ 177.5708, 62.5583 ], [ 177.5583, 62.5583 ], [ 177.5542, 62.5604 ], [ 177.55, 62.5583 ], [ 177.5208, 62.5583 ], [ 177.5167, 62.5604 ], [ 177.5125, 62.5583 ], [ 177.4729, 62.5583 ], [ 177.4688, 62.5563 ], [ 177.4333, 62.5563 ], [ 177.4271, 62.5583 ], [ 177.4229, 62.5563 ], [ 177.3479, 62.5563 ], [ 177.3438, 62.5542 ], [ 177.3208, 62.5542 ], [ 177.3167, 62.5521 ], [ 177.2708, 62.5521 ], [ 177.2729, 62.5542 ], [ 177.2979, 62.5542 ], [ 177.3021, 62.5563 ], [ 177.3391, 62.5568 ], [ 177.3375, 62.5583 ], [ 177.2917, 62.5583 ], [ 177.2875, 62.5563 ], [ 177.2667, 62.5563 ], [ 177.2625, 62.5542 ], [ 177.2271, 62.5542 ], [ 177.2229, 62.5521 ], [ 177.1958, 62.5521 ], [ 177.1917, 62.55 ], [ 177.1734, 62.5495 ], [ 177.1771, 62.5458 ], [ 177.1812, 62.5479 ], [ 177.2021, 62.5479 ], [ 177.2063, 62.55 ], [ 177.2375, 62.55 ], [ 177.2417, 62.5521 ], [ 177.2578, 62.5516 ], [ 177.2563, 62.55 ], [ 177.2437, 62.55 ], [ 177.2396, 62.5479 ], [ 177.1958, 62.5458 ], [ 177.1917, 62.5438 ], [ 177.1604, 62.5438 ], [ 177.1563, 62.5417 ], [ 177.1521, 62.5417 ], [ 177.1458, 62.5458 ], [ 177.1375, 62.5458 ], [ 177.1359, 62.5443 ], [ 177.1417, 62.5438 ], [ 177.1432, 62.5422 ], [ 177.1292, 62.5417 ], [ 177.125, 62.5396 ], [ 177.1021, 62.5396 ], [ 177.0995, 62.5432 ], [ 177.0917, 62.5438 ], [ 177.0854, 62.5396 ], [ 177.0729, 62.5396 ], [ 177.0688, 62.5375 ], [ 177.0458, 62.5375 ], [ 177.0333, 62.5333 ], [ 177.0146, 62.5313 ], [ 177.0104, 62.5292 ], [ 176.9958, 62.5292 ], [ 176.9943, 62.5276 ], [ 177.0021, 62.5271 ], [ 177.0036, 62.5255 ], [ 176.9937, 62.525 ], [ 176.9833, 62.5208 ], [ 176.9625, 62.5187 ], [ 176.9583, 62.5167 ], [ 176.9375, 62.5146 ], [ 176.9333, 62.5125 ], [ 176.9229, 62.5125 ], [ 176.9214, 62.5141 ], [ 176.9245, 62.5161 ], [ 176.9146, 62.5167 ], [ 176.9083, 62.5146 ], [ 176.9083, 62.5125 ], [ 176.9021, 62.5146 ], [ 176.8979, 62.5125 ], [ 176.8896, 62.5125 ], [ 176.8771, 62.5083 ], [ 176.8687, 62.5083 ], [ 176.8646, 62.5062 ], [ 176.8562, 62.5062 ], [ 176.8521, 62.5042 ], [ 176.8396, 62.5042 ], [ 176.838, 62.5026 ], [ 176.8542, 62.5 ], [ 176.8667, 62.5042 ], [ 176.8875, 62.5062 ], [ 176.8917, 62.5083 ], [ 176.8974, 62.5078 ], [ 176.8938, 62.5042 ], [ 176.8896, 62.5042 ], [ 176.8854, 62.5021 ], [ 176.8771, 62.5021 ], [ 176.8687, 62.4979 ], [ 176.8562, 62.4979 ], [ 176.85, 62.4938 ], [ 176.8208, 62.4875 ], [ 176.8104, 62.4833 ], [ 176.8021, 62.4833 ], [ 176.7813, 62.4771 ], [ 176.7729, 62.4771 ], [ 176.7625, 62.4729 ], [ 176.7568, 62.4724 ], [ 176.75, 62.4688 ], [ 176.7417, 62.4688 ], [ 176.7, 62.4562 ], [ 176.6922, 62.4568 ], [ 176.6964, 62.4599 ], [ 176.7036, 62.4609 ], [ 176.7068, 62.4641 ], [ 176.7167, 62.4646 ], [ 176.7224, 62.4672 ], [ 176.7172, 62.4703 ], [ 176.7229, 62.4729 ], [ 176.7312, 62.4729 ], [ 176.7354, 62.4708 ], [ 176.7396, 62.4708 ], [ 176.7479, 62.475 ], [ 176.7563, 62.475 ], [ 176.7604, 62.4771 ], [ 176.7646, 62.4771 ], [ 176.7693, 62.4807 ], [ 176.7766, 62.4818 ], [ 176.7833, 62.4854 ], [ 176.7875, 62.4833 ], [ 176.7953, 62.488 ], [ 176.7937, 62.4896 ], [ 176.7854, 62.4896 ], [ 176.7672, 62.4849 ], [ 176.7583, 62.4792 ], [ 176.75, 62.4792 ], [ 176.7458, 62.4771 ], [ 176.7292, 62.4771 ], [ 176.7167, 62.4729 ], [ 176.7109, 62.4734 ], [ 176.713, 62.4766 ], [ 176.7172, 62.4786 ], [ 176.7245, 62.4797 ], [ 176.7297, 62.487 ], [ 176.737, 62.488 ], [ 176.7359, 62.4911 ], [ 176.7417, 62.4938 ], [ 176.7516, 62.4943 ], [ 176.7495, 62.4974 ], [ 176.7437, 62.4979 ], [ 176.7229, 62.4917 ], [ 176.7208, 62.4938 ], [ 176.7234, 62.4974 ], [ 176.7307, 62.4984 ], [ 176.7479, 62.5062 ], [ 176.762, 62.5089 ], [ 176.7604, 62.5104 ], [ 176.7526, 62.5109 ], [ 176.7547, 62.5141 ], [ 176.7771, 62.5167 ], [ 176.787, 62.5193 ], [ 176.7891, 62.5214 ], [ 176.7849, 62.5245 ], [ 176.7734, 62.5276 ], [ 176.7667, 62.5313 ], [ 176.7604, 62.5313 ], [ 176.7563, 62.5292 ], [ 176.7312, 62.5292 ], [ 176.7104, 62.5229 ], [ 176.6979, 62.5229 ], [ 176.688, 62.5203 ], [ 176.6932, 62.5172 ], [ 176.6875, 62.5146 ], [ 176.6708, 62.5146 ], [ 176.6651, 62.512 ], [ 176.6662, 62.5089 ], [ 176.663, 62.5068 ], [ 176.6667, 62.5021 ], [ 176.6641, 62.4984 ], [ 176.6609, 62.4974 ], [ 176.6625, 62.4938 ], [ 176.6599, 62.4901 ], [ 176.6484, 62.4891 ], [ 176.6401, 62.4849 ], [ 176.6411, 62.4818 ], [ 176.6266, 62.4755 ], [ 176.6193, 62.4745 ], [ 176.6167, 62.4708 ], [ 176.6193, 62.4672 ], [ 176.6245, 62.4661 ], [ 176.6234, 62.4589 ], [ 176.6286, 62.4578 ], [ 176.6255, 62.4547 ], [ 176.6396, 62.4479 ], [ 176.6474, 62.4484 ], [ 176.6505, 62.4536 ], [ 176.6542, 62.4562 ], [ 176.6646, 62.4562 ], [ 176.6687, 62.4542 ], [ 176.6729, 62.4562 ], [ 176.6771, 62.4542 ], [ 176.6833, 62.4542 ], [ 176.6849, 62.4526 ], [ 176.675, 62.45 ], [ 176.6667, 62.45 ], [ 176.662, 62.4464 ], [ 176.6438, 62.4417 ], [ 176.6354, 62.4417 ], [ 176.6339, 62.4411 ], [ 176.6349, 62.438 ], [ 176.6333, 62.4375 ], [ 176.6125, 62.4354 ], [ 176.6062, 62.4313 ], [ 176.6021, 62.4313 ], [ 176.5896, 62.4271 ], [ 176.5813, 62.4271 ], [ 176.5766, 62.4234 ], [ 176.5667, 62.4208 ], [ 176.5583, 62.4208 ], [ 176.5437, 62.4146 ], [ 176.5297, 62.412 ], [ 176.525, 62.4083 ], [ 176.5151, 62.4078 ], [ 176.5141, 62.4047 ], [ 176.5104, 62.4021 ], [ 176.4922, 62.3995 ], [ 176.4849, 62.3943 ], [ 176.4776, 62.3932 ], [ 176.4787, 62.3901 ], [ 176.4688, 62.3896 ], [ 176.4588, 62.387 ], [ 176.4474, 62.3797 ], [ 176.438, 62.3766 ], [ 176.4245, 62.3672 ], [ 176.4172, 62.3661 ], [ 176.4099, 62.3609 ], [ 176.4026, 62.3599 ], [ 176.3953, 62.3547 ], [ 176.3932, 62.3505 ], [ 176.3859, 62.3495 ], [ 176.3828, 62.3443 ], [ 176.3734, 62.3411 ], [ 176.3745, 62.338 ], [ 176.362, 62.3297 ], [ 176.3562, 62.3271 ], [ 176.3505, 62.3266 ], [ 176.3516, 62.3234 ], [ 176.3422, 62.3182 ], [ 176.3432, 62.3151 ], [ 176.3276, 62.312 ], [ 176.3203, 62.3068 ], [ 176.313, 62.3057 ], [ 176.3083, 62.3021 ], [ 176.3026, 62.3016 ], [ 176.2995, 62.2984 ], [ 176.2937, 62.2979 ], [ 176.2854, 62.2938 ], [ 176.2792, 62.2938 ], [ 176.2708, 62.2896 ], [ 176.2667, 62.2896 ], [ 176.2625, 62.2875 ], [ 176.25, 62.2875 ], [ 176.2458, 62.2854 ], [ 176.2333, 62.2854 ], [ 176.2229, 62.2813 ], [ 176.2146, 62.2813 ], [ 176.2104, 62.2792 ], [ 176.2063, 62.2813 ], [ 176.2, 62.2792 ], [ 176.1833, 62.2792 ], [ 176.1792, 62.2771 ], [ 176.175, 62.2771 ], [ 176.1708, 62.2792 ], [ 176.1667, 62.2771 ], [ 176.1583, 62.2771 ], [ 176.1542, 62.275 ], [ 176.1333, 62.275 ], [ 176.1292, 62.2729 ], [ 176.1125, 62.2729 ], [ 176.1083, 62.2708 ], [ 176.1, 62.2708 ], [ 176.0958, 62.2687 ], [ 176.0917, 62.2687 ], [ 176.0875, 62.2667 ], [ 176.0792, 62.2667 ], [ 176.075, 62.2646 ], [ 176.0458, 62.2625 ], [ 176.0229, 62.2562 ], [ 176.0146, 62.2562 ], [ 176.0104, 62.2542 ], [ 175.9979, 62.2542 ], [ 175.9896, 62.25 ], [ 175.9854, 62.25 ], [ 175.9812, 62.2521 ], [ 175.9688, 62.2479 ], [ 175.9604, 62.2479 ], [ 175.9563, 62.2458 ], [ 175.9396, 62.2458 ], [ 175.9354, 62.2438 ], [ 175.9312, 62.2458 ], [ 175.9255, 62.2432 ], [ 175.9266, 62.2401 ], [ 175.9208, 62.2375 ], [ 175.9146, 62.2375 ], [ 175.8854, 62.2292 ], [ 175.8792, 62.2292 ], [ 175.8667, 62.225 ], [ 175.8583, 62.225 ], [ 175.8271, 62.2167 ], [ 175.8062, 62.2146 ], [ 175.8021, 62.2125 ], [ 175.7937, 62.2125 ], [ 175.7833, 62.2083 ], [ 175.775, 62.2083 ], [ 175.7604, 62.2021 ], [ 175.7521, 62.2021 ], [ 175.725, 62.1937 ], [ 175.7167, 62.1937 ], [ 175.7042, 62.1896 ], [ 175.6979, 62.1896 ], [ 175.6911, 62.1859 ], [ 175.6854, 62.1854 ], [ 175.675, 62.1813 ], [ 175.6667, 62.1813 ], [ 175.6599, 62.1776 ], [ 175.6521, 62.1771 ], [ 175.6438, 62.1729 ], [ 175.6292, 62.1708 ], [ 175.6208, 62.1667 ], [ 175.6125, 62.1667 ], [ 175.5979, 62.1604 ], [ 175.5792, 62.1583 ], [ 175.5708, 62.1542 ], [ 175.5547, 62.1516 ], [ 175.5495, 62.1484 ], [ 175.5359, 62.1453 ], [ 175.5292, 62.1417 ], [ 175.5208, 62.1417 ], [ 175.5161, 62.138 ], [ 175.5026, 62.1349 ], [ 175.4958, 62.1312 ], [ 175.4875, 62.1312 ], [ 175.4828, 62.1276 ], [ 175.4672, 62.1245 ], [ 175.4625, 62.1208 ], [ 175.4542, 62.1208 ], [ 175.4495, 62.1172 ], [ 175.4422, 62.1161 ], [ 175.4354, 62.1125 ], [ 175.4255, 62.113 ], [ 175.4287, 62.1161 ], [ 175.425, 62.1188 ], [ 175.4208, 62.1188 ], [ 175.4187, 62.1167 ], [ 175.4203, 62.113 ], [ 175.413, 62.112 ], [ 175.4141, 62.1089 ], [ 175.4083, 62.1063 ], [ 175.3813, 62.1063 ], [ 175.3771, 62.1083 ], [ 175.3646, 62.1083 ], [ 175.3604, 62.1063 ], [ 175.3521, 62.1063 ], [ 175.3479, 62.1042 ], [ 175.3438, 62.1042 ], [ 175.3391, 62.1005 ], [ 175.3318, 62.0995 ], [ 175.3266, 62.0922 ], [ 175.3208, 62.0896 ], [ 175.3167, 62.0896 ], [ 175.3125, 62.0917 ], [ 175.3068, 62.0901 ], [ 175.3141, 62.0974 ], [ 175.3083, 62.0979 ], [ 175.3042, 62.0958 ], [ 175.2943, 62.0964 ], [ 175.287, 62.1057 ], [ 175.2818, 62.1068 ], [ 175.2745, 62.112 ], [ 175.2693, 62.113 ], [ 175.2672, 62.1182 ], [ 175.2724, 62.1214 ], [ 175.275, 62.125 ], [ 175.2745, 62.1266 ], [ 175.2693, 62.1286 ], [ 175.2766, 62.1297 ], [ 175.2828, 62.1339 ], [ 175.2818, 62.137 ], [ 175.2849, 62.138 ], [ 175.287, 62.1411 ], [ 175.2813, 62.1417 ], [ 175.2651, 62.137 ], [ 175.2609, 62.1349 ], [ 175.262, 62.1318 ], [ 175.2589, 62.1307 ], [ 175.2599, 62.1255 ], [ 175.2526, 62.1203 ], [ 175.2542, 62.1167 ], [ 175.2526, 62.113 ], [ 175.262, 62.112 ], [ 175.2651, 62.1068 ], [ 175.2703, 62.1057 ], [ 175.2672, 62.1026 ], [ 175.2724, 62.1016 ], [ 175.2745, 62.0995 ], [ 175.2713, 62.0964 ], [ 175.2797, 62.0901 ], [ 175.2849, 62.0891 ], [ 175.2839, 62.0839 ], [ 175.2854, 62.0812 ], [ 175.2839, 62.0776 ], [ 175.287, 62.0766 ], [ 175.2859, 62.0745 ], [ 175.287, 62.0714 ], [ 175.2839, 62.0693 ], [ 175.2859, 62.0672 ], [ 175.2912, 62.0661 ], [ 175.2932, 62.0609 ], [ 175.2839, 62.0557 ], [ 175.2849, 62.0526 ], [ 175.2734, 62.0453 ], [ 175.2786, 62.0422 ], [ 175.2755, 62.0411 ], [ 175.2724, 62.0359 ], [ 175.263, 62.0349 ], [ 175.2599, 62.0297 ], [ 175.2536, 62.0255 ], [ 175.2464, 62.0245 ], [ 175.2474, 62.0214 ], [ 175.2339, 62.0161 ], [ 175.2349, 62.013 ], [ 175.2292, 62.0146 ], [ 175.2203, 62.0089 ], [ 175.2063, 62.0062 ], [ 175.1958, 62.0021 ], [ 175.1917, 62.0021 ], [ 175.1875, 62.0 ], [ 175.1792, 62.0 ], [ 175.1687, 61.9958 ], [ 175.1604, 61.9958 ], [ 175.1563, 61.9938 ], [ 175.1396, 61.9938 ], [ 175.1297, 61.9911 ], [ 175.1266, 61.988 ], [ 175.1208, 61.9875 ], [ 175.1104, 61.9833 ], [ 175.0979, 61.9813 ], [ 175.0875, 61.9771 ], [ 175.0792, 61.9771 ], [ 175.0729, 61.9729 ], [ 175.0604, 61.9729 ], [ 175.0479, 61.9688 ], [ 175.0313, 61.9688 ], [ 175.0271, 61.9667 ], [ 175.0146, 61.9667 ], [ 175.0104, 61.9646 ], [ 175.0021, 61.9646 ], [ 174.9979, 61.9625 ], [ 174.9771, 61.9625 ], [ 174.9729, 61.9604 ], [ 174.9667, 61.9604 ], [ 174.9583, 61.9562 ], [ 174.95, 61.9562 ], [ 174.9396, 61.9521 ], [ 174.9354, 61.9521 ], [ 174.9292, 61.9479 ], [ 174.9208, 61.9479 ], [ 174.9021, 61.9417 ], [ 174.8979, 61.9417 ], [ 174.8917, 61.9375 ], [ 174.8833, 61.9375 ], [ 174.8792, 61.9354 ], [ 174.875, 61.9354 ], [ 174.8542, 61.9271 ], [ 174.8458, 61.9271 ], [ 174.8443, 61.9266 ], [ 174.8453, 61.9234 ], [ 174.8354, 61.9229 ], [ 174.8313, 61.9208 ], [ 174.825, 61.9208 ], [ 174.825, 61.9229 ], [ 174.8276, 61.9245 ], [ 174.8349, 61.9255 ], [ 174.8297, 61.9276 ], [ 174.8276, 61.9307 ], [ 174.8396, 61.9313 ], [ 174.8438, 61.9292 ], [ 174.8479, 61.9313 ], [ 174.8479, 61.9333 ], [ 174.8599, 61.9339 ], [ 174.8641, 61.9411 ], [ 174.8542, 61.9396 ], [ 174.85, 61.9417 ], [ 174.8396, 61.9417 ], [ 174.8354, 61.9437 ], [ 174.8292, 61.9437 ], [ 174.825, 61.9458 ], [ 174.8104, 61.9458 ], [ 174.8062, 61.9479 ], [ 174.7875, 61.9479 ], [ 174.7646, 61.9396 ], [ 174.7583, 61.9396 ], [ 174.7542, 61.9375 ], [ 174.7396, 61.9375 ], [ 174.7349, 61.9432 ], [ 174.7297, 61.9443 ], [ 174.7287, 61.9474 ], [ 174.7172, 61.9505 ], [ 174.7151, 61.9536 ], [ 174.7182, 61.9557 ], [ 174.7068, 61.9589 ], [ 174.7099, 61.962 ], [ 174.7021, 61.9625 ], [ 174.6979, 61.9646 ], [ 174.6938, 61.9625 ], [ 174.6833, 61.9625 ], [ 174.6818, 61.9641 ], [ 174.6849, 61.9661 ], [ 174.6714, 61.9672 ], [ 174.6682, 61.9703 ], [ 174.663, 61.9714 ], [ 174.6662, 61.9745 ], [ 174.6583, 61.9771 ], [ 174.6375, 61.975 ], [ 174.6318, 61.9714 ], [ 174.6411, 61.9703 ], [ 174.6401, 61.9672 ], [ 174.6453, 61.9661 ], [ 174.6484, 61.9609 ], [ 174.6578, 61.9578 ], [ 174.6526, 61.9557 ], [ 174.6547, 61.9526 ], [ 174.6604, 61.95 ], [ 174.6682, 61.9495 ], [ 174.6672, 61.9464 ], [ 174.6724, 61.9453 ], [ 174.6755, 61.9401 ], [ 174.6807, 61.9391 ], [ 174.6839, 61.9339 ], [ 174.6891, 61.9328 ], [ 174.6901, 61.9297 ], [ 174.6979, 61.9292 ], [ 174.7057, 61.9266 ], [ 174.7047, 61.9214 ], [ 174.7161, 61.9182 ], [ 174.713, 61.9151 ], [ 174.7229, 61.9146 ], [ 174.7229, 61.9125 ], [ 174.7287, 61.912 ], [ 174.7312, 61.9083 ], [ 174.7297, 61.9047 ], [ 174.7417, 61.9 ], [ 174.7495, 61.8995 ], [ 174.7484, 61.8964 ], [ 174.75, 61.8958 ], [ 174.762, 61.8953 ], [ 174.7589, 61.8932 ], [ 174.7599, 61.8901 ], [ 174.7505, 61.887 ], [ 174.7474, 61.8818 ], [ 174.7396, 61.8812 ], [ 174.7339, 61.8786 ], [ 174.7349, 61.8755 ], [ 174.7318, 61.8734 ], [ 174.7349, 61.8693 ], [ 174.7318, 61.8682 ], [ 174.7328, 61.8651 ], [ 174.7297, 61.8641 ], [ 174.7307, 61.8609 ], [ 174.7167, 61.8521 ], [ 174.7042, 61.8521 ], [ 174.6938, 61.8479 ], [ 174.6854, 61.8479 ], [ 174.6625, 61.8417 ], [ 174.6542, 61.8417 ], [ 174.6479, 61.8375 ], [ 174.638, 61.837 ], [ 174.6391, 61.8339 ], [ 174.6375, 61.8333 ], [ 174.6167, 61.8312 ], [ 174.612, 61.8276 ], [ 174.6062, 61.8271 ], [ 174.5958, 61.8229 ], [ 174.5875, 61.8229 ], [ 174.5833, 61.8208 ], [ 174.575, 61.8208 ], [ 174.5688, 61.8229 ], [ 174.5646, 61.8208 ], [ 174.5542, 61.8208 ], [ 174.55, 61.8187 ], [ 174.5375, 61.8187 ], [ 174.5333, 61.8167 ], [ 174.525, 61.8167 ], [ 174.5188, 61.8125 ], [ 174.5089, 61.812 ], [ 174.5099, 61.8089 ], [ 174.5083, 61.8083 ], [ 174.4729, 61.8083 ], [ 174.4688, 61.8063 ], [ 174.4604, 61.8063 ], [ 174.4557, 61.8099 ], [ 174.4464, 61.8109 ], [ 174.4495, 61.8141 ], [ 174.4443, 61.8151 ], [ 174.4333, 61.8229 ], [ 174.4292, 61.8208 ], [ 174.4, 61.8209 ], [ 174.4365, 61.8253 ], [ 174.4429, 61.8247 ], [ 174.4517, 61.8403 ], [ 174.4555, 61.872 ], [ 174.3844, 61.9018 ], [ 174.3804, 61.9174 ], [ 174.3609, 61.9642 ], [ 174.2802, 61.9958 ], [ 174.2788, 62.0449 ], [ 174.2378, 62.0793 ], [ 174.1187, 62.0654 ], [ 174.0506, 62.0887 ], [ 174.0596, 62.1552 ], [ 174.0293, 62.2097 ], [ 173.925, 62.2386 ], [ 174.001, 62.2936 ], [ 174.092, 62.3294 ], [ 174.0423, 62.4049 ], [ 173.9549, 62.4364 ], [ 173.8285, 62.4406 ], [ 173.7817, 62.484 ], [ 173.6733, 62.4997 ], [ 173.5315, 62.4762 ], [ 173.3978, 62.4679 ], [ 173.2303, 62.431 ], [ 173.0799, 62.4066 ], [ 173.0209, 62.3824 ], [ 172.9518, 62.3596 ], [ 172.8507, 62.3473 ], [ 172.7692, 62.3105 ], [ 172.6524, 62.358 ], [ 172.4659, 62.3917 ], [ 172.3315, 62.3779 ], [ 172.2712, 62.3503 ], [ 172.1084, 62.3467 ], [ 172.0599, 62.3889 ], [ 171.9394, 62.412 ], [ 171.8632, 62.3921 ], [ 171.7907, 62.3623 ], [ 171.7502, 62.3642 ], [ 171.6849, 62.3341 ], [ 171.6804, 62.3063 ], [ 171.4599, 62.2826 ], [ 171.3826, 62.252 ], [ 171.2309, 62.2324 ], [ 171.2513, 62.2792 ], [ 171.2171, 62.2838 ], [ 171.1623, 62.2541 ], [ 171.1103, 62.2473 ], [ 171.1244, 62.3087 ], [ 171.0914, 62.32 ], [ 171.0335, 62.3065 ], [ 171.0, 62.2619 ], [ 170.9046, 62.2563 ], [ 170.9012, 62.2173 ], [ 170.8203, 62.2046 ], [ 170.7277, 62.2046 ], [ 170.6984, 62.1703 ], [ 170.6083, 62.173 ], [ 170.4969, 62.2127 ], [ 170.2996, 62.2329 ], [ 170.221, 62.2504 ], [ 170.1605, 62.2122 ], [ 170.0892, 62.1967 ], [ 170.0296, 62.2206 ], [ 170.0263, 62.2593 ], [ 169.9841, 62.2873 ], [ 169.9906, 62.3202 ], [ 169.9122, 62.3626 ], [ 169.9368, 62.4036 ], [ 169.8353, 62.4527 ], [ 169.8644, 62.5263 ], [ 169.8242, 62.5806 ], [ 169.717, 62.5847 ], [ 169.6312, 62.6231 ], [ 169.539, 62.6211 ], [ 169.4996, 62.655 ], [ 169.4207, 62.7169 ], [ 169.2331, 62.7743 ], [ 169.1533, 62.8165 ], [ 168.9528, 62.8754 ], [ 168.7969, 62.8581 ], [ 168.621, 62.8913 ], [ 168.5539, 62.9384 ], [ 168.4328, 62.9581 ], [ 168.2931, 63.0071 ], [ 168.3037, 63.0516 ], [ 168.4001, 63.0685 ], [ 168.4346, 63.1022 ], [ 168.5598, 63.1143 ], [ 168.6293, 63.1647 ], [ 168.6827, 63.1776 ], [ 168.681, 63.248 ], [ 168.7619, 63.2728 ], [ 168.8067, 63.3288 ], [ 168.9422, 63.3517 ], [ 169.0288, 63.4217 ], [ 169.1136, 63.454 ], [ 169.1675, 63.4582 ], [ 169.2846, 63.4734 ], [ 169.3906, 63.5134 ], [ 169.3798, 63.5384 ], [ 169.4471, 63.5808 ], [ 169.4083, 63.6178 ], [ 169.3596, 63.708 ], [ 169.3337, 63.7713 ], [ 169.1896, 63.8099 ], [ 169.2, 63.8678 ], [ 169.1047, 63.8895 ], [ 169.0063, 63.8862 ], [ 168.8627, 63.9001 ], [ 168.8248, 63.9319 ], [ 168.7186, 63.9756 ], [ 168.6651, 64.0521 ], [ 168.6419, 64.148 ], [ 168.4447, 64.1674 ], [ 168.378, 64.2089 ], [ 168.3424, 64.2909 ], [ 168.152, 64.2711 ], [ 168.0975, 64.2872 ], [ 167.9995, 64.2481 ], [ 167.8439, 64.2305 ], [ 167.7513, 64.2441 ], [ 167.7332, 64.2949 ], [ 167.5929, 64.2741 ], [ 167.5258, 64.2866 ], [ 167.5082, 64.3333 ], [ 167.5121, 64.3839 ], [ 167.3527, 64.4192 ], [ 167.2494, 64.4681 ], [ 167.1438, 64.5406 ], [ 167.0955, 64.5874 ], [ 167.0269, 64.578 ], [ 166.9971, 64.554 ], [ 166.9236, 64.5132 ], [ 166.7624, 64.4844 ], [ 166.6383, 64.4939 ], [ 166.4733, 64.4715 ], [ 166.4106, 64.4955 ], [ 166.3093, 64.5514 ], [ 166.1408, 64.5647 ], [ 166.0182, 64.5865 ], [ 165.9154, 64.5227 ], [ 165.8049, 64.5124 ], [ 165.7336, 64.5312 ], [ 165.7625, 64.5677 ], [ 165.7714, 64.5897 ], [ 165.7221, 64.6351 ], [ 165.7074, 64.6869 ], [ 165.6529, 64.7142 ], [ 165.4988, 64.7099 ], [ 165.2849, 64.7232 ], [ 165.262, 64.759 ], [ 165.2035, 64.798 ], [ 165.1119, 64.8129 ], [ 164.9519, 64.7896 ], [ 164.9069, 64.7544 ], [ 164.9494, 64.7147 ], [ 164.9543, 64.6698 ], [ 164.8727, 64.6438 ], [ 164.8349, 64.6584 ], [ 164.8165, 64.6966 ], [ 164.7691, 64.7318 ], [ 164.7055, 64.7634 ], [ 164.5859, 64.7642 ], [ 164.5374, 64.7898 ], [ 164.3794, 64.8388 ], [ 164.1886, 64.8888 ], [ 164.058, 64.8896 ], [ 163.9656, 64.8553 ], [ 163.7948, 64.8523 ], [ 163.7145, 64.884 ], [ 163.5941, 64.8886 ], [ 163.5322, 64.9113 ], [ 163.5023, 64.8849 ], [ 163.421, 64.8338 ], [ 163.3151, 64.7842 ], [ 163.2082, 64.7927 ], [ 163.1582, 64.7768 ], [ 163.1531, 64.7419 ], [ 163.2292, 64.709 ], [ 163.2343, 64.6692 ], [ 163.1175, 64.6639 ], [ 162.9806, 64.6615 ], [ 162.7915, 64.6612 ], [ 162.7085, 64.6886 ], [ 162.6429, 64.733 ], [ 162.5819, 64.7549 ], [ 162.4776, 64.7533 ], [ 162.1087, 64.7897 ], [ 161.9075, 64.8499 ], [ 161.7822, 64.9402 ], [ 161.686, 65.0433 ], [ 161.534, 65.0546 ], [ 161.3069, 65.124 ], [ 161.0819, 65.1518 ], [ 160.8217, 65.1521 ], [ 160.6401, 65.1753 ], [ 160.4563, 65.2527 ], [ 160.2253, 65.381 ], [ 160.1884, 65.4614 ], [ 160.1321, 65.5 ], [ 159.9993, 65.5091 ], [ 159.8512, 65.5252 ], [ 159.7687, 65.5783 ], [ 159.6137, 65.6045 ], [ 159.3952, 65.6245 ], [ 159.1305, 65.6744 ], [ 158.9682, 65.7502 ], [ 158.9613, 65.8833 ], [ 158.9783, 65.9901 ], [ 159.0472, 66.0624 ], [ 159.2323, 66.1658 ], [ 159.244, 66.2025 ], [ 159.1385, 66.2013 ], [ 158.9713, 66.2086 ], [ 158.7463, 66.2663 ], [ 158.7094, 66.3131 ], [ 158.5754, 66.3175 ], [ 158.496, 66.3599 ], [ 158.4982, 66.3842 ], [ 158.456, 66.426 ], [ 158.575, 66.4594 ], [ 158.6126, 66.5244 ], [ 158.6846, 66.5783 ], [ 158.7884, 66.6278 ], [ 158.7785, 66.6767 ], [ 158.8444, 66.7297 ], [ 158.9966, 66.7639 ], [ 158.9762, 66.7984 ], [ 158.8171, 66.878 ], [ 158.8314, 66.9161 ], [ 158.8595, 66.9705 ], [ 158.8251, 67.0324 ], [ 158.665, 67.0683 ], [ 158.5216, 67.0919 ], [ 158.3474, 67.1028 ], [ 158.285, 67.1477 ], [ 158.2075, 67.162 ], [ 158.1546, 67.2039 ], [ 157.9698, 67.2269 ], [ 157.9402, 67.2554 ], [ 157.9573, 67.3028 ], [ 157.9683, 67.3709 ], [ 157.8513, 67.4528 ], [ 157.8305, 67.5441 ], [ 157.8715, 67.6032 ], [ 157.882, 67.6797 ], [ 158.043, 67.6944 ], [ 158.1691, 67.6862 ], [ 158.3071, 67.7092 ], [ 158.3614, 67.7449 ], [ 158.352, 67.7644 ], [ 158.2256, 67.7897 ], [ 158.1702, 67.8416 ], [ 158.2582, 67.8816 ], [ 158.3189, 67.9145 ], [ 158.3237, 67.9623 ], [ 158.4063, 68.0456 ], [ 158.5896, 68.0956 ], [ 158.8066, 68.0987 ], [ 158.959, 68.0871 ], [ 159.1177, 68.1509 ], [ 159.167, 68.1571 ], [ 159.2491, 68.1736 ], [ 159.3466, 68.2117 ], [ 159.3774, 68.205 ], [ 159.4313, 68.1761 ], [ 159.5322, 68.1746 ], [ 159.5887, 68.1903 ], [ 159.6517, 68.1829 ], [ 159.744, 68.1636 ], [ 159.8111, 68.2235 ], [ 159.888, 68.2542 ], [ 159.9906, 68.264 ], [ 160.2979, 68.257 ], [ 160.7359, 68.2356 ], [ 160.95, 68.2677 ], [ 161.2655, 68.3737 ], [ 161.4075, 68.3858 ], [ 162.0237, 68.3163 ], [ 162.4006, 68.2514 ], [ 162.4817, 68.302 ], [ 162.4745, 68.3857 ], [ 162.5325, 68.4251 ], [ 162.5203, 68.5213 ], [ 162.6427, 68.5488 ], [ 162.628, 68.5916 ], [ 162.6772, 68.6386 ], [ 162.564, 68.6707 ], [ 162.5514, 68.7277 ], [ 162.6756, 68.7571 ], [ 162.773, 68.8195 ], [ 162.7201, 68.8505 ], [ 162.6706, 68.8986 ], [ 162.5591, 68.9099 ], [ 162.4992, 68.9227 ], [ 162.439, 68.9634 ], [ 162.4635, 69.0724 ], [ 162.5053, 69.1128 ], [ 162.7056, 69.1291 ], [ 162.7004, 69.1583 ], [ 162.4026, 69.2932 ], [ 162.4133, 69.3401 ], [ 162.39, 69.369 ], [ 162.2619, 69.3917 ], [ 162.2991, 69.4422 ], [ 162.1947, 69.4963 ], [ 162.2938, 69.5133 ], [ 162.3121, 69.546 ], [ 162.3679, 69.5878 ], [ 162.3555, 69.6288 ], [ 162.3594, 69.6289 ], [ 162.3696, 69.6391 ], [ 162.385, 69.6467 ], [ 162.3829, 69.654 ], [ 162.3849, 69.6557 ], [ 162.3776, 69.663 ], [ 162.3807, 69.6651 ], [ 162.3786, 69.6682 ], [ 162.363, 69.6713 ], [ 162.3609, 69.6734 ], [ 162.3625, 69.675 ], [ 162.3854, 69.6729 ], [ 162.3963, 69.663 ], [ 162.4125, 69.6583 ], [ 162.4375, 69.6563 ], [ 162.4417, 69.6542 ], [ 162.4479, 69.6563 ], [ 162.4542, 69.6542 ], [ 162.4583, 69.6563 ], [ 162.4646, 69.6542 ], [ 162.4729, 69.6563 ], [ 162.4745, 69.6547 ], [ 162.4714, 69.6526 ], [ 162.4776, 69.6464 ], [ 162.4849, 69.6453 ], [ 162.4896, 69.6417 ], [ 162.5036, 69.6391 ], [ 162.5068, 69.6359 ], [ 162.5146, 69.6333 ], [ 162.5229, 69.6333 ], [ 162.5245, 69.6349 ], [ 162.5224, 69.637 ], [ 162.5151, 69.638 ], [ 162.5099, 69.6432 ], [ 162.5047, 69.6443 ], [ 162.5016, 69.6474 ], [ 162.4901, 69.6464 ], [ 162.4932, 69.6495 ], [ 162.488, 69.6557 ], [ 162.5313, 69.6563 ], [ 162.5396, 69.6604 ], [ 162.5453, 69.6609 ], [ 162.5464, 69.6641 ], [ 162.55, 69.6667 ], [ 162.5688, 69.6667 ], [ 162.5833, 69.6708 ], [ 162.5896, 69.6708 ], [ 162.5938, 69.6687 ], [ 162.6187, 69.6667 ], [ 162.6229, 69.6646 ], [ 162.6396, 69.6646 ], [ 162.6458, 69.6583 ], [ 162.6667, 69.6542 ], [ 162.6729, 69.65 ], [ 162.7083, 69.6417 ], [ 162.7146, 69.6375 ], [ 162.7229, 69.6396 ], [ 162.7292, 69.6354 ], [ 162.7458, 69.6354 ], [ 162.75, 69.6333 ], [ 162.7625, 69.6333 ], [ 162.7667, 69.6354 ], [ 162.7792, 69.6354 ], [ 162.7875, 69.6396 ], [ 162.8062, 69.6396 ], [ 162.8104, 69.6375 ], [ 162.8271, 69.6375 ], [ 162.8313, 69.6354 ], [ 162.8687, 69.6354 ], [ 162.8729, 69.6375 ], [ 162.8807, 69.638 ], [ 162.8875, 69.6417 ], [ 162.8932, 69.6422 ], [ 162.9167, 69.6542 ], [ 162.9583, 69.6542 ], [ 162.9625, 69.6563 ], [ 162.975, 69.6563 ], [ 162.9833, 69.6604 ], [ 162.9937, 69.6604 ], [ 162.9979, 69.6625 ], [ 163.025, 69.6667 ], [ 163.0292, 69.6687 ], [ 163.0396, 69.6687 ], [ 163.0437, 69.6708 ], [ 163.0521, 69.6708 ], [ 163.0583, 69.6687 ], [ 163.0625, 69.6708 ], [ 163.0896, 69.6729 ], [ 163.1042, 69.6771 ], [ 163.1104, 69.675 ], [ 163.1187, 69.675 ], [ 163.1203, 69.6755 ], [ 163.1193, 69.6787 ], [ 163.125, 69.6813 ], [ 163.1313, 69.6813 ], [ 163.138, 69.6849 ], [ 163.1479, 69.6875 ], [ 163.1771, 69.6896 ], [ 163.1854, 69.6937 ], [ 163.1958, 69.6937 ], [ 163.1974, 69.6943 ], [ 163.1964, 69.6974 ], [ 163.1979, 69.6979 ], [ 163.2104, 69.6979 ], [ 163.2146, 69.7 ], [ 163.2271, 69.7 ], [ 163.2312, 69.7021 ], [ 163.2667, 69.7021 ], [ 163.2708, 69.7042 ], [ 163.2917, 69.7042 ], [ 163.2958, 69.7021 ], [ 163.3, 69.7042 ], [ 163.3271, 69.7042 ], [ 163.3313, 69.7021 ], [ 163.3417, 69.7021 ], [ 163.3458, 69.7 ], [ 163.3542, 69.7 ], [ 163.3583, 69.6979 ], [ 163.3687, 69.6979 ], [ 163.3813, 69.6937 ], [ 163.3958, 69.6937 ], [ 163.4005, 69.6901 ], [ 163.4187, 69.6854 ], [ 163.4396, 69.6854 ], [ 163.4578, 69.6807 ], [ 163.4625, 69.6771 ], [ 163.4667, 69.6771 ], [ 163.4708, 69.675 ], [ 163.4792, 69.6771 ], [ 163.4896, 69.6729 ], [ 163.4979, 69.675 ], [ 163.5021, 69.6729 ], [ 163.5354, 69.6729 ], [ 163.5396, 69.6708 ], [ 163.5458, 69.6729 ], [ 163.5542, 69.6729 ], [ 163.5583, 69.6708 ], [ 163.6792, 69.6708 ], [ 163.6833, 69.6729 ], [ 163.7146, 69.6729 ], [ 163.7188, 69.675 ], [ 163.7417, 69.675 ], [ 163.7458, 69.6771 ], [ 163.7521, 69.675 ], [ 163.7583, 69.6771 ], [ 163.7875, 69.6771 ], [ 163.7917, 69.6792 ], [ 163.8042, 69.6792 ], [ 163.8083, 69.6813 ], [ 163.8146, 69.6813 ], [ 163.8292, 69.6854 ], [ 163.8396, 69.6854 ], [ 163.8479, 69.6896 ], [ 163.8542, 69.6896 ], [ 163.8583, 69.6917 ], [ 163.8662, 69.6922 ], [ 163.8729, 69.6958 ], [ 163.8792, 69.6958 ], [ 163.8943, 69.7037 ], [ 163.9021, 69.7042 ], [ 163.913, 69.7099 ], [ 163.9203, 69.7109 ], [ 163.9578, 69.7297 ], [ 163.9568, 69.7328 ], [ 163.9641, 69.7359 ], [ 163.963, 69.7391 ], [ 163.9672, 69.7412 ], [ 163.9854, 69.7438 ], [ 163.9912, 69.7464 ], [ 163.9839, 69.7495 ], [ 163.9917, 69.75 ], [ 163.9958, 69.7521 ], [ 164.0083, 69.7521 ], [ 164.0125, 69.7542 ], [ 164.0208, 69.7542 ], [ 164.025, 69.7521 ], [ 164.0333, 69.7521 ], [ 164.0375, 69.75 ], [ 164.0458, 69.75 ], [ 164.05, 69.7521 ], [ 164.0562, 69.75 ], [ 164.1021, 69.75 ], [ 164.1062, 69.7479 ], [ 164.1292, 69.7479 ], [ 164.137, 69.7453 ], [ 164.1474, 69.7338 ], [ 164.1396, 69.7333 ], [ 164.138, 69.7328 ], [ 164.1391, 69.7297 ], [ 164.1297, 69.7286 ], [ 164.1339, 69.7234 ], [ 164.1479, 69.725 ], [ 164.1521, 69.7271 ], [ 164.1563, 69.725 ], [ 164.1667, 69.725 ], [ 164.1766, 69.7214 ], [ 164.1687, 69.7208 ], [ 164.1672, 69.7203 ], [ 164.1682, 69.7172 ], [ 164.1604, 69.7167 ], [ 164.1563, 69.7146 ], [ 164.125, 69.7167 ], [ 164.1193, 69.7141 ], [ 164.1208, 69.7125 ], [ 164.1266, 69.712 ], [ 164.1297, 69.7068 ], [ 164.1396, 69.7063 ], [ 164.1411, 69.7068 ], [ 164.1401, 69.7099 ], [ 164.1583, 69.7125 ], [ 164.1625, 69.7146 ], [ 164.1787, 69.7141 ], [ 164.1755, 69.712 ], [ 164.1776, 69.7068 ], [ 164.1812, 69.7042 ], [ 164.187, 69.7037 ], [ 164.1839, 69.7005 ], [ 164.1995, 69.6974 ], [ 164.2026, 69.6922 ], [ 164.2099, 69.6911 ], [ 164.213, 69.688 ], [ 164.2188, 69.6875 ], [ 164.2234, 69.6839 ], [ 164.2307, 69.6828 ], [ 164.2422, 69.6755 ], [ 164.2495, 69.6745 ], [ 164.2542, 69.6708 ], [ 164.2599, 69.6703 ], [ 164.262, 69.6672 ], [ 164.2589, 69.6661 ], [ 164.2589, 69.663 ], [ 164.2625, 69.6625 ], [ 164.2688, 69.6646 ], [ 164.2703, 69.663 ], [ 164.2672, 69.662 ], [ 164.2688, 69.6604 ], [ 164.2792, 69.6604 ], [ 164.2917, 69.6563 ], [ 164.3, 69.6563 ], [ 164.3062, 69.6521 ], [ 164.3286, 69.6474 ], [ 164.3313, 69.6438 ], [ 164.3521, 69.6396 ], [ 164.3599, 69.637 ], [ 164.3646, 69.6333 ], [ 164.3786, 69.6307 ], [ 164.3833, 69.6271 ], [ 164.3917, 69.6271 ], [ 164.3963, 69.6234 ], [ 164.4187, 69.6188 ], [ 164.4292, 69.6146 ], [ 164.4349, 69.6141 ], [ 164.437, 69.612 ], [ 164.4287, 69.6068 ], [ 164.4193, 69.6057 ], [ 164.4208, 69.6042 ], [ 164.4375, 69.6042 ], [ 164.4479, 69.6 ], [ 164.4667, 69.6042 ], [ 164.4729, 69.6 ], [ 164.4875, 69.6 ], [ 164.4974, 69.5974 ], [ 164.5021, 69.5938 ], [ 164.525, 69.5917 ], [ 164.5297, 69.588 ], [ 164.5396, 69.5854 ], [ 164.55, 69.5854 ], [ 164.5604, 69.5813 ], [ 164.5833, 69.5813 ], [ 164.5875, 69.5792 ], [ 164.5958, 69.5792 ], [ 164.6, 69.5771 ], [ 164.6229, 69.5771 ], [ 164.6271, 69.575 ], [ 164.6479, 69.575 ], [ 164.6521, 69.5729 ], [ 164.6729, 69.5729 ], [ 164.6771, 69.5708 ], [ 164.7, 69.5708 ], [ 164.7042, 69.5687 ], [ 164.7104, 69.5708 ], [ 164.7146, 69.5687 ], [ 164.7312, 69.5687 ], [ 164.7354, 69.5667 ], [ 164.75, 69.5667 ], [ 164.7542, 69.5646 ], [ 164.7604, 69.5667 ], [ 164.7667, 69.5646 ], [ 164.7937, 69.5646 ], [ 164.7979, 69.5625 ], [ 164.8146, 69.5625 ], [ 164.8188, 69.5604 ], [ 164.8458, 69.5604 ], [ 164.85, 69.5583 ], [ 164.8562, 69.5604 ], [ 164.8625, 69.5583 ], [ 164.9, 69.5583 ], [ 164.9042, 69.5604 ], [ 164.9104, 69.5583 ], [ 164.9146, 69.5604 ], [ 164.9229, 69.5604 ], [ 164.9292, 69.5583 ], [ 164.9333, 69.5604 ], [ 164.9563, 69.5604 ], [ 164.9604, 69.5625 ], [ 164.9854, 69.5625 ], [ 164.9896, 69.5646 ], [ 165.0063, 69.5646 ], [ 165.0104, 69.5667 ], [ 165.0229, 69.5667 ], [ 165.0271, 69.5687 ], [ 165.0583, 69.5687 ], [ 165.0625, 69.5708 ], [ 165.0729, 69.5708 ], [ 165.0771, 69.5729 ], [ 165.1083, 69.5729 ], [ 165.1125, 69.575 ], [ 165.1292, 69.575 ], [ 165.1438, 69.5792 ], [ 165.1625, 69.5792 ], [ 165.1667, 69.5813 ], [ 165.1771, 69.5813 ], [ 165.1812, 69.5833 ], [ 165.1875, 69.5833 ], [ 165.1917, 69.5854 ], [ 165.2042, 69.5854 ], [ 165.2083, 69.5875 ], [ 165.2182, 69.587 ], [ 165.2167, 69.5854 ], [ 165.2104, 69.5854 ], [ 165.2047, 69.5828 ], [ 165.2083, 69.5792 ], [ 165.2161, 69.5797 ], [ 165.2188, 69.5833 ], [ 165.2292, 69.5833 ], [ 165.2437, 69.5875 ], [ 165.25, 69.5854 ], [ 165.2646, 69.5854 ], [ 165.2708, 69.5875 ], [ 165.275, 69.5854 ], [ 165.3146, 69.5854 ], [ 165.325, 69.5813 ], [ 165.3479, 69.5813 ], [ 165.3521, 69.5792 ], [ 165.3667, 69.5792 ], [ 165.3708, 69.5771 ], [ 165.3854, 69.5771 ], [ 165.3896, 69.575 ], [ 165.4125, 69.575 ], [ 165.4167, 69.5729 ], [ 165.4375, 69.5729 ], [ 165.4417, 69.5708 ], [ 165.5, 69.5708 ], [ 165.5042, 69.5687 ], [ 165.5083, 69.5708 ], [ 165.5146, 69.5687 ], [ 165.5292, 69.5687 ], [ 165.5333, 69.5708 ], [ 165.5417, 69.5708 ], [ 165.5458, 69.5687 ], [ 165.55, 69.5708 ], [ 165.6104, 69.5708 ], [ 165.6146, 69.5729 ], [ 165.6208, 69.5708 ], [ 165.6271, 69.5729 ], [ 165.6333, 69.5708 ], [ 165.6458, 69.5708 ], [ 165.65, 69.5729 ], [ 165.6583, 69.5729 ], [ 165.6625, 69.5708 ], [ 165.6687, 69.5729 ], [ 165.6938, 69.5729 ], [ 165.6979, 69.5708 ], [ 165.7042, 69.5729 ], [ 165.7188, 69.5729 ], [ 165.725, 69.5708 ], [ 165.7292, 69.5729 ], [ 165.7354, 69.5729 ], [ 165.7396, 69.5708 ], [ 165.7563, 69.5708 ], [ 165.7604, 69.5687 ], [ 165.7667, 69.5708 ], [ 165.8042, 69.5708 ], [ 165.8083, 69.5687 ], [ 165.8167, 69.5687 ], [ 165.8208, 69.5667 ], [ 165.8417, 69.5667 ], [ 165.8458, 69.5646 ], [ 165.8562, 69.5646 ], [ 165.8604, 69.5625 ], [ 165.8708, 69.5625 ], [ 165.875, 69.5604 ], [ 165.8896, 69.5604 ], [ 165.9021, 69.5563 ], [ 165.9167, 69.5563 ], [ 165.9208, 69.5542 ], [ 165.9312, 69.5542 ], [ 165.9417, 69.55 ], [ 165.9521, 69.55 ], [ 165.9563, 69.5479 ], [ 165.9604, 69.5479 ], [ 165.9708, 69.5437 ], [ 165.9812, 69.5437 ], [ 165.9875, 69.5396 ], [ 166.0104, 69.5375 ], [ 166.0292, 69.5313 ], [ 166.0521, 69.5292 ], [ 166.0646, 69.525 ], [ 166.0729, 69.525 ], [ 166.0771, 69.5229 ], [ 166.0875, 69.5229 ], [ 166.0917, 69.5208 ], [ 166.1146, 69.5188 ], [ 166.1187, 69.5167 ], [ 166.1333, 69.5167 ], [ 166.1375, 69.5146 ], [ 166.1479, 69.5146 ], [ 166.1521, 69.5125 ], [ 166.1667, 69.5125 ], [ 166.1708, 69.5104 ], [ 166.1875, 69.5104 ], [ 166.1917, 69.5083 ], [ 166.2125, 69.5083 ], [ 166.2167, 69.5062 ], [ 166.25, 69.5062 ], [ 166.2542, 69.5042 ], [ 166.2604, 69.5062 ], [ 166.2646, 69.5042 ], [ 166.3042, 69.5042 ], [ 166.3083, 69.5021 ], [ 166.3417, 69.5021 ], [ 166.3458, 69.5 ], [ 166.4042, 69.5 ], [ 166.4083, 69.4979 ], [ 166.4646, 69.4979 ], [ 166.4688, 69.4958 ], [ 166.4875, 69.4958 ], [ 166.4917, 69.4979 ], [ 166.5458, 69.4979 ], [ 166.55, 69.5 ], [ 166.5583, 69.4979 ], [ 166.5625, 69.5 ], [ 166.5917, 69.5 ], [ 166.5958, 69.5021 ], [ 166.6146, 69.5021 ], [ 166.6187, 69.5042 ], [ 166.6292, 69.5042 ], [ 166.6333, 69.5062 ], [ 166.6625, 69.5083 ], [ 166.6771, 69.5125 ], [ 166.6958, 69.5125 ], [ 166.7, 69.5146 ], [ 166.7099, 69.512 ], [ 166.7141, 69.5078 ], [ 166.7083, 69.5042 ], [ 166.6896, 69.5042 ], [ 166.6854, 69.5021 ], [ 166.6729, 69.5021 ], [ 166.6687, 69.5 ], [ 166.6458, 69.5 ], [ 166.6417, 69.4979 ], [ 166.6354, 69.5 ], [ 166.6255, 69.4995 ], [ 166.6271, 69.4979 ], [ 166.6328, 69.4974 ], [ 166.6297, 69.4943 ], [ 166.6333, 69.4917 ], [ 166.6375, 69.4938 ], [ 166.6563, 69.4938 ], [ 166.6578, 69.4943 ], [ 166.6568, 69.4974 ], [ 166.662, 69.4974 ], [ 166.6667, 69.4917 ], [ 166.663, 69.4891 ], [ 166.6641, 69.4859 ], [ 166.6609, 69.4849 ], [ 166.662, 69.4818 ], [ 166.6563, 69.4792 ], [ 166.6479, 69.4792 ], [ 166.6318, 69.4838 ], [ 166.6245, 69.4912 ], [ 166.6193, 69.4922 ], [ 166.6172, 69.4943 ], [ 166.6203, 69.4974 ], [ 166.6167, 69.5 ], [ 166.6062, 69.4958 ], [ 166.6021, 69.4979 ], [ 166.5938, 69.4979 ], [ 166.5896, 69.4958 ], [ 166.5771, 69.4958 ], [ 166.5714, 69.4922 ], [ 166.5813, 69.4875 ], [ 166.6, 69.4833 ], [ 166.6016, 69.4849 ], [ 166.5995, 69.487 ], [ 166.5838, 69.4901 ], [ 166.5838, 69.4932 ], [ 166.6042, 69.4938 ], [ 166.6141, 69.4912 ], [ 166.6172, 69.4859 ], [ 166.6245, 69.4849 ], [ 166.6203, 69.4818 ], [ 166.6125, 69.4812 ], [ 166.6109, 69.4797 ], [ 166.6208, 69.4792 ], [ 166.625, 69.4771 ], [ 166.6354, 69.4771 ], [ 166.6396, 69.475 ], [ 166.6557, 69.4745 ], [ 166.6542, 69.4729 ], [ 166.6479, 69.4729 ], [ 166.6438, 69.4708 ], [ 166.6271, 69.4708 ], [ 166.6229, 69.4688 ], [ 166.6167, 69.4708 ], [ 166.6104, 69.4688 ], [ 166.5813, 69.4688 ], [ 166.5755, 69.4661 ], [ 166.5771, 69.4646 ], [ 166.5833, 69.4646 ], [ 166.5875, 69.4667 ], [ 166.6021, 69.4667 ], [ 166.6062, 69.4646 ], [ 166.6125, 69.4667 ], [ 166.6187, 69.4667 ], [ 166.6229, 69.4646 ], [ 166.6375, 69.4646 ], [ 166.6417, 69.4667 ], [ 166.6604, 69.4667 ], [ 166.6646, 69.4688 ], [ 166.6708, 69.4688 ], [ 166.6849, 69.4755 ], [ 166.6833, 69.4771 ], [ 166.6776, 69.4776 ], [ 166.6755, 69.4797 ], [ 166.6755, 69.4828 ], [ 166.6787, 69.4838 ], [ 166.6776, 69.487 ], [ 166.6807, 69.488 ], [ 166.6797, 69.4912 ], [ 166.6875, 69.4917 ], [ 166.6958, 69.4958 ], [ 166.7021, 69.4958 ], [ 166.7167, 69.5 ], [ 166.725, 69.5 ], [ 166.7292, 69.4979 ], [ 166.7437, 69.4979 ], [ 166.7479, 69.4958 ], [ 166.7583, 69.4958 ], [ 166.7688, 69.4917 ], [ 166.7745, 69.4912 ], [ 166.7713, 69.4891 ], [ 166.7734, 69.4859 ], [ 166.7896, 69.4854 ], [ 166.7937, 69.4833 ], [ 166.8, 69.4854 ], [ 166.8104, 69.4854 ], [ 166.8146, 69.4875 ], [ 166.8521, 69.4875 ], [ 166.8562, 69.4896 ], [ 166.8625, 69.4896 ], [ 166.8667, 69.4875 ], [ 166.8896, 69.4875 ], [ 166.8938, 69.4854 ], [ 166.9083, 69.4854 ], [ 166.912, 69.4818 ], [ 166.9042, 69.4812 ], [ 166.8984, 69.4786 ], [ 166.8979, 69.4771 ], [ 166.9047, 69.4693 ], [ 166.9083, 69.4688 ], [ 166.9141, 69.4714 ], [ 166.913, 69.4745 ], [ 166.9187, 69.4771 ], [ 166.9604, 69.4771 ], [ 166.9646, 69.4792 ], [ 166.9708, 69.4792 ], [ 166.9854, 69.4833 ], [ 166.9917, 69.4833 ], [ 167.0083, 69.4917 ], [ 167.0375, 69.4938 ], [ 167.0391, 69.4943 ], [ 167.038, 69.4974 ], [ 167.0422, 69.4995 ], [ 167.05, 69.5 ], [ 167.0609, 69.5057 ], [ 167.0688, 69.5062 ], [ 167.0771, 69.5104 ], [ 167.0896, 69.5104 ], [ 167.1005, 69.5162 ], [ 167.1099, 69.5172 ], [ 167.1292, 69.5271 ], [ 167.1417, 69.5271 ], [ 167.1484, 69.5307 ], [ 167.1563, 69.5313 ], [ 167.1708, 69.5375 ], [ 167.1787, 69.538 ], [ 167.1896, 69.5437 ], [ 167.2125, 69.5437 ], [ 167.2141, 69.5443 ], [ 167.213, 69.5474 ], [ 167.2208, 69.55 ], [ 167.2328, 69.5505 ], [ 167.2328, 69.5537 ], [ 167.2255, 69.5547 ], [ 167.2234, 69.5578 ], [ 167.2328, 69.5589 ], [ 167.2318, 69.562 ], [ 167.2333, 69.5625 ], [ 167.2396, 69.5625 ], [ 167.25, 69.5667 ], [ 167.2578, 69.5672 ], [ 167.262, 69.5693 ], [ 167.2609, 69.5724 ], [ 167.2651, 69.5745 ], [ 167.2729, 69.575 ], [ 167.2922, 69.5849 ], [ 167.3, 69.5854 ], [ 167.312, 69.5901 ], [ 167.338, 69.6036 ], [ 167.3474, 69.6047 ], [ 167.3516, 69.6068 ], [ 167.3526, 69.6099 ], [ 167.3604, 69.6104 ], [ 167.3687, 69.6146 ], [ 167.375, 69.6146 ], [ 167.3833, 69.6188 ], [ 167.3911, 69.6193 ], [ 167.3901, 69.6224 ], [ 167.4255, 69.6391 ], [ 167.4354, 69.6396 ], [ 167.4396, 69.6375 ], [ 167.4432, 69.638 ], [ 167.4474, 69.6412 ], [ 167.4401, 69.6422 ], [ 167.438, 69.6453 ], [ 167.4458, 69.6458 ], [ 167.4536, 69.6484 ], [ 167.4729, 69.6583 ], [ 167.4807, 69.6589 ], [ 167.5213, 69.6787 ], [ 167.5307, 69.6797 ], [ 167.5589, 69.6932 ], [ 167.5682, 69.6943 ], [ 167.6089, 69.7141 ], [ 167.6167, 69.7167 ], [ 167.6245, 69.7172 ], [ 167.6286, 69.7193 ], [ 167.6276, 69.7224 ], [ 167.6318, 69.7245 ], [ 167.6396, 69.725 ], [ 167.6479, 69.7292 ], [ 167.6557, 69.7297 ], [ 167.6568, 69.7328 ], [ 167.6641, 69.7359 ], [ 167.663, 69.7391 ], [ 167.6708, 69.7396 ], [ 167.6854, 69.7458 ], [ 167.6995, 69.7464 ], [ 167.6984, 69.7495 ], [ 167.7078, 69.7505 ], [ 167.7068, 69.7536 ], [ 167.7141, 69.7568 ], [ 167.7167, 69.7604 ], [ 167.7229, 69.7604 ], [ 167.7312, 69.7646 ], [ 167.7375, 69.7646 ], [ 167.7458, 69.7688 ], [ 167.7604, 69.7688 ], [ 167.7646, 69.7667 ], [ 167.7708, 69.7688 ], [ 167.7958, 69.7688 ], [ 167.8021, 69.7667 ], [ 167.8062, 69.7688 ], [ 167.8188, 69.7688 ], [ 167.8229, 69.7708 ], [ 167.8729, 69.7708 ], [ 167.8854, 69.7667 ], [ 167.8938, 69.7667 ], [ 167.8979, 69.7646 ], [ 167.9141, 69.7651 ], [ 167.9125, 69.7667 ], [ 167.9005, 69.7672 ], [ 167.9063, 69.7708 ], [ 167.9146, 69.7708 ], [ 167.9208, 69.7667 ], [ 167.9271, 69.7688 ], [ 167.9417, 69.7688 ], [ 167.9495, 69.7662 ], [ 167.9547, 69.7589 ], [ 167.9688, 69.7562 ], [ 167.9792, 69.7521 ], [ 167.9896, 69.7521 ], [ 167.9937, 69.75 ], [ 168.0083, 69.75 ], [ 168.0125, 69.7479 ], [ 168.0333, 69.7479 ], [ 168.0375, 69.7458 ], [ 168.0479, 69.7458 ], [ 168.0557, 69.7432 ], [ 168.0578, 69.7412 ], [ 168.0547, 69.7391 ], [ 168.0568, 69.7359 ], [ 168.062, 69.7349 ], [ 168.0667, 69.7312 ], [ 168.0724, 69.7307 ], [ 168.0646, 69.7271 ], [ 168.0568, 69.7266 ], [ 168.0641, 69.7161 ], [ 168.0609, 69.7141 ], [ 168.062, 69.7109 ], [ 168.0568, 69.7089 ], [ 168.0703, 69.7078 ], [ 168.0672, 69.7057 ], [ 168.0672, 69.7026 ], [ 168.0745, 69.7016 ], [ 168.0766, 69.6984 ], [ 168.0734, 69.6964 ], [ 168.0786, 69.6953 ], [ 168.0859, 69.6859 ], [ 168.0932, 69.6849 ], [ 168.0995, 69.6734 ], [ 168.0917, 69.6708 ], [ 168.088, 69.6745 ], [ 168.0932, 69.6766 ], [ 168.075, 69.6813 ], [ 168.0667, 69.6813 ], [ 168.0604, 69.6854 ], [ 168.0479, 69.6854 ], [ 168.0266, 69.6755 ], [ 168.0172, 69.6745 ], [ 168.0182, 69.6713 ], [ 168.013, 69.6703 ], [ 167.9964, 69.662 ], [ 167.9974, 69.6589 ], [ 167.9922, 69.6578 ], [ 167.9797, 69.6516 ], [ 167.9807, 69.6484 ], [ 167.9583, 69.6479 ], [ 167.9521, 69.6521 ], [ 167.9443, 69.6495 ], [ 167.9401, 69.6464 ], [ 167.9474, 69.6453 ], [ 167.9521, 69.6417 ], [ 167.962, 69.6412 ], [ 167.9651, 69.638 ], [ 167.9703, 69.637 ], [ 167.9672, 69.6339 ], [ 167.9792, 69.6333 ], [ 167.987, 69.6307 ], [ 167.9901, 69.6276 ], [ 167.9974, 69.6266 ], [ 167.9984, 69.6234 ], [ 168.0057, 69.6224 ], [ 168.0089, 69.6172 ], [ 168.0203, 69.6182 ], [ 168.0172, 69.6151 ], [ 168.0271, 69.6146 ], [ 168.0339, 69.6088 ], [ 168.0474, 69.6057 ], [ 168.0505, 69.6026 ], [ 168.0583, 69.6 ], [ 168.0724, 69.5974 ], [ 168.0734, 69.5943 ], [ 168.1099, 69.5849 ], [ 168.113, 69.5818 ], [ 168.1182, 69.5807 ], [ 168.1229, 69.5771 ], [ 168.1333, 69.5771 ], [ 168.1583, 69.5687 ], [ 168.1687, 69.5687 ], [ 168.1729, 69.5667 ], [ 168.1812, 69.5667 ], [ 168.1854, 69.5646 ], [ 168.2021, 69.5646 ], [ 168.2063, 69.5625 ], [ 168.2146, 69.5625 ], [ 168.2188, 69.5604 ], [ 168.2271, 69.5604 ], [ 168.2312, 69.5583 ], [ 168.2542, 69.5563 ], [ 168.2703, 69.5516 ], [ 168.2755, 69.5463 ], [ 168.2807, 69.5453 ], [ 168.2849, 69.5411 ], [ 168.2813, 69.5375 ], [ 168.2828, 69.5339 ], [ 168.2755, 69.5307 ], [ 168.2766, 69.5276 ], [ 168.2713, 69.5266 ], [ 168.2724, 69.5234 ], [ 168.2693, 69.5224 ], [ 168.2724, 69.5151 ], [ 168.2651, 69.512 ], [ 168.2703, 69.5099 ], [ 168.2672, 69.5068 ], [ 168.2745, 69.5057 ], [ 168.2708, 69.5021 ], [ 168.2766, 69.4932 ], [ 168.2734, 69.4912 ], [ 168.2745, 69.488 ], [ 168.2713, 69.487 ], [ 168.2766, 69.4849 ], [ 168.2734, 69.4828 ], [ 168.2745, 69.4776 ], [ 168.2713, 69.4766 ], [ 168.2724, 69.4734 ], [ 168.2693, 69.4724 ], [ 168.2703, 69.4693 ], [ 168.2651, 69.4682 ], [ 168.2661, 69.4651 ], [ 168.2589, 69.462 ], [ 168.2641, 69.4589 ], [ 168.2526, 69.4537 ], [ 168.2557, 69.4464 ], [ 168.2484, 69.4432 ], [ 168.2495, 69.4401 ], [ 168.2401, 69.437 ], [ 168.2453, 69.4297 ], [ 168.2401, 69.4287 ], [ 168.2412, 69.4255 ], [ 168.2354, 69.4229 ], [ 168.2292, 69.425 ], [ 168.2229, 69.4229 ], [ 168.2167, 69.425 ], [ 168.2042, 69.425 ], [ 168.2026, 69.4234 ], [ 168.2063, 69.4208 ], [ 168.2391, 69.4203 ], [ 168.2359, 69.4182 ], [ 168.2391, 69.413 ], [ 168.2297, 69.412 ], [ 168.2349, 69.4089 ], [ 168.2234, 69.4036 ], [ 168.225, 69.4 ], [ 168.2214, 69.3964 ], [ 168.2245, 69.3922 ], [ 168.2208, 69.3896 ], [ 168.2245, 69.3839 ], [ 168.2151, 69.3807 ], [ 168.2203, 69.3745 ], [ 168.2151, 69.3714 ], [ 168.2224, 69.3703 ], [ 168.2193, 69.3682 ], [ 168.2224, 69.3609 ], [ 168.2193, 69.3599 ], [ 168.2203, 69.3547 ], [ 168.2172, 69.3536 ], [ 168.2208, 69.3479 ], [ 168.2172, 69.3443 ], [ 168.2229, 69.3438 ], [ 168.2245, 69.3422 ], [ 168.2214, 69.3411 ], [ 168.2203, 69.338 ], [ 168.2172, 69.3359 ], [ 168.2245, 69.3349 ], [ 168.2208, 69.3313 ], [ 168.2287, 69.3203 ], [ 168.2255, 69.3182 ], [ 168.2287, 69.312 ], [ 168.2255, 69.3099 ], [ 168.2287, 69.3026 ], [ 168.2255, 69.3005 ], [ 168.2328, 69.2995 ], [ 168.2328, 69.2963 ], [ 168.2297, 69.2943 ], [ 168.2318, 69.2922 ], [ 168.237, 69.2911 ], [ 168.2339, 69.288 ], [ 168.238, 69.2839 ], [ 168.2432, 69.2828 ], [ 168.2401, 69.2797 ], [ 168.2443, 69.2755 ], [ 168.2495, 69.2745 ], [ 168.2464, 69.2724 ], [ 168.2484, 69.2672 ], [ 168.2547, 69.2609 ], [ 168.2599, 69.2599 ], [ 168.2568, 69.2578 ], [ 168.2589, 69.2526 ], [ 168.2646, 69.2521 ], [ 168.2682, 69.2495 ], [ 168.2682, 69.2464 ], [ 168.2609, 69.2422 ], [ 168.2745, 69.2412 ], [ 168.2818, 69.2297 ], [ 168.2891, 69.2286 ], [ 168.2901, 69.2255 ], [ 168.2974, 69.2245 ], [ 168.3089, 69.2172 ], [ 168.3141, 69.2161 ], [ 168.3172, 69.213 ], [ 168.3229, 69.2125 ], [ 168.3417, 69.2063 ], [ 168.3792, 69.2063 ], [ 168.3833, 69.2042 ], [ 168.4229, 69.2042 ], [ 168.4271, 69.2021 ], [ 168.4771, 69.2021 ], [ 168.4812, 69.2 ], [ 168.5813, 69.2 ], [ 168.5854, 69.2021 ], [ 168.5938, 69.2 ], [ 168.5979, 69.2021 ], [ 168.6583, 69.2021 ], [ 168.6625, 69.2 ], [ 168.6708, 69.2 ], [ 168.675, 69.1979 ], [ 168.7146, 69.1979 ], [ 168.7172, 69.1943 ], [ 168.7245, 69.1932 ], [ 168.7292, 69.1896 ], [ 168.75, 69.1875 ], [ 168.7542, 69.1854 ], [ 168.7688, 69.1854 ], [ 168.7875, 69.1792 ], [ 168.8104, 69.1771 ], [ 168.8151, 69.1734 ], [ 168.8229, 69.1708 ], [ 168.8375, 69.1708 ], [ 168.8438, 69.1667 ], [ 168.8521, 69.1667 ], [ 168.8562, 69.1646 ], [ 168.8708, 69.1646 ], [ 168.875, 69.1625 ], [ 168.8917, 69.1625 ], [ 168.8958, 69.1583 ], [ 168.9063, 69.1583 ], [ 168.9089, 69.1547 ], [ 168.9162, 69.1536 ], [ 168.9172, 69.1505 ], [ 168.9245, 69.1495 ], [ 168.9276, 69.1443 ], [ 168.9411, 69.1432 ], [ 168.9422, 69.1401 ], [ 168.9495, 69.1391 ], [ 168.9542, 69.1354 ], [ 168.975, 69.1333 ], [ 168.9875, 69.1292 ], [ 168.9958, 69.1292 ], [ 169.0063, 69.125 ], [ 169.012, 69.1245 ], [ 169.0151, 69.1193 ], [ 169.0286, 69.1182 ], [ 169.0297, 69.1151 ], [ 169.037, 69.1141 ], [ 169.0396, 69.1104 ], [ 169.0562, 69.1104 ], [ 169.0703, 69.1057 ], [ 169.0766, 69.0995 ], [ 169.0776, 69.0964 ], [ 169.0849, 69.0953 ], [ 169.0859, 69.0922 ], [ 169.0932, 69.0911 ], [ 169.0964, 69.088 ], [ 169.1042, 69.0854 ], [ 169.1187, 69.0854 ], [ 169.1229, 69.0833 ], [ 169.15, 69.0833 ], [ 169.1542, 69.0813 ], [ 169.1854, 69.0813 ], [ 169.1979, 69.0771 ], [ 169.2125, 69.0771 ], [ 169.2167, 69.075 ], [ 169.225, 69.075 ], [ 169.2292, 69.0729 ], [ 169.2437, 69.0729 ], [ 169.2479, 69.075 ], [ 169.2813, 69.075 ], [ 169.2854, 69.0771 ], [ 169.3042, 69.0771 ], [ 169.3083, 69.0792 ], [ 169.3349, 69.0787 ], [ 169.337, 69.0755 ], [ 169.3338, 69.0745 ], [ 169.3338, 69.0714 ], [ 169.3411, 69.0703 ], [ 169.3422, 69.0672 ], [ 169.3495, 69.0661 ], [ 169.3526, 69.0609 ], [ 169.3687, 69.0604 ], [ 169.3734, 69.0568 ], [ 169.3813, 69.0542 ], [ 169.3917, 69.0542 ], [ 169.3932, 69.0526 ], [ 169.388, 69.0505 ], [ 169.3938, 69.05 ], [ 169.3984, 69.0463 ], [ 169.4036, 69.0453 ], [ 169.4005, 69.0422 ], [ 169.4026, 69.0401 ], [ 169.4078, 69.0391 ], [ 169.4047, 69.0359 ], [ 169.4099, 69.0349 ], [ 169.4141, 69.0307 ], [ 169.4141, 69.0276 ], [ 169.4109, 69.0255 ], [ 169.413, 69.0234 ], [ 169.4182, 69.0224 ], [ 169.4151, 69.0193 ], [ 169.4224, 69.0182 ], [ 169.4245, 69.0141 ], [ 169.4214, 69.012 ], [ 169.4234, 69.0068 ], [ 169.4266, 69.0036 ], [ 169.4234, 69.0005 ], [ 169.4287, 68.9995 ], [ 169.4307, 68.9974 ], [ 169.4307, 68.9943 ], [ 169.4276, 68.9922 ], [ 169.4328, 68.9838 ], [ 169.4292, 68.9812 ], [ 169.4391, 68.9661 ], [ 169.4359, 68.9641 ], [ 169.437, 68.9609 ], [ 169.4339, 68.9599 ], [ 169.4391, 68.9578 ], [ 169.4359, 68.9547 ], [ 169.4417, 68.9458 ], [ 169.438, 68.9422 ], [ 169.4453, 68.9411 ], [ 169.4474, 68.9359 ], [ 169.4443, 68.9349 ], [ 169.4453, 68.9297 ], [ 169.4422, 68.9287 ], [ 169.4432, 68.9255 ], [ 169.4401, 68.9245 ], [ 169.4411, 68.9193 ], [ 169.4297, 68.9141 ], [ 169.4287, 68.9109 ], [ 169.4214, 68.9078 ], [ 169.4224, 68.9047 ], [ 169.413, 68.9016 ], [ 169.4141, 68.8984 ], [ 169.4109, 68.8974 ], [ 169.412, 68.8943 ], [ 169.4089, 68.8932 ], [ 169.4099, 68.888 ], [ 169.4068, 68.887 ], [ 169.4068, 68.8839 ], [ 169.4109, 68.8776 ], [ 169.4182, 68.8766 ], [ 169.4151, 68.8745 ], [ 169.4167, 68.8708 ], [ 169.413, 68.8682 ], [ 169.4141, 68.8651 ], [ 169.4104, 68.8625 ], [ 169.4151, 68.8544 ], [ 169.4193, 68.8505 ], [ 169.4245, 68.8495 ], [ 169.4297, 68.8422 ], [ 169.437, 68.8411 ], [ 169.4505, 68.8193 ], [ 169.4547, 68.8151 ], [ 169.4599, 68.8141 ], [ 169.463, 68.8089 ], [ 169.4703, 68.8078 ], [ 169.4734, 68.8047 ], [ 169.4812, 68.8021 ], [ 169.4912, 68.8016 ], [ 169.4958, 68.7979 ], [ 169.5042, 68.7979 ], [ 169.5083, 68.7958 ], [ 169.5167, 68.7958 ], [ 169.5292, 68.7917 ], [ 169.55, 68.7896 ], [ 169.5547, 68.7859 ], [ 169.5599, 68.7849 ], [ 169.563, 68.7818 ], [ 169.5682, 68.7807 ], [ 169.5734, 68.7755 ], [ 169.5833, 68.775 ], [ 169.6021, 68.7688 ], [ 169.6104, 68.7688 ], [ 169.6146, 68.7667 ], [ 169.6396, 68.7667 ], [ 169.6438, 68.7646 ], [ 169.65, 68.7667 ], [ 169.6563, 68.7646 ], [ 169.7208, 68.7646 ], [ 169.725, 68.7667 ], [ 169.7312, 68.7667 ], [ 169.7375, 68.7646 ], [ 169.7417, 68.7667 ], [ 169.8042, 68.7688 ], [ 169.8083, 68.7708 ], [ 169.8313, 68.7708 ], [ 169.8354, 68.7729 ], [ 169.8583, 68.7729 ], [ 169.8625, 68.775 ], [ 169.8854, 68.775 ], [ 169.8896, 68.7771 ], [ 169.9083, 68.7771 ], [ 169.9125, 68.7792 ], [ 169.925, 68.7792 ], [ 169.9375, 68.7833 ], [ 169.95, 68.7833 ], [ 169.9542, 68.7854 ], [ 169.9667, 68.7854 ], [ 169.9729, 68.7813 ], [ 169.9792, 68.7813 ], [ 169.987, 68.7839 ], [ 169.9859, 68.787 ], [ 170.0, 68.7875 ], [ 170.0083, 68.7917 ], [ 170.0146, 68.7917 ], [ 170.0188, 68.7896 ], [ 170.0271, 68.7896 ], [ 170.0313, 68.7917 ], [ 170.0729, 68.7917 ], [ 170.0771, 68.7896 ], [ 170.0917, 68.7896 ], [ 170.0917, 68.7917 ], [ 170.1, 68.7917 ], [ 170.1042, 68.7937 ], [ 170.1333, 68.7937 ], [ 170.1375, 68.7958 ], [ 170.15, 68.7958 ], [ 170.1542, 68.7979 ], [ 170.1646, 68.7979 ], [ 170.1687, 68.8 ], [ 170.1979, 68.8021 ], [ 170.2063, 68.8063 ], [ 170.2167, 68.8063 ], [ 170.2312, 68.8104 ], [ 170.2375, 68.8104 ], [ 170.2432, 68.813 ], [ 170.2443, 68.8161 ], [ 170.2625, 68.8187 ], [ 170.2708, 68.8229 ], [ 170.2771, 68.8229 ], [ 170.288, 68.8287 ], [ 170.2974, 68.8297 ], [ 170.3125, 68.8375 ], [ 170.3313, 68.8375 ], [ 170.3562, 68.8292 ], [ 170.3682, 68.8287 ], [ 170.3714, 68.8234 ], [ 170.3766, 68.8224 ], [ 170.3818, 68.8172 ], [ 170.3932, 68.8141 ], [ 170.3963, 68.8109 ], [ 170.4063, 68.8104 ], [ 170.4109, 68.8068 ], [ 170.4162, 68.8057 ], [ 170.4224, 68.7963 ], [ 170.413, 68.7953 ], [ 170.412, 68.7922 ], [ 170.4089, 68.7911 ], [ 170.4109, 68.788 ], [ 170.4162, 68.787 ], [ 170.4089, 68.7828 ], [ 170.4109, 68.7797 ], [ 170.4187, 68.7792 ], [ 170.4203, 68.7797 ], [ 170.4193, 68.7828 ], [ 170.4375, 68.7854 ], [ 170.4458, 68.7896 ], [ 170.4646, 68.7896 ], [ 170.4688, 68.7917 ], [ 170.475, 68.7917 ], [ 170.4833, 68.7958 ], [ 170.4896, 68.7958 ], [ 170.4953, 68.7984 ], [ 170.4901, 68.8016 ], [ 170.4958, 68.8042 ], [ 170.5016, 68.7995 ], [ 170.5026, 68.7963 ], [ 170.5099, 68.7953 ], [ 170.5068, 68.7922 ], [ 170.512, 68.7911 ], [ 170.5151, 68.7859 ], [ 170.5224, 68.7849 ], [ 170.5172, 68.7828 ], [ 170.5213, 68.7755 ], [ 170.5286, 68.7745 ], [ 170.5297, 68.7714 ], [ 170.537, 68.7703 ], [ 170.538, 68.7672 ], [ 170.5417, 68.7646 ], [ 170.5578, 68.7641 ], [ 170.5547, 68.762 ], [ 170.5547, 68.7589 ], [ 170.562, 68.7578 ], [ 170.5651, 68.7526 ], [ 170.5786, 68.7495 ], [ 170.575, 68.7458 ], [ 170.5776, 68.7401 ], [ 170.5833, 68.7396 ], [ 170.5938, 68.7354 ], [ 170.6083, 68.7354 ], [ 170.6208, 68.7312 ], [ 170.6271, 68.7312 ], [ 170.6286, 68.7328 ], [ 170.6234, 68.7338 ], [ 170.6187, 68.7375 ], [ 170.5979, 68.7375 ], [ 170.5838, 68.7432 ], [ 170.587, 68.7443 ], [ 170.5859, 68.7474 ], [ 170.5875, 68.7479 ], [ 170.6, 68.7479 ], [ 170.6042, 68.7458 ], [ 170.6104, 68.7479 ], [ 170.6187, 68.7479 ], [ 170.6229, 68.7458 ], [ 170.6307, 68.7495 ], [ 170.6208, 68.7521 ], [ 170.6083, 68.7521 ], [ 170.6042, 68.75 ], [ 170.5958, 68.75 ], [ 170.5917, 68.7521 ], [ 170.5693, 68.7547 ], [ 170.5724, 68.7578 ], [ 170.5672, 68.7589 ], [ 170.5651, 68.7609 ], [ 170.5688, 68.7646 ], [ 170.5661, 68.7703 ], [ 170.5542, 68.7708 ], [ 170.5464, 68.7734 ], [ 170.5412, 68.7807 ], [ 170.5359, 68.7818 ], [ 170.5339, 68.7839 ], [ 170.537, 68.7859 ], [ 170.537, 68.7891 ], [ 170.5297, 68.7901 ], [ 170.5224, 68.8037 ], [ 170.5151, 68.8047 ], [ 170.513, 68.8089 ], [ 170.5146, 68.8104 ], [ 170.5437, 68.8104 ], [ 170.55, 68.8083 ], [ 170.5516, 68.8089 ], [ 170.5516, 68.812 ], [ 170.5396, 68.8125 ], [ 170.5354, 68.8146 ], [ 170.5104, 68.8146 ], [ 170.5063, 68.8167 ], [ 170.4854, 68.8167 ], [ 170.4812, 68.8187 ], [ 170.4667, 68.8187 ], [ 170.4625, 68.8208 ], [ 170.4479, 68.8208 ], [ 170.4464, 68.8224 ], [ 170.4516, 68.8234 ], [ 170.4479, 68.8271 ], [ 170.4401, 68.8245 ], [ 170.4411, 68.8214 ], [ 170.4396, 68.8208 ], [ 170.4208, 68.8208 ], [ 170.4167, 68.8229 ], [ 170.4021, 68.8229 ], [ 170.3974, 68.8266 ], [ 170.3922, 68.8276 ], [ 170.3953, 68.8307 ], [ 170.3771, 68.8333 ], [ 170.3547, 68.8411 ], [ 170.3646, 68.8458 ], [ 170.3771, 68.8458 ], [ 170.3813, 68.8479 ], [ 170.4083, 68.85 ], [ 170.4229, 68.8542 ], [ 170.4307, 68.8544 ], [ 170.4318, 68.856 ], [ 170.4396, 68.8562 ], [ 170.4479, 68.8604 ], [ 170.475, 68.8646 ], [ 170.4833, 68.8688 ], [ 170.4958, 68.8688 ], [ 170.4974, 68.8693 ], [ 170.4964, 68.8724 ], [ 170.5005, 68.8745 ], [ 170.5083, 68.875 ], [ 170.5188, 68.8792 ], [ 170.5229, 68.8792 ], [ 170.5484, 68.8912 ], [ 170.5578, 68.8922 ], [ 170.5589, 68.8953 ], [ 170.6109, 68.9203 ], [ 170.625, 68.9229 ], [ 170.6349, 68.9203 ], [ 170.6359, 68.9172 ], [ 170.6432, 68.9161 ], [ 170.6443, 68.913 ], [ 170.6516, 68.912 ], [ 170.6536, 68.9078 ], [ 170.6463, 68.9036 ], [ 170.6479, 68.8979 ], [ 170.6443, 68.8943 ], [ 170.6474, 68.888 ], [ 170.6401, 68.8849 ], [ 170.6401, 68.8818 ], [ 170.6432, 68.8776 ], [ 170.638, 68.8755 ], [ 170.6516, 68.8745 ], [ 170.6463, 68.8714 ], [ 170.6505, 68.8672 ], [ 170.6557, 68.8662 ], [ 170.6526, 68.863 ], [ 170.6578, 68.862 ], [ 170.662, 68.8578 ], [ 170.6563, 68.8552 ], [ 170.6599, 68.8516 ], [ 170.663, 68.8443 ], [ 170.6729, 68.8438 ], [ 170.6792, 68.8458 ], [ 170.6839, 68.8401 ], [ 170.6953, 68.8391 ], [ 170.6922, 68.8359 ], [ 170.7057, 68.8328 ], [ 170.7026, 68.8307 ], [ 170.7026, 68.8276 ], [ 170.7099, 68.8266 ], [ 170.713, 68.8193 ], [ 170.7203, 68.8182 ], [ 170.7172, 68.8151 ], [ 170.7224, 68.8141 ], [ 170.7193, 68.812 ], [ 170.7182, 68.8089 ], [ 170.7109, 68.8057 ], [ 170.713, 68.8026 ], [ 170.7167, 68.8021 ], [ 170.7182, 68.8026 ], [ 170.7172, 68.8057 ], [ 170.7245, 68.8089 ], [ 170.7255, 68.812 ], [ 170.7328, 68.8161 ], [ 170.7307, 68.8182 ], [ 170.7193, 68.8214 ], [ 170.7224, 68.8245 ], [ 170.7203, 68.8266 ], [ 170.7151, 68.8276 ], [ 170.7182, 68.8297 ], [ 170.7151, 68.837 ], [ 170.7182, 68.8391 ], [ 170.713, 68.8411 ], [ 170.7161, 68.8432 ], [ 170.7109, 68.8443 ], [ 170.7057, 68.8516 ], [ 170.6922, 68.8544 ], [ 170.6891, 68.8599 ], [ 170.6818, 68.8609 ], [ 170.6807, 68.8641 ], [ 170.6734, 68.8651 ], [ 170.6724, 68.8682 ], [ 170.6667, 68.8688 ], [ 170.6651, 68.8703 ], [ 170.6792, 68.8708 ], [ 170.6849, 68.8734 ], [ 170.6776, 68.8776 ], [ 170.6755, 68.8828 ], [ 170.6787, 68.8839 ], [ 170.6797, 68.887 ], [ 170.6896, 68.8917 ], [ 170.7057, 68.8891 ], [ 170.7068, 68.8859 ], [ 170.7104, 68.8833 ], [ 170.7167, 68.8854 ], [ 170.7458, 68.8854 ], [ 170.7474, 68.887 ], [ 170.7188, 68.8896 ], [ 170.7141, 68.8932 ], [ 170.7005, 68.8964 ], [ 170.6995, 68.8995 ], [ 170.6922, 68.9005 ], [ 170.6911, 68.9036 ], [ 170.6839, 68.9047 ], [ 170.6807, 68.9099 ], [ 170.6755, 68.9109 ], [ 170.6787, 68.9141 ], [ 170.6734, 68.9151 ], [ 170.6682, 68.9203 ], [ 170.6568, 68.9234 ], [ 170.6547, 68.9266 ], [ 170.6641, 68.9276 ], [ 170.663, 68.9307 ], [ 170.6646, 68.9313 ], [ 170.6708, 68.9313 ], [ 170.675, 68.9333 ], [ 170.6875, 68.9333 ], [ 170.6917, 68.9354 ], [ 170.7042, 68.9354 ], [ 170.7083, 68.9375 ], [ 170.7271, 68.9354 ], [ 170.7287, 68.9359 ], [ 170.7276, 68.9411 ], [ 170.7354, 68.9417 ], [ 170.7437, 68.9458 ], [ 170.7625, 68.9437 ], [ 170.7641, 68.9443 ], [ 170.763, 68.9495 ], [ 170.7724, 68.9505 ], [ 170.7833, 68.9563 ], [ 170.7896, 68.9542 ], [ 170.7922, 68.9578 ], [ 170.8, 68.9583 ], [ 170.8125, 68.9646 ], [ 170.8203, 68.9651 ], [ 170.8229, 68.9688 ], [ 170.8292, 68.9667 ], [ 170.8354, 68.9688 ], [ 170.8354, 68.9708 ], [ 170.8438, 68.9708 ], [ 170.8521, 68.975 ], [ 170.8703, 68.9776 ], [ 170.8651, 68.9797 ], [ 170.8682, 68.9818 ], [ 170.8693, 68.9849 ], [ 170.8771, 68.9854 ], [ 170.8854, 68.9896 ], [ 170.8932, 68.9901 ], [ 170.8974, 68.9922 ], [ 170.8963, 68.9953 ], [ 170.8979, 68.9958 ], [ 170.9141, 68.9943 ], [ 170.913, 68.9974 ], [ 170.9266, 69.0026 ], [ 170.9255, 69.0057 ], [ 170.9438, 69.0083 ], [ 170.9453, 69.0089 ], [ 170.9443, 69.012 ], [ 170.9521, 69.0146 ], [ 170.9599, 69.0151 ], [ 170.9568, 69.0203 ], [ 170.9661, 69.0234 ], [ 170.9651, 69.0286 ], [ 170.9745, 69.0297 ], [ 170.9734, 69.0328 ], [ 170.9896, 69.0396 ], [ 170.9958, 69.0396 ], [ 171.0, 69.0417 ], [ 171.0141, 69.0411 ], [ 171.0167, 69.0375 ], [ 171.013, 69.0349 ], [ 171.0141, 69.0318 ], [ 171.0089, 69.0307 ], [ 171.012, 69.0255 ], [ 171.0068, 69.0234 ], [ 171.0109, 69.0193 ], [ 171.0161, 69.0182 ], [ 171.0193, 69.0151 ], [ 171.0271, 69.0125 ], [ 171.0458, 69.0125 ], [ 171.0583, 69.0188 ], [ 171.075, 69.0208 ], [ 171.088, 69.0266 ], [ 171.0958, 69.0271 ], [ 171.1, 69.0292 ], [ 171.137, 69.0297 ], [ 171.1354, 69.0313 ], [ 171.1193, 69.0318 ], [ 171.1208, 69.0333 ], [ 171.1396, 69.0333 ], [ 171.1443, 69.0297 ], [ 171.1521, 69.0271 ], [ 171.1557, 69.0276 ], [ 171.1599, 69.0307 ], [ 171.15, 69.0313 ], [ 171.1438, 69.0354 ], [ 171.1187, 69.0354 ], [ 171.1104, 69.0313 ], [ 171.0854, 69.0313 ], [ 171.075, 69.0354 ], [ 171.0667, 69.0354 ], [ 171.0651, 69.0349 ], [ 171.0661, 69.0297 ], [ 171.0537, 69.0234 ], [ 171.0422, 69.0234 ], [ 171.0412, 69.0266 ], [ 171.0339, 69.0276 ], [ 171.0339, 69.0307 ], [ 171.037, 69.0328 ], [ 171.0349, 69.0349 ], [ 171.0297, 69.0359 ], [ 171.0328, 69.038 ], [ 171.0307, 69.0432 ], [ 171.0234, 69.0443 ], [ 171.0224, 69.0474 ], [ 171.0151, 69.0484 ], [ 171.0109, 69.0568 ], [ 171.0141, 69.0589 ], [ 171.0151, 69.062 ], [ 171.0182, 69.063 ], [ 171.0161, 69.0661 ], [ 171.0104, 69.0667 ], [ 171.0109, 69.0682 ], [ 171.0182, 69.0714 ], [ 171.0172, 69.0766 ], [ 171.0245, 69.0818 ], [ 171.0234, 69.0849 ], [ 171.0328, 69.088 ], [ 171.0313, 69.0917 ], [ 171.0349, 69.0953 ], [ 171.0286, 69.1057 ], [ 171.0213, 69.1068 ], [ 171.0182, 69.1141 ], [ 171.0109, 69.1151 ], [ 171.0057, 69.1224 ], [ 171.0005, 69.1234 ], [ 171.0036, 69.1266 ], [ 170.9984, 69.1276 ], [ 170.9912, 69.137 ], [ 170.9859, 69.138 ], [ 170.9745, 69.1453 ], [ 170.9646, 69.1458 ], [ 170.9458, 69.1542 ], [ 170.9229, 69.1563 ], [ 170.9187, 69.1583 ], [ 170.9125, 69.1563 ], [ 170.8901, 69.163 ], [ 170.8974, 69.1672 ], [ 170.8963, 69.1703 ], [ 170.9016, 69.1713 ], [ 170.9005, 69.1745 ], [ 170.9078, 69.1776 ], [ 170.9068, 69.1828 ], [ 170.9162, 69.1859 ], [ 170.913, 69.1932 ], [ 170.9203, 69.1964 ], [ 170.9193, 69.2016 ], [ 170.9229, 69.2042 ], [ 170.9214, 69.2078 ], [ 170.9287, 69.213 ], [ 170.9276, 69.2161 ], [ 170.9328, 69.2172 ], [ 170.9255, 69.2203 ], [ 170.9349, 69.2234 ], [ 170.9339, 69.2266 ], [ 170.9375, 69.2292 ], [ 170.9318, 69.238 ], [ 170.937, 69.2412 ], [ 170.9349, 69.2432 ], [ 170.9297, 69.2443 ], [ 170.9328, 69.2464 ], [ 170.9318, 69.2495 ], [ 170.9349, 69.2505 ], [ 170.9297, 69.2526 ], [ 170.9333, 69.2562 ], [ 170.9318, 69.2599 ], [ 170.9349, 69.2609 ], [ 170.9297, 69.2641 ], [ 170.937, 69.2672 ], [ 170.9375, 69.2688 ], [ 170.9349, 69.2745 ], [ 170.9318, 69.2776 ], [ 170.9287, 69.2849 ], [ 170.9214, 69.2859 ], [ 170.9203, 69.2891 ], [ 170.913, 69.2901 ], [ 170.9099, 69.2953 ], [ 170.9047, 69.2963 ], [ 170.9005, 69.3005 ], [ 170.9057, 69.3037 ], [ 170.8984, 69.3047 ], [ 170.8984, 69.3078 ], [ 170.9016, 69.3099 ], [ 170.8943, 69.3109 ], [ 170.8995, 69.313 ], [ 170.8974, 69.3161 ], [ 170.8922, 69.3172 ], [ 170.887, 69.3224 ], [ 170.8818, 69.3234 ], [ 170.8786, 69.3287 ], [ 170.8714, 69.3297 ], [ 170.8682, 69.337 ], [ 170.8609, 69.338 ], [ 170.8599, 69.3411 ], [ 170.8526, 69.3422 ], [ 170.8557, 69.3443 ], [ 170.8505, 69.3464 ], [ 170.8537, 69.3484 ], [ 170.8537, 69.3516 ], [ 170.8401, 69.3526 ], [ 170.837, 69.3578 ], [ 170.8318, 69.3588 ], [ 170.8349, 69.362 ], [ 170.825, 69.3625 ], [ 170.8208, 69.3646 ], [ 170.8104, 69.3604 ], [ 170.7833, 69.3604 ], [ 170.7755, 69.363 ], [ 170.7786, 69.3651 ], [ 170.7786, 69.3682 ], [ 170.7713, 69.3693 ], [ 170.763, 69.3734 ], [ 170.7609, 69.3786 ], [ 170.7646, 69.3812 ], [ 170.7604, 69.3875 ], [ 170.7641, 69.3901 ], [ 170.763, 69.3953 ], [ 170.7667, 69.3979 ], [ 170.763, 69.4036 ], [ 170.7682, 69.4047 ], [ 170.7651, 69.4099 ], [ 170.7703, 69.4109 ], [ 170.7667, 69.4167 ], [ 170.7703, 69.4193 ], [ 170.7693, 69.4245 ], [ 170.7729, 69.4271 ], [ 170.7724, 69.4287 ], [ 170.7651, 69.4297 ], [ 170.7688, 69.4333 ], [ 170.763, 69.4432 ], [ 170.7682, 69.4453 ], [ 170.7609, 69.4464 ], [ 170.7599, 69.4495 ], [ 170.7526, 69.4505 ], [ 170.7516, 69.4537 ], [ 170.7443, 69.4547 ], [ 170.7417, 69.4583 ], [ 170.725, 69.4583 ], [ 170.7172, 69.4609 ], [ 170.7125, 69.4646 ], [ 170.7042, 69.4646 ], [ 170.6964, 69.4672 ], [ 170.6995, 69.4703 ], [ 170.6943, 69.4786 ], [ 170.6995, 69.4797 ], [ 170.6964, 69.487 ], [ 170.7, 69.4896 ], [ 170.6964, 69.4953 ], [ 170.7016, 69.4964 ], [ 170.6984, 69.5016 ], [ 170.7036, 69.5036 ], [ 170.6964, 69.5047 ], [ 170.6943, 69.5099 ], [ 170.6979, 69.5125 ], [ 170.6891, 69.5286 ], [ 170.6787, 69.5432 ], [ 170.6734, 69.5443 ], [ 170.6714, 69.5463 ], [ 170.6766, 69.5495 ], [ 170.6693, 69.5505 ], [ 170.6662, 69.5557 ], [ 170.6526, 69.5589 ], [ 170.6557, 69.562 ], [ 170.6505, 69.563 ], [ 170.6391, 69.5703 ], [ 170.6339, 69.5714 ], [ 170.6286, 69.5766 ], [ 170.6172, 69.5797 ], [ 170.612, 69.5849 ], [ 170.6042, 69.5875 ], [ 170.5854, 69.5917 ], [ 170.575, 69.5958 ], [ 170.5667, 69.5958 ], [ 170.5625, 69.5979 ], [ 170.5542, 69.5958 ], [ 170.55, 69.5979 ], [ 170.5417, 69.5979 ], [ 170.5375, 69.6 ], [ 170.5313, 69.5979 ], [ 170.525, 69.6 ], [ 170.5167, 69.6 ], [ 170.5125, 69.5979 ], [ 170.5042, 69.5979 ], [ 170.5, 69.6 ], [ 170.4937, 69.5979 ], [ 170.4896, 69.6 ], [ 170.4833, 69.5979 ], [ 170.4396, 69.5979 ], [ 170.4354, 69.5958 ], [ 170.4063, 69.5958 ], [ 170.4021, 69.5938 ], [ 170.3938, 69.5958 ], [ 170.3896, 69.5938 ], [ 170.3771, 69.5938 ], [ 170.3625, 69.5896 ], [ 170.3333, 69.5875 ], [ 170.3318, 69.587 ], [ 170.3328, 69.5839 ], [ 170.3313, 69.5833 ], [ 170.3188, 69.5833 ], [ 170.3146, 69.5813 ], [ 170.2771, 69.5813 ], [ 170.2729, 69.5792 ], [ 170.2646, 69.5792 ], [ 170.2604, 69.5813 ], [ 170.2563, 69.5792 ], [ 170.2417, 69.5792 ], [ 170.2375, 69.5813 ], [ 170.2312, 69.5792 ], [ 170.225, 69.5813 ], [ 170.1979, 69.5813 ], [ 170.1953, 69.5849 ], [ 170.188, 69.5859 ], [ 170.1854, 69.5896 ], [ 170.1771, 69.5875 ], [ 170.1583, 69.5938 ], [ 170.1526, 69.5943 ], [ 170.1578, 69.5964 ], [ 170.1526, 69.6047 ], [ 170.1563, 69.6083 ], [ 170.1547, 69.612 ], [ 170.162, 69.6172 ], [ 170.1609, 69.6203 ], [ 170.1662, 69.6214 ], [ 170.163, 69.6266 ], [ 170.1724, 69.6297 ], [ 170.1714, 69.6349 ], [ 170.1807, 69.6359 ], [ 170.1797, 69.6391 ], [ 170.1891, 69.6422 ], [ 170.188, 69.6453 ], [ 170.1953, 69.6505 ], [ 170.1943, 69.6536 ], [ 170.1995, 69.6547 ], [ 170.1984, 69.6578 ], [ 170.2042, 69.6604 ], [ 170.2104, 69.6604 ], [ 170.2182, 69.663 ], [ 170.2151, 69.6682 ], [ 170.2287, 69.6734 ], [ 170.2312, 69.6771 ], [ 170.2453, 69.6776 ], [ 170.2443, 69.6807 ], [ 170.2516, 69.6839 ], [ 170.2542, 69.6875 ], [ 170.2604, 69.6875 ], [ 170.2667, 69.6896 ], [ 170.2771, 69.6854 ], [ 170.2917, 69.6854 ], [ 170.2974, 69.688 ], [ 170.2974, 69.6911 ], [ 170.2937, 69.6937 ], [ 170.2875, 69.6917 ], [ 170.2776, 69.6922 ], [ 170.2828, 69.6943 ], [ 170.2797, 69.6995 ], [ 170.2901, 69.7037 ], [ 170.3042, 69.7042 ], [ 170.3188, 69.7083 ], [ 170.3313, 69.7083 ], [ 170.3354, 69.7125 ], [ 170.3625, 69.7167 ], [ 170.3771, 69.7208 ], [ 170.3833, 69.7208 ], [ 170.3938, 69.725 ], [ 170.4063, 69.725 ], [ 170.4104, 69.7229 ], [ 170.4167, 69.725 ], [ 170.4312, 69.725 ], [ 170.4354, 69.7229 ], [ 170.4854, 69.7229 ], [ 170.5, 69.7271 ], [ 170.5125, 69.7271 ], [ 170.5208, 69.7312 ], [ 170.5333, 69.7312 ], [ 170.5521, 69.7396 ], [ 170.562, 69.7401 ], [ 170.5609, 69.7432 ], [ 170.5703, 69.7443 ], [ 170.6021, 69.7583 ], [ 170.6167, 69.7583 ], [ 170.6208, 69.7562 ], [ 170.6286, 69.7589 ], [ 170.6271, 69.7604 ], [ 170.6151, 69.763 ], [ 170.6182, 69.7662 ], [ 170.5984, 69.7714 ], [ 170.5943, 69.7766 ], [ 170.5995, 69.7786 ], [ 170.5974, 69.7807 ], [ 170.5922, 69.7818 ], [ 170.5953, 69.7849 ], [ 170.5932, 69.787 ], [ 170.588, 69.788 ], [ 170.5911, 69.7901 ], [ 170.5891, 69.7932 ], [ 170.5755, 69.7984 ], [ 170.5786, 69.8016 ], [ 170.5729, 69.8021 ], [ 170.5729, 69.8042 ], [ 170.5651, 69.8047 ], [ 170.5703, 69.8068 ], [ 170.5688, 69.8083 ], [ 170.563, 69.8089 ], [ 170.5661, 69.8109 ], [ 170.5672, 69.8141 ], [ 170.5703, 69.8151 ], [ 170.5693, 69.8203 ], [ 170.5729, 69.8229 ], [ 170.5693, 69.8287 ], [ 170.5786, 69.8318 ], [ 170.5766, 69.8349 ], [ 170.5714, 69.8359 ], [ 170.575, 69.8396 ], [ 170.5734, 69.8432 ], [ 170.5766, 69.8443 ], [ 170.5714, 69.8464 ], [ 170.575, 69.85 ], [ 170.5734, 69.8536 ], [ 170.5771, 69.8562 ], [ 170.5693, 69.8693 ], [ 170.5724, 69.8714 ], [ 170.5714, 69.8766 ], [ 170.5745, 69.8776 ], [ 170.5745, 69.8807 ], [ 170.5703, 69.887 ], [ 170.5651, 69.888 ], [ 170.5609, 69.8922 ], [ 170.5578, 69.8974 ], [ 170.5297, 69.9047 ], [ 170.5255, 69.9109 ], [ 170.5307, 69.913 ], [ 170.5297, 69.9161 ], [ 170.5328, 69.9172 ], [ 170.538, 69.9245 ], [ 170.5412, 69.9255 ], [ 170.5391, 69.9287 ], [ 170.5339, 69.9297 ], [ 170.5318, 69.9328 ], [ 170.537, 69.9339 ], [ 170.5359, 69.937 ], [ 170.5391, 69.938 ], [ 170.5401, 69.9411 ], [ 170.5474, 69.9443 ], [ 170.5422, 69.9474 ], [ 170.5495, 69.9505 ], [ 170.55, 69.9521 ], [ 170.5464, 69.9589 ], [ 170.55, 69.9625 ], [ 170.5437, 69.9729 ], [ 170.5443, 69.9745 ], [ 170.5516, 69.9776 ], [ 170.5505, 69.9828 ], [ 170.5557, 69.9838 ], [ 170.5547, 69.987 ], [ 170.5583, 69.9896 ], [ 170.5568, 69.9932 ], [ 170.5599, 69.9943 ], [ 170.5526, 69.9984 ], [ 170.5505, 70.0036 ], [ 170.5583, 70.0042 ], [ 170.5703, 70.0089 ], [ 170.5703, 70.012 ], [ 170.5646, 70.0125 ], [ 170.563, 70.0141 ], [ 170.587, 70.0234 ], [ 170.5854, 70.025 ], [ 170.5797, 70.0255 ], [ 170.5776, 70.0307 ], [ 170.587, 70.0339 ], [ 170.5797, 70.037 ], [ 170.5917, 70.0417 ], [ 170.5995, 70.0422 ], [ 170.5974, 70.0453 ], [ 170.5922, 70.0463 ], [ 170.5953, 70.0484 ], [ 170.5901, 70.0505 ], [ 170.5932, 70.0537 ], [ 170.588, 70.0547 ], [ 170.5838, 70.0589 ], [ 170.5854, 70.0604 ], [ 170.5953, 70.0609 ], [ 170.5995, 70.0641 ], [ 170.5896, 70.0687 ], [ 170.5838, 70.0693 ], [ 170.5891, 70.0724 ], [ 170.5755, 70.0734 ], [ 170.5807, 70.0766 ], [ 170.575, 70.0771 ], [ 170.5734, 70.0787 ], [ 170.5828, 70.0818 ], [ 170.5818, 70.087 ], [ 170.5901, 70.0911 ], [ 170.5979, 70.0938 ], [ 170.6057, 70.0943 ], [ 170.6047, 70.0974 ], [ 170.6099, 70.0984 ], [ 170.6089, 70.1016 ], [ 170.6104, 70.1021 ], [ 170.6167, 70.1 ], [ 170.6313, 70.1 ], [ 170.6354, 70.0979 ], [ 170.6458, 70.0979 ], [ 170.65, 70.0958 ], [ 170.6708, 70.0958 ], [ 170.675, 70.0979 ], [ 170.6833, 70.0979 ], [ 170.6875, 70.0958 ], [ 170.7021, 70.0958 ], [ 170.7063, 70.0938 ], [ 170.7146, 70.0938 ], [ 170.7182, 70.0911 ], [ 170.7151, 70.0891 ], [ 170.7151, 70.0859 ], [ 170.7287, 70.0849 ], [ 170.7271, 70.0833 ], [ 170.7172, 70.0828 ], [ 170.7188, 70.0813 ], [ 170.7245, 70.0807 ], [ 170.7214, 70.0776 ], [ 170.7312, 70.0771 ], [ 170.7354, 70.075 ], [ 170.7417, 70.0771 ], [ 170.7479, 70.0771 ], [ 170.7583, 70.0813 ], [ 170.7958, 70.0813 ], [ 170.8, 70.0792 ], [ 170.8146, 70.0792 ], [ 170.8188, 70.0771 ], [ 170.8271, 70.0771 ], [ 170.8313, 70.0792 ], [ 170.8458, 70.0792 ], [ 170.85, 70.0813 ], [ 170.8917, 70.0813 ], [ 170.9021, 70.0854 ], [ 170.9245, 70.0849 ], [ 170.9229, 70.0833 ], [ 170.9068, 70.0828 ], [ 170.9125, 70.0771 ], [ 170.925, 70.0771 ], [ 170.9292, 70.0792 ], [ 170.9479, 70.0792 ], [ 170.9521, 70.0813 ], [ 170.9917, 70.0813 ], [ 170.9958, 70.0792 ], [ 171.0104, 70.0792 ], [ 171.025, 70.0833 ], [ 171.0313, 70.0813 ], [ 171.0396, 70.0833 ], [ 171.0437, 70.0813 ], [ 171.0521, 70.0813 ], [ 171.062, 70.0787 ], [ 171.0646, 70.075 ], [ 171.0708, 70.0771 ], [ 171.0792, 70.0771 ], [ 171.0833, 70.075 ], [ 171.0875, 70.075 ], [ 171.0979, 70.0708 ], [ 171.1037, 70.0703 ], [ 171.1057, 70.0661 ], [ 171.1042, 70.0646 ], [ 171.0896, 70.0646 ], [ 171.0854, 70.0667 ], [ 171.0818, 70.0661 ], [ 171.0838, 70.0609 ], [ 171.0854, 70.0604 ], [ 171.1, 70.0604 ], [ 171.1042, 70.0625 ], [ 171.1313, 70.0625 ], [ 171.1354, 70.0604 ], [ 171.15, 70.0604 ], [ 171.1542, 70.0625 ], [ 171.1604, 70.0625 ], [ 171.1667, 70.0646 ], [ 171.1708, 70.0625 ], [ 171.1792, 70.0625 ], [ 171.1917, 70.0583 ], [ 171.2312, 70.0583 ], [ 171.2354, 70.0563 ], [ 171.2437, 70.0563 ], [ 171.2479, 70.0542 ], [ 171.2563, 70.0563 ], [ 171.275, 70.05 ], [ 171.2833, 70.05 ], [ 171.2958, 70.0458 ], [ 171.3042, 70.0458 ], [ 171.3083, 70.0479 ], [ 171.3208, 70.0479 ], [ 171.325, 70.05 ], [ 171.35, 70.05 ], [ 171.3542, 70.0521 ], [ 171.3979, 70.0521 ], [ 171.4042, 70.0542 ], [ 171.4083, 70.0521 ], [ 171.4229, 70.0521 ], [ 171.4271, 70.05 ], [ 171.45, 70.0479 ], [ 171.4516, 70.0463 ], [ 171.4422, 70.0453 ], [ 171.45, 70.0417 ], [ 171.4667, 70.0417 ], [ 171.4708, 70.0396 ], [ 171.4979, 70.0396 ], [ 171.5083, 70.0354 ], [ 171.5188, 70.0354 ], [ 171.5229, 70.0333 ], [ 171.5328, 70.0328 ], [ 171.5271, 70.0292 ], [ 171.5172, 70.0286 ], [ 171.5188, 70.0271 ], [ 171.5333, 70.0271 ], [ 171.5375, 70.0292 ], [ 171.5583, 70.0292 ], [ 171.5688, 70.025 ], [ 171.5792, 70.025 ], [ 171.6042, 70.0167 ], [ 171.6208, 70.0167 ], [ 171.625, 70.0146 ], [ 171.6812, 70.0146 ], [ 171.6875, 70.0167 ], [ 171.6917, 70.0146 ], [ 171.7, 70.0146 ], [ 171.7042, 70.0125 ], [ 171.7188, 70.0125 ], [ 171.7229, 70.0104 ], [ 171.7333, 70.0104 ], [ 171.7375, 70.0083 ], [ 171.7604, 70.0062 ], [ 171.7646, 70.0042 ], [ 171.7792, 70.0042 ], [ 171.7833, 70.0021 ], [ 171.8042, 70.0021 ], [ 171.8057, 70.0005 ], [ 171.8026, 69.9995 ], [ 171.8042, 69.9979 ], [ 171.8562, 69.9979 ], [ 171.8578, 69.9964 ], [ 171.8401, 69.9953 ], [ 171.8422, 69.9922 ], [ 171.8521, 69.9917 ], [ 171.8562, 69.9896 ], [ 171.8729, 69.9896 ], [ 171.8771, 69.9875 ], [ 171.8979, 69.9875 ], [ 171.9021, 69.9854 ], [ 171.9354, 69.9854 ], [ 171.9396, 69.9833 ], [ 171.9604, 69.9833 ], [ 171.9646, 69.9812 ], [ 171.9729, 69.9812 ], [ 171.9771, 69.9792 ], [ 171.9937, 69.9792 ], [ 171.9979, 69.9771 ], [ 172.0437, 69.9771 ], [ 172.0479, 69.975 ], [ 172.1292, 69.975 ], [ 172.1333, 69.9708 ], [ 172.1438, 69.9708 ], [ 172.1479, 69.9688 ], [ 172.1563, 69.9688 ], [ 172.1604, 69.9667 ], [ 172.1687, 69.9667 ], [ 172.1771, 69.9708 ], [ 172.2479, 69.9688 ], [ 172.2521, 69.9667 ], [ 172.262, 69.9661 ], [ 172.2651, 69.963 ], [ 172.2703, 69.962 ], [ 172.275, 69.9583 ], [ 172.3083, 69.9563 ], [ 172.3125, 69.9542 ], [ 172.3333, 69.9542 ], [ 172.3375, 69.9521 ], [ 172.3542, 69.9521 ], [ 172.3646, 69.9479 ], [ 172.3729, 69.95 ], [ 172.3792, 69.9458 ], [ 172.3875, 69.9458 ], [ 172.3979, 69.9417 ], [ 172.6, 69.9417 ], [ 172.6042, 69.9437 ], [ 172.6187, 69.9437 ], [ 172.6229, 69.9458 ], [ 172.6292, 69.9458 ], [ 172.6375, 69.95 ], [ 172.6521, 69.95 ], [ 172.6563, 69.9521 ], [ 172.6875, 69.9521 ], [ 172.7021, 69.9563 ], [ 172.7146, 69.9563 ], [ 172.7188, 69.9583 ], [ 172.7271, 69.9583 ], [ 172.7417, 69.9625 ], [ 172.75, 69.9625 ], [ 172.7526, 69.9589 ], [ 172.7599, 69.9578 ], [ 172.7609, 69.9547 ], [ 172.7682, 69.9537 ], [ 172.7713, 69.9484 ], [ 172.7912, 69.9432 ], [ 172.7912, 69.9401 ], [ 172.788, 69.938 ], [ 172.7932, 69.937 ], [ 172.7953, 69.9339 ], [ 172.7859, 69.9307 ], [ 172.7849, 69.9276 ], [ 172.7818, 69.9255 ], [ 172.787, 69.9245 ], [ 172.7891, 69.9224 ], [ 172.7839, 69.9203 ], [ 172.7859, 69.9172 ], [ 172.7995, 69.912 ], [ 172.8026, 69.9089 ], [ 172.825, 69.9021 ], [ 172.8349, 69.9016 ], [ 172.838, 69.8984 ], [ 172.8432, 69.8974 ], [ 172.8479, 69.8938 ], [ 172.8687, 69.8917 ], [ 172.875, 69.8875 ], [ 172.8958, 69.8854 ], [ 172.9083, 69.8812 ], [ 172.9167, 69.8812 ], [ 172.9208, 69.8771 ], [ 172.9312, 69.8771 ], [ 172.9391, 69.8745 ], [ 172.9422, 69.8714 ], [ 172.9563, 69.8667 ], [ 172.9745, 69.8641 ], [ 172.9776, 69.8609 ], [ 172.9828, 69.8599 ], [ 172.9859, 69.8547 ], [ 172.9937, 69.8521 ], [ 173.0042, 69.8521 ], [ 173.0083, 69.85 ], [ 173.0229, 69.85 ], [ 173.0255, 69.8464 ], [ 173.0354, 69.8438 ], [ 173.0562, 69.8438 ], [ 173.075, 69.8354 ], [ 173.0833, 69.8375 ], [ 173.0859, 69.8339 ], [ 173.0958, 69.8333 ], [ 173.1, 69.8313 ], [ 173.175, 69.8313 ], [ 173.1792, 69.8333 ], [ 173.1979, 69.8333 ], [ 173.2042, 69.8354 ], [ 173.2083, 69.8333 ], [ 173.2167, 69.8333 ], [ 173.2266, 69.8307 ], [ 173.2188, 69.8271 ], [ 173.2063, 69.8271 ], [ 173.2047, 69.8255 ], [ 173.2083, 69.8229 ], [ 173.2292, 69.8229 ], [ 173.237, 69.8203 ], [ 173.2401, 69.8172 ], [ 173.2453, 69.8161 ], [ 173.2422, 69.8141 ], [ 173.2422, 69.8109 ], [ 173.2474, 69.8099 ], [ 173.2505, 69.8068 ], [ 173.2703, 69.8016 ], [ 173.2703, 69.7984 ], [ 173.2667, 69.7979 ], [ 173.2625, 69.8 ], [ 173.2583, 69.7979 ], [ 173.2484, 69.7974 ], [ 173.2443, 69.7953 ], [ 173.2453, 69.7901 ], [ 173.2437, 69.7896 ], [ 173.2125, 69.7896 ], [ 173.2021, 69.7854 ], [ 173.1943, 69.7849 ], [ 173.1979, 69.7771 ], [ 173.1943, 69.7745 ], [ 173.1943, 69.7714 ], [ 173.1979, 69.7688 ], [ 173.2125, 69.7688 ], [ 173.2151, 69.7651 ], [ 173.225, 69.7646 ], [ 173.2312, 69.7604 ], [ 173.2479, 69.7667 ], [ 173.2604, 69.7625 ], [ 173.2667, 69.7646 ], [ 173.2792, 69.7646 ], [ 173.2828, 69.7609 ], [ 173.2755, 69.7557 ], [ 173.2771, 69.7542 ], [ 173.2833, 69.7542 ], [ 173.2912, 69.7568 ], [ 173.3005, 69.7641 ], [ 173.3083, 69.7667 ], [ 173.3146, 69.7667 ], [ 173.3229, 69.7708 ], [ 173.3333, 69.7667 ], [ 173.3432, 69.7714 ], [ 173.3443, 69.7745 ], [ 173.35, 69.7771 ], [ 173.3599, 69.7766 ], [ 173.363, 69.7714 ], [ 173.3667, 69.7688 ], [ 173.3813, 69.775 ], [ 173.3974, 69.7745 ], [ 173.3943, 69.7724 ], [ 173.3932, 69.7693 ], [ 173.3901, 69.7682 ], [ 173.3917, 69.7667 ], [ 173.3979, 69.7667 ], [ 173.4021, 69.7688 ], [ 173.4333, 69.7688 ], [ 173.4542, 69.775 ], [ 173.4667, 69.775 ], [ 173.4849, 69.7818 ], [ 173.4839, 69.7849 ], [ 173.4932, 69.7859 ], [ 173.4943, 69.7891 ], [ 173.5141, 69.7963 ], [ 173.5182, 69.7995 ], [ 173.5104, 69.8021 ], [ 173.5005, 69.8026 ], [ 173.5036, 69.8047 ], [ 173.5021, 69.8063 ], [ 173.4875, 69.8063 ], [ 173.4833, 69.8083 ], [ 173.4734, 69.8089 ], [ 173.4807, 69.813 ], [ 173.4818, 69.8161 ], [ 173.4912, 69.8193 ], [ 173.4896, 69.8208 ], [ 173.4839, 69.8214 ], [ 173.4891, 69.8234 ], [ 173.487, 69.8266 ], [ 173.4792, 69.8292 ], [ 173.4458, 69.8292 ], [ 173.4438, 69.8313 ], [ 173.4474, 69.8349 ], [ 173.4333, 69.8396 ], [ 173.4229, 69.8396 ], [ 173.4151, 69.8422 ], [ 173.4125, 69.8458 ], [ 173.4162, 69.8484 ], [ 173.4162, 69.8516 ], [ 173.4083, 69.8542 ], [ 173.3979, 69.8542 ], [ 173.3901, 69.8568 ], [ 173.387, 69.8599 ], [ 173.3729, 69.8646 ], [ 173.3583, 69.8646 ], [ 173.3542, 69.8667 ], [ 173.3313, 69.8688 ], [ 173.3271, 69.8729 ], [ 173.3167, 69.8729 ], [ 173.3026, 69.8776 ], [ 173.3125, 69.8833 ], [ 173.3224, 69.8839 ], [ 173.3266, 69.8859 ], [ 173.3266, 69.8891 ], [ 173.3026, 69.888 ], [ 173.3005, 69.8932 ], [ 173.3057, 69.8953 ], [ 173.2922, 69.8964 ], [ 173.2912, 69.8995 ], [ 173.2859, 69.9005 ], [ 173.2813, 69.9042 ], [ 173.2708, 69.9 ], [ 173.2609, 69.8995 ], [ 173.262, 69.8964 ], [ 173.2542, 69.8938 ], [ 173.2479, 69.8938 ], [ 173.2255, 69.8849 ], [ 173.2266, 69.8818 ], [ 173.2214, 69.8807 ], [ 173.2224, 69.8776 ], [ 173.2068, 69.8724 ], [ 173.2141, 69.8693 ], [ 173.2088, 69.8682 ], [ 173.2099, 69.8651 ], [ 173.2047, 69.8641 ], [ 173.2057, 69.8588 ], [ 173.2026, 69.8578 ], [ 173.2016, 69.8547 ], [ 173.1958, 69.8521 ], [ 173.1896, 69.8542 ], [ 173.1797, 69.8547 ], [ 173.1833, 69.8583 ], [ 173.1818, 69.862 ], [ 173.187, 69.863 ], [ 173.1859, 69.8662 ], [ 173.1911, 69.8672 ], [ 173.1901, 69.8724 ], [ 173.1974, 69.8776 ], [ 173.1984, 69.8807 ], [ 173.212, 69.8859 ], [ 173.213, 69.8891 ], [ 173.2224, 69.8901 ], [ 173.2318, 69.8974 ], [ 173.2437, 69.9021 ], [ 173.2521, 69.9021 ], [ 173.263, 69.9078 ], [ 173.2708, 69.9104 ], [ 173.2771, 69.9104 ], [ 173.2875, 69.9146 ], [ 173.3, 69.9146 ], [ 173.3083, 69.9187 ], [ 173.3229, 69.9187 ], [ 173.3271, 69.9208 ], [ 173.3396, 69.9208 ], [ 173.3438, 69.9229 ], [ 173.3562, 69.9229 ], [ 173.3604, 69.925 ], [ 173.3729, 69.925 ], [ 173.3771, 69.9271 ], [ 173.3979, 69.9271 ], [ 173.4021, 69.9292 ], [ 173.4208, 69.9292 ], [ 173.425, 69.9313 ], [ 173.45, 69.9313 ], [ 173.4542, 69.9333 ], [ 173.4854, 69.9333 ], [ 173.4896, 69.9313 ], [ 173.5, 69.9313 ], [ 173.5026, 69.9276 ], [ 173.5099, 69.9266 ], [ 173.5109, 69.9234 ], [ 173.5161, 69.9224 ], [ 173.5213, 69.9151 ], [ 173.5412, 69.9099 ], [ 173.5437, 69.9063 ], [ 173.5557, 69.9057 ], [ 173.5568, 69.9026 ], [ 173.5703, 69.8995 ], [ 173.5714, 69.8964 ], [ 173.5771, 69.8958 ], [ 173.5875, 69.8917 ], [ 173.5958, 69.8917 ], [ 173.6, 69.8896 ], [ 173.6042, 69.8896 ], [ 173.6083, 69.8854 ], [ 173.6313, 69.8833 ], [ 173.6354, 69.8792 ], [ 173.6417, 69.8812 ], [ 173.6453, 69.8807 ], [ 173.6453, 69.8776 ], [ 173.6359, 69.8766 ], [ 173.6349, 69.8734 ], [ 173.6318, 69.8724 ], [ 173.6333, 69.8708 ], [ 173.6432, 69.8714 ], [ 173.6422, 69.8745 ], [ 173.65, 69.8771 ], [ 173.6583, 69.8771 ], [ 173.6687, 69.8729 ], [ 173.6833, 69.8729 ], [ 173.6875, 69.8708 ], [ 173.6958, 69.8708 ], [ 173.7, 69.8688 ], [ 173.7042, 69.8688 ], [ 173.7083, 69.8667 ], [ 173.7182, 69.8662 ], [ 173.7104, 69.8625 ], [ 173.6792, 69.8625 ], [ 173.6776, 69.862 ], [ 173.6787, 69.8588 ], [ 173.6708, 69.8562 ], [ 173.6646, 69.8562 ], [ 173.663, 69.8547 ], [ 173.6651, 69.8526 ], [ 173.6766, 69.8495 ], [ 173.6807, 69.8432 ], [ 173.6818, 69.838 ], [ 173.6891, 69.837 ], [ 173.6922, 69.8318 ], [ 173.7036, 69.8287 ], [ 173.7068, 69.8234 ], [ 173.7141, 69.8224 ], [ 173.7167, 69.8187 ], [ 173.7266, 69.8182 ], [ 173.7297, 69.8151 ], [ 173.7437, 69.8104 ], [ 173.7583, 69.8104 ], [ 173.7604, 69.8125 ], [ 173.7563, 69.8208 ], [ 173.7599, 69.8234 ], [ 173.7609, 69.8266 ], [ 173.7641, 69.8276 ], [ 173.7641, 69.8328 ], [ 173.7505, 69.8359 ], [ 173.7536, 69.838 ], [ 173.7536, 69.8411 ], [ 173.7422, 69.8422 ], [ 173.7458, 69.8458 ], [ 173.7417, 69.8542 ], [ 173.7464, 69.8578 ], [ 173.7557, 69.8609 ], [ 173.7583, 69.8646 ], [ 173.7646, 69.8646 ], [ 173.7688, 69.8667 ], [ 173.7958, 69.8667 ], [ 173.7974, 69.8651 ], [ 173.7922, 69.863 ], [ 173.8083, 69.8625 ], [ 173.8099, 69.8641 ], [ 173.8047, 69.8662 ], [ 173.8078, 69.8672 ], [ 173.8104, 69.8708 ], [ 173.8182, 69.8703 ], [ 173.8151, 69.8682 ], [ 173.8167, 69.8667 ], [ 173.825, 69.8667 ], [ 173.8292, 69.8688 ], [ 173.8375, 69.8667 ], [ 173.8391, 69.8672 ], [ 173.838, 69.8703 ], [ 173.8417, 69.8708 ], [ 173.8443, 69.8672 ], [ 173.85, 69.8667 ], [ 173.8516, 69.8651 ], [ 173.8464, 69.8641 ], [ 173.8474, 69.8609 ], [ 173.838, 69.8578 ], [ 173.8391, 69.8505 ], [ 173.8188, 69.85 ], [ 173.8172, 69.8484 ], [ 173.8271, 69.8479 ], [ 173.8313, 69.8458 ], [ 173.8458, 69.8458 ], [ 173.85, 69.8438 ], [ 173.8646, 69.8438 ], [ 173.8854, 69.85 ], [ 173.8995, 69.8505 ], [ 173.8995, 69.8536 ], [ 173.8979, 69.8542 ], [ 173.8708, 69.8542 ], [ 173.8667, 69.8521 ], [ 173.8526, 69.8526 ], [ 173.8505, 69.8557 ], [ 173.8599, 69.8588 ], [ 173.8589, 69.8662 ], [ 173.8667, 69.8667 ], [ 173.8687, 69.8688 ], [ 173.8682, 69.8703 ], [ 173.8609, 69.8714 ], [ 173.8599, 69.8745 ], [ 173.8547, 69.8755 ], [ 173.8604, 69.8812 ], [ 173.8703, 69.8786 ], [ 173.8734, 69.8734 ], [ 173.8771, 69.8729 ], [ 173.8813, 69.875 ], [ 173.8875, 69.8708 ], [ 173.9021, 69.8708 ], [ 173.9063, 69.8729 ], [ 173.925, 69.8729 ], [ 173.9333, 69.8771 ], [ 173.9479, 69.8771 ], [ 173.9495, 69.8755 ], [ 173.9464, 69.8734 ], [ 173.9516, 69.8724 ], [ 173.9547, 69.8651 ], [ 173.9771, 69.8646 ], [ 173.9896, 69.8604 ], [ 173.9995, 69.8599 ], [ 174.0021, 69.8562 ], [ 174.0104, 69.8562 ], [ 174.012, 69.8578 ], [ 174.0068, 69.8588 ], [ 174.0036, 69.8641 ], [ 173.9958, 69.8667 ], [ 173.9729, 69.8667 ], [ 173.9651, 69.8703 ], [ 173.9703, 69.8714 ], [ 173.9688, 69.875 ], [ 173.9708, 69.8771 ], [ 173.9792, 69.8771 ], [ 173.9833, 69.875 ], [ 173.9937, 69.8792 ], [ 174.0016, 69.8797 ], [ 173.9937, 69.8833 ], [ 173.9854, 69.8833 ], [ 173.9755, 69.887 ], [ 174.0021, 69.8875 ], [ 174.0063, 69.8896 ], [ 174.0146, 69.8896 ], [ 174.0208, 69.8917 ], [ 174.0286, 69.8891 ], [ 174.0333, 69.8854 ], [ 174.0396, 69.8875 ], [ 174.0495, 69.8849 ], [ 174.0495, 69.8818 ], [ 174.0458, 69.8792 ], [ 174.0479, 69.8771 ], [ 174.0703, 69.8766 ], [ 174.0672, 69.8734 ], [ 174.0724, 69.8714 ], [ 174.0693, 69.8703 ], [ 174.0682, 69.8651 ], [ 174.0651, 69.8641 ], [ 174.0667, 69.8625 ], [ 174.0828, 69.863 ], [ 174.0818, 69.8662 ], [ 174.087, 69.8672 ], [ 174.0859, 69.8703 ], [ 174.0911, 69.8714 ], [ 174.0896, 69.875 ], [ 174.0917, 69.8771 ], [ 174.1292, 69.8771 ], [ 174.1333, 69.875 ], [ 174.1432, 69.8745 ], [ 174.1479, 69.8708 ], [ 174.1542, 69.8729 ], [ 174.1667, 69.8729 ], [ 174.1708, 69.8708 ], [ 174.1792, 69.8729 ], [ 174.1833, 69.8708 ], [ 174.1938, 69.8708 ], [ 174.1979, 69.8688 ], [ 174.2063, 69.8688 ], [ 174.2104, 69.8667 ], [ 174.2188, 69.8688 ], [ 174.2229, 69.8667 ], [ 174.2312, 69.8667 ], [ 174.2354, 69.8646 ], [ 174.25, 69.8646 ], [ 174.2542, 69.8625 ], [ 174.275, 69.8625 ], [ 174.2792, 69.8646 ], [ 174.2917, 69.8646 ], [ 174.2979, 69.8625 ], [ 174.3005, 69.8662 ], [ 174.3062, 69.8688 ], [ 174.3458, 69.8688 ], [ 174.3562, 69.8646 ], [ 174.3646, 69.8646 ], [ 174.375, 69.8604 ], [ 174.3854, 69.8604 ], [ 174.388, 69.8568 ], [ 174.3979, 69.8562 ], [ 174.4042, 69.8521 ], [ 174.4187, 69.8521 ], [ 174.4229, 69.8479 ], [ 174.4375, 69.8479 ], [ 174.4417, 69.8458 ], [ 174.4625, 69.8458 ], [ 174.4667, 69.8438 ], [ 174.4771, 69.8438 ], [ 174.4812, 69.8396 ], [ 174.4875, 69.8417 ], [ 174.4937, 69.8417 ], [ 174.5083, 69.8479 ], [ 174.5229, 69.8479 ], [ 174.5271, 69.8458 ], [ 174.5542, 69.8458 ], [ 174.5583, 69.8438 ], [ 174.5792, 69.8438 ], [ 174.5833, 69.8417 ], [ 174.6292, 69.8417 ], [ 174.6333, 69.8396 ], [ 174.6375, 69.8417 ], [ 174.6521, 69.8417 ], [ 174.6563, 69.8396 ], [ 174.7813, 69.8396 ], [ 174.7854, 69.8375 ], [ 174.7937, 69.8375 ], [ 174.7979, 69.8354 ], [ 174.8667, 69.8354 ], [ 174.8708, 69.8375 ], [ 174.875, 69.8354 ], [ 174.8833, 69.8354 ], [ 174.8875, 69.8375 ], [ 174.8958, 69.8375 ], [ 174.8974, 69.837 ], [ 174.8974, 69.8339 ], [ 174.888, 69.8328 ], [ 174.888, 69.8297 ], [ 174.9, 69.8292 ], [ 174.9042, 69.825 ], [ 174.925, 69.825 ], [ 174.9307, 69.8276 ], [ 174.9318, 69.8307 ], [ 174.9349, 69.8318 ], [ 174.9349, 69.8349 ], [ 174.9234, 69.8359 ], [ 174.925, 69.8375 ], [ 174.9563, 69.8375 ], [ 174.9604, 69.8396 ], [ 174.9979, 69.8396 ], [ 175.0125, 69.8438 ], [ 175.0161, 69.8432 ], [ 175.0188, 69.8396 ], [ 175.0333, 69.8396 ], [ 175.0375, 69.8375 ], [ 175.125, 69.8375 ], [ 175.1313, 69.8396 ], [ 175.1354, 69.8375 ], [ 175.1458, 69.8375 ], [ 175.1458, 69.8354 ], [ 175.1401, 69.8349 ], [ 175.1438, 69.8313 ], [ 175.1687, 69.8313 ], [ 175.1714, 69.8349 ], [ 175.1807, 69.8359 ], [ 175.1792, 69.8375 ], [ 175.1708, 69.8375 ], [ 175.1693, 69.8391 ], [ 175.1792, 69.8396 ], [ 175.1833, 69.8417 ], [ 175.2021, 69.8417 ], [ 175.2063, 69.8438 ], [ 175.2375, 69.8438 ], [ 175.2391, 69.8422 ], [ 175.2312, 69.8417 ], [ 175.2271, 69.8396 ], [ 175.2188, 69.8396 ], [ 175.2172, 69.838 ], [ 175.2208, 69.8354 ], [ 175.2307, 69.8349 ], [ 175.2318, 69.8318 ], [ 175.2417, 69.8292 ], [ 175.2479, 69.8292 ], [ 175.2646, 69.8354 ], [ 175.2708, 69.8313 ], [ 175.2792, 69.8313 ], [ 175.287, 69.8287 ], [ 175.2901, 69.8255 ], [ 175.2979, 69.8229 ], [ 175.3062, 69.8229 ], [ 175.3104, 69.8208 ], [ 175.3188, 69.8208 ], [ 175.3229, 69.8229 ], [ 175.3417, 69.8229 ], [ 175.3458, 69.825 ], [ 175.3542, 69.8229 ], [ 175.3599, 69.8255 ], [ 175.3609, 69.8287 ], [ 175.3687, 69.8313 ], [ 175.3875, 69.8313 ], [ 175.3917, 69.8292 ], [ 175.3979, 69.8313 ], [ 175.425, 69.8313 ], [ 175.4292, 69.8292 ], [ 175.4604, 69.8313 ], [ 175.4667, 69.8292 ], [ 175.475, 69.8333 ], [ 175.4896, 69.8333 ], [ 175.4937, 69.8354 ], [ 175.5016, 69.8359 ], [ 175.5026, 69.8391 ], [ 175.5057, 69.8401 ], [ 175.5068, 69.8453 ], [ 175.5161, 69.8464 ], [ 175.5151, 69.8516 ], [ 175.5203, 69.8526 ], [ 175.5193, 69.8578 ], [ 175.5245, 69.8588 ], [ 175.5234, 69.862 ], [ 175.525, 69.8625 ], [ 175.5688, 69.8625 ], [ 175.5729, 69.8646 ], [ 175.6, 69.8646 ], [ 175.6016, 69.863 ], [ 175.5938, 69.8604 ], [ 175.5813, 69.8604 ], [ 175.5771, 69.8583 ], [ 175.5688, 69.8583 ], [ 175.5568, 69.8526 ], [ 175.5641, 69.8516 ], [ 175.5589, 69.8484 ], [ 175.5688, 69.8479 ], [ 175.5833, 69.8542 ], [ 175.587, 69.8536 ], [ 175.587, 69.8505 ], [ 175.5776, 69.8474 ], [ 175.5771, 69.8458 ], [ 175.5818, 69.8359 ], [ 175.5979, 69.8354 ], [ 175.6021, 69.8333 ], [ 175.6104, 69.8333 ], [ 175.6146, 69.8313 ], [ 175.6208, 69.8333 ], [ 175.6333, 69.8333 ], [ 175.6375, 69.8354 ], [ 175.6458, 69.8354 ], [ 175.6583, 69.8313 ], [ 175.6662, 69.8339 ], [ 175.6651, 69.837 ], [ 175.6708, 69.8375 ], [ 175.6708, 69.8354 ], [ 175.6734, 69.8339 ], [ 175.6849, 69.8328 ], [ 175.6797, 69.8307 ], [ 175.6812, 69.8292 ], [ 175.6958, 69.8292 ], [ 175.7021, 69.8313 ], [ 175.7063, 69.8292 ], [ 175.7146, 69.8292 ], [ 175.7188, 69.8271 ], [ 175.7271, 69.8271 ], [ 175.7333, 69.8292 ], [ 175.7359, 69.8255 ], [ 175.7412, 69.8245 ], [ 175.7443, 69.8193 ], [ 175.7495, 69.8182 ], [ 175.7516, 69.8161 ], [ 175.7516, 69.813 ], [ 175.75, 69.8125 ], [ 175.7359, 69.812 ], [ 175.7375, 69.8104 ], [ 175.7474, 69.8099 ], [ 175.7474, 69.8068 ], [ 175.7443, 69.8047 ], [ 175.7521, 69.8042 ], [ 175.7563, 69.8063 ], [ 175.7646, 69.8063 ], [ 175.7688, 69.8083 ], [ 175.7833, 69.8083 ], [ 175.7859, 69.8047 ], [ 175.7958, 69.8021 ], [ 175.8083, 69.8063 ], [ 175.8109, 69.8026 ], [ 175.8161, 69.8005 ], [ 175.813, 69.7995 ], [ 175.812, 69.7963 ], [ 175.8068, 69.7943 ], [ 175.8125, 69.7937 ], [ 175.8328, 69.787 ], [ 175.8297, 69.7849 ], [ 175.8313, 69.7833 ], [ 175.8474, 69.7828 ], [ 175.8495, 69.7776 ], [ 175.8443, 69.7755 ], [ 175.8542, 69.775 ], [ 175.862, 69.7724 ], [ 175.8667, 69.7688 ], [ 175.8807, 69.7693 ], [ 175.8818, 69.7745 ], [ 175.8849, 69.7766 ], [ 175.8734, 69.7797 ], [ 175.8687, 69.7833 ], [ 175.8604, 69.7833 ], [ 175.8589, 69.7849 ], [ 175.8911, 69.7963 ], [ 175.8953, 69.7984 ], [ 175.8979, 69.8021 ], [ 175.9016, 69.8016 ], [ 175.9036, 69.7943 ], [ 175.8943, 69.7911 ], [ 175.8943, 69.7859 ], [ 175.8963, 69.7839 ], [ 175.9016, 69.7828 ], [ 175.8963, 69.7797 ], [ 175.9036, 69.7786 ], [ 175.9047, 69.7755 ], [ 175.9099, 69.7745 ], [ 175.9109, 69.7714 ], [ 175.9208, 69.7688 ], [ 175.9479, 69.7688 ], [ 175.9521, 69.7667 ], [ 175.9646, 69.7667 ], [ 175.9708, 69.7688 ], [ 175.975, 69.7667 ], [ 175.9833, 69.7667 ], [ 175.9979, 69.7708 ], [ 176.0479, 69.7708 ], [ 176.0521, 69.7688 ], [ 176.0667, 69.7688 ], [ 176.0708, 69.7667 ], [ 176.0771, 69.7688 ], [ 176.0854, 69.7688 ], [ 176.0917, 69.7667 ], [ 176.0958, 69.7688 ], [ 176.1083, 69.7688 ], [ 176.1125, 69.7708 ], [ 176.1271, 69.7708 ], [ 176.1458, 69.7792 ], [ 176.1521, 69.7771 ], [ 176.1557, 69.7776 ], [ 176.1557, 69.7807 ], [ 176.15, 69.7813 ], [ 176.1422, 69.7849 ], [ 176.1578, 69.7901 ], [ 176.1589, 69.7932 ], [ 176.1682, 69.7963 ], [ 176.1667, 69.7979 ], [ 176.1568, 69.7984 ], [ 176.1599, 69.8005 ], [ 176.1609, 69.8057 ], [ 176.1703, 69.8089 ], [ 176.1687, 69.8104 ], [ 176.163, 69.8109 ], [ 176.1609, 69.8182 ], [ 176.1662, 69.8193 ], [ 176.1667, 69.8208 ], [ 176.1662, 69.8224 ], [ 176.1604, 69.8229 ], [ 176.1589, 69.8245 ], [ 176.1641, 69.8266 ], [ 176.1526, 69.8297 ], [ 176.1505, 69.8318 ], [ 176.1474, 69.8411 ], [ 176.1422, 69.8422 ], [ 176.1391, 69.8453 ], [ 176.1276, 69.8484 ], [ 176.1307, 69.8516 ], [ 176.1208, 69.8521 ], [ 176.1068, 69.8568 ], [ 176.1037, 69.8599 ], [ 176.0958, 69.8625 ], [ 176.0875, 69.8625 ], [ 176.0771, 69.8667 ], [ 176.0688, 69.8667 ], [ 176.0646, 69.8688 ], [ 176.025, 69.8688 ], [ 176.0104, 69.8646 ], [ 175.9917, 69.8625 ], [ 175.9839, 69.8599 ], [ 175.9849, 69.8568 ], [ 175.975, 69.8562 ], [ 175.963, 69.8516 ], [ 175.962, 69.8484 ], [ 175.9578, 69.8464 ], [ 175.95, 69.8438 ], [ 175.9438, 69.8438 ], [ 175.9401, 69.8464 ], [ 175.9391, 69.8516 ], [ 175.9167, 69.8521 ], [ 175.912, 69.8557 ], [ 175.9005, 69.8588 ], [ 175.9021, 69.8604 ], [ 175.9099, 69.8609 ], [ 175.9099, 69.8641 ], [ 175.9083, 69.8646 ], [ 175.8813, 69.8646 ], [ 175.875, 69.8667 ], [ 175.8708, 69.8646 ], [ 175.8583, 69.8646 ], [ 175.8542, 69.8625 ], [ 175.8396, 69.8625 ], [ 175.8354, 69.8667 ], [ 175.8109, 69.8693 ], [ 175.8047, 69.8714 ], [ 175.8021, 69.875 ], [ 175.7958, 69.8729 ], [ 175.7521, 69.8729 ], [ 175.7484, 69.8755 ], [ 175.75, 69.8771 ], [ 175.7646, 69.8771 ], [ 175.7688, 69.8792 ], [ 175.775, 69.8792 ], [ 175.7792, 69.8771 ], [ 175.8125, 69.8771 ], [ 175.8167, 69.875 ], [ 175.8438, 69.875 ], [ 175.8479, 69.8729 ], [ 175.8687, 69.8729 ], [ 175.8729, 69.8708 ], [ 175.8938, 69.8708 ], [ 175.9042, 69.8667 ], [ 175.9104, 69.8688 ], [ 175.9167, 69.8667 ], [ 175.9729, 69.8667 ], [ 175.9771, 69.8688 ], [ 176.0208, 69.8708 ], [ 176.025, 69.8729 ], [ 176.0375, 69.8729 ], [ 176.0417, 69.875 ], [ 176.0604, 69.875 ], [ 176.0646, 69.8771 ], [ 176.0917, 69.8771 ], [ 176.0958, 69.875 ], [ 176.1042, 69.8771 ], [ 176.1057, 69.8766 ], [ 176.1089, 69.8672 ], [ 176.1203, 69.8662 ], [ 176.1172, 69.8641 ], [ 176.1187, 69.8625 ], [ 176.1286, 69.862 ], [ 176.1234, 69.8588 ], [ 176.1349, 69.8578 ], [ 176.138, 69.8547 ], [ 176.1495, 69.8516 ], [ 176.1521, 69.8479 ], [ 176.1641, 69.8474 ], [ 176.1651, 69.8443 ], [ 176.1766, 69.8411 ], [ 176.1797, 69.838 ], [ 176.1911, 69.837 ], [ 176.1943, 69.8318 ], [ 176.2057, 69.8307 ], [ 176.2083, 69.8271 ], [ 176.2182, 69.8266 ], [ 176.2214, 69.8214 ], [ 176.2328, 69.8203 ], [ 176.2359, 69.8151 ], [ 176.2437, 69.8125 ], [ 176.2536, 69.812 ], [ 176.2568, 69.8068 ], [ 176.2682, 69.8037 ], [ 176.2693, 69.8005 ], [ 176.2828, 69.7974 ], [ 176.2839, 69.7943 ], [ 176.2974, 69.7911 ], [ 176.2979, 69.7875 ], [ 176.2958, 69.7854 ], [ 176.2875, 69.7854 ], [ 176.2813, 69.7875 ], [ 176.2771, 69.7854 ], [ 176.2708, 69.7854 ], [ 176.2604, 69.7813 ], [ 176.2542, 69.7813 ], [ 176.2437, 69.7771 ], [ 176.2375, 69.7771 ], [ 176.2193, 69.7724 ], [ 176.2266, 69.7682 ], [ 176.2214, 69.7651 ], [ 176.2266, 69.7641 ], [ 176.2312, 69.7604 ], [ 176.2458, 69.7604 ], [ 176.2484, 69.7568 ], [ 176.2563, 69.7542 ], [ 176.2875, 69.7542 ], [ 176.2917, 69.7562 ], [ 176.3292, 69.7562 ], [ 176.337, 69.7589 ], [ 176.3396, 69.7625 ], [ 176.3521, 69.7625 ], [ 176.3562, 69.7604 ], [ 176.3646, 69.7604 ], [ 176.3708, 69.7562 ], [ 176.3854, 69.7562 ], [ 176.3995, 69.7516 ], [ 176.4026, 69.7464 ], [ 176.4141, 69.7453 ], [ 176.4151, 69.7422 ], [ 176.4287, 69.7391 ], [ 176.4297, 69.7359 ], [ 176.4375, 69.7333 ], [ 176.4458, 69.7333 ], [ 176.45, 69.7312 ], [ 176.4583, 69.7312 ], [ 176.4625, 69.7292 ], [ 176.4771, 69.7292 ], [ 176.4812, 69.7271 ], [ 176.4958, 69.7271 ], [ 176.5, 69.725 ], [ 176.5083, 69.725 ], [ 176.5458, 69.7125 ], [ 176.5542, 69.7125 ], [ 176.5583, 69.7104 ], [ 176.5625, 69.7104 ], [ 176.5854, 69.7021 ], [ 176.5938, 69.7021 ], [ 176.5979, 69.7 ], [ 176.6187, 69.7 ], [ 176.6229, 69.6979 ], [ 176.6438, 69.6958 ], [ 176.6625, 69.6896 ], [ 176.6771, 69.6896 ], [ 176.6787, 69.688 ], [ 176.6734, 69.6859 ], [ 176.6896, 69.6854 ], [ 176.7, 69.6813 ], [ 176.7083, 69.6813 ], [ 176.7271, 69.675 ], [ 176.7495, 69.6724 ], [ 176.7495, 69.6693 ], [ 176.7401, 69.6682 ], [ 176.7479, 69.6646 ], [ 176.7563, 69.6646 ], [ 176.7604, 69.6625 ], [ 176.7917, 69.6646 ], [ 176.7958, 69.6625 ], [ 176.8042, 69.6625 ], [ 176.8229, 69.6563 ], [ 176.8313, 69.6563 ], [ 176.8354, 69.6542 ], [ 176.85, 69.6542 ], [ 176.8542, 69.65 ], [ 176.875, 69.6479 ], [ 176.8854, 69.6438 ], [ 176.8938, 69.6438 ], [ 176.8979, 69.6417 ], [ 176.9125, 69.6417 ], [ 176.9229, 69.6375 ], [ 176.9287, 69.637 ], [ 176.9312, 69.6333 ], [ 176.9396, 69.6333 ], [ 176.9474, 69.6307 ], [ 176.9484, 69.6276 ], [ 176.9521, 69.625 ], [ 176.9583, 69.625 ], [ 176.9625, 69.6271 ], [ 177.0063, 69.6271 ], [ 177.0125, 69.6229 ], [ 177.025, 69.6229 ], [ 177.0292, 69.625 ], [ 177.0375, 69.625 ], [ 177.0417, 69.6229 ], [ 177.0516, 69.6224 ], [ 177.05, 69.6208 ], [ 177.0297, 69.6203 ], [ 177.0375, 69.6167 ], [ 177.0437, 69.6188 ], [ 177.0625, 69.6167 ], [ 177.0667, 69.6208 ], [ 177.0729, 69.6188 ], [ 177.0875, 69.6188 ], [ 177.0979, 69.6146 ], [ 177.1078, 69.6141 ], [ 177.1104, 69.6104 ], [ 177.1438, 69.6083 ], [ 177.1458, 69.6062 ], [ 177.1422, 69.6026 ], [ 177.1474, 69.6016 ], [ 177.15, 69.5979 ], [ 177.1563, 69.6 ], [ 177.1646, 69.6 ], [ 177.1687, 69.5979 ], [ 177.1771, 69.5979 ], [ 177.1812, 69.5958 ], [ 177.1875, 69.5979 ], [ 177.2, 69.5979 ], [ 177.2063, 69.5958 ], [ 177.2104, 69.5979 ], [ 177.225, 69.5979 ], [ 177.2292, 69.6 ], [ 177.2417, 69.6 ], [ 177.2479, 69.5979 ], [ 177.2583, 69.6021 ], [ 177.3, 69.6021 ], [ 177.3062, 69.6042 ], [ 177.3078, 69.6026 ], [ 177.3047, 69.6016 ], [ 177.3037, 69.5984 ], [ 177.2984, 69.5974 ], [ 177.3005, 69.5901 ], [ 177.3083, 69.5875 ], [ 177.3167, 69.5875 ], [ 177.3208, 69.5854 ], [ 177.3271, 69.5875 ], [ 177.337, 69.588 ], [ 177.3359, 69.5911 ], [ 177.3411, 69.5922 ], [ 177.3438, 69.5958 ], [ 177.3641, 69.5953 ], [ 177.3599, 69.5922 ], [ 177.3547, 69.5911 ], [ 177.3547, 69.588 ], [ 177.3562, 69.5875 ], [ 177.3662, 69.588 ], [ 177.3672, 69.5911 ], [ 177.3729, 69.5938 ], [ 177.3813, 69.5938 ], [ 177.3854, 69.5917 ], [ 177.4354, 69.5917 ], [ 177.4432, 69.5891 ], [ 177.4479, 69.5833 ], [ 177.4583, 69.5875 ], [ 177.4792, 69.5854 ], [ 177.4807, 69.587 ], [ 177.4755, 69.5891 ], [ 177.4833, 69.5896 ], [ 177.4958, 69.5854 ], [ 177.5271, 69.5833 ], [ 177.5318, 69.5797 ], [ 177.5396, 69.5771 ], [ 177.562, 69.5745 ], [ 177.563, 69.5714 ], [ 177.5771, 69.5667 ], [ 177.5854, 69.5667 ], [ 177.6042, 69.5604 ], [ 177.6167, 69.5604 ], [ 177.6208, 69.5583 ], [ 177.6292, 69.5583 ], [ 177.6339, 69.5547 ], [ 177.6417, 69.5521 ], [ 177.6516, 69.5516 ], [ 177.6542, 69.5479 ], [ 177.6641, 69.5474 ], [ 177.6641, 69.5443 ], [ 177.6547, 69.5432 ], [ 177.6536, 69.5401 ], [ 177.6443, 69.537 ], [ 177.6453, 69.5297 ], [ 177.6401, 69.5286 ], [ 177.6391, 69.5255 ], [ 177.6359, 69.5234 ], [ 177.6474, 69.5203 ], [ 177.6484, 69.5172 ], [ 177.6542, 69.5167 ], [ 177.6646, 69.5125 ], [ 177.6896, 69.5125 ], [ 177.6938, 69.5104 ], [ 177.7, 69.5125 ], [ 177.7083, 69.5125 ], [ 177.7125, 69.5146 ], [ 177.7188, 69.5146 ], [ 177.7229, 69.5125 ], [ 177.75, 69.5125 ], [ 177.7557, 69.5151 ], [ 177.7568, 69.5182 ], [ 177.7667, 69.5188 ], [ 177.7833, 69.5125 ], [ 177.7979, 69.5125 ], [ 177.8021, 69.5083 ], [ 177.8104, 69.5083 ], [ 177.8208, 69.5042 ], [ 177.8292, 69.5042 ], [ 177.8333, 69.5021 ], [ 177.8417, 69.5021 ], [ 177.8443, 69.4984 ], [ 177.8542, 69.4979 ], [ 177.8646, 69.4938 ], [ 177.8854, 69.4917 ], [ 177.8958, 69.4875 ], [ 177.9167, 69.4854 ], [ 177.9271, 69.4812 ], [ 177.9479, 69.4792 ], [ 177.9708, 69.4729 ], [ 177.9792, 69.475 ], [ 177.9958, 69.4688 ], [ 178.0104, 69.4688 ], [ 178.0146, 69.4667 ], [ 178.0292, 69.4667 ], [ 178.0333, 69.4646 ], [ 178.0417, 69.4646 ], [ 178.0521, 69.4604 ], [ 178.0792, 69.4604 ], [ 178.0833, 69.4583 ], [ 178.0917, 69.4583 ], [ 178.0958, 69.4563 ], [ 178.1396, 69.4563 ], [ 178.1438, 69.4542 ], [ 178.15, 69.4563 ], [ 178.1542, 69.4542 ], [ 178.1687, 69.4542 ], [ 178.1729, 69.4521 ], [ 178.1938, 69.4521 ], [ 178.1979, 69.45 ], [ 178.2042, 69.4521 ], [ 178.2229, 69.4521 ], [ 178.2307, 69.4495 ], [ 178.2333, 69.4458 ], [ 178.2396, 69.4479 ], [ 178.2479, 69.4479 ], [ 178.2521, 69.45 ], [ 178.2646, 69.45 ], [ 178.2661, 69.4495 ], [ 178.2661, 69.4464 ], [ 178.263, 69.4453 ], [ 178.262, 69.4422 ], [ 178.2526, 69.4411 ], [ 178.2516, 69.438 ], [ 178.2464, 69.4359 ], [ 178.275, 69.425 ], [ 178.2833, 69.425 ], [ 178.2875, 69.4229 ], [ 178.3062, 69.4229 ], [ 178.3104, 69.425 ], [ 178.3188, 69.425 ], [ 178.3245, 69.4276 ], [ 178.3193, 69.4307 ], [ 178.3245, 69.4318 ], [ 178.3255, 69.4349 ], [ 178.3313, 69.4375 ], [ 178.3396, 69.4375 ], [ 178.3422, 69.4339 ], [ 178.3474, 69.4339 ], [ 178.3484, 69.437 ], [ 178.3516, 69.4391 ], [ 178.3401, 69.4432 ], [ 178.3521, 69.4479 ], [ 178.3786, 69.4484 ], [ 178.3786, 69.4537 ], [ 178.3672, 69.4568 ], [ 178.3687, 69.4583 ], [ 178.4063, 69.4583 ], [ 178.4104, 69.4563 ], [ 178.4354, 69.4563 ], [ 178.437, 69.4547 ], [ 178.4104, 69.4542 ], [ 178.4083, 69.4521 ], [ 178.4104, 69.45 ], [ 178.4167, 69.4521 ], [ 178.4312, 69.4521 ], [ 178.4354, 69.45 ], [ 178.4542, 69.45 ], [ 178.4583, 69.4479 ], [ 178.4979, 69.4479 ], [ 178.5083, 69.4437 ], [ 178.5271, 69.4437 ], [ 178.5313, 69.4417 ], [ 178.5396, 69.4437 ], [ 178.5437, 69.4417 ], [ 178.5521, 69.4417 ], [ 178.5562, 69.4396 ], [ 178.5708, 69.4396 ], [ 178.5734, 69.4359 ], [ 178.5833, 69.4354 ], [ 178.5875, 69.4333 ], [ 178.5938, 69.4354 ], [ 178.5979, 69.4333 ], [ 178.6062, 69.4333 ], [ 178.6078, 69.4318 ], [ 178.5984, 69.4307 ], [ 178.6062, 69.4271 ], [ 178.625, 69.4271 ], [ 178.6292, 69.4292 ], [ 178.6375, 69.4292 ], [ 178.6391, 69.4276 ], [ 178.6339, 69.4255 ], [ 178.6359, 69.4234 ], [ 178.6411, 69.4224 ], [ 178.6438, 69.4187 ], [ 178.6646, 69.4167 ], [ 178.675, 69.4125 ], [ 178.6958, 69.4104 ], [ 178.7063, 69.4063 ], [ 178.7271, 69.4042 ], [ 178.7375, 69.4 ], [ 178.7458, 69.4 ], [ 178.75, 69.3979 ], [ 178.7583, 69.3979 ], [ 178.7625, 69.3958 ], [ 178.775, 69.3958 ], [ 178.7792, 69.3979 ], [ 178.7875, 69.3979 ], [ 178.7953, 69.3953 ], [ 178.7901, 69.3922 ], [ 178.7979, 69.3896 ], [ 178.8125, 69.3896 ], [ 178.8167, 69.3875 ], [ 178.8375, 69.3854 ], [ 178.8479, 69.3812 ], [ 178.8562, 69.3812 ], [ 178.8604, 69.3792 ], [ 178.875, 69.3792 ], [ 178.8776, 69.3755 ], [ 178.8854, 69.3729 ], [ 178.8938, 69.3729 ], [ 178.8979, 69.3688 ], [ 178.9063, 69.3688 ], [ 178.9167, 69.3646 ], [ 178.925, 69.3646 ], [ 178.9292, 69.3625 ], [ 178.9375, 69.3625 ], [ 178.9401, 69.3588 ], [ 178.9464, 69.3568 ], [ 178.9542, 69.3542 ], [ 178.9641, 69.3536 ], [ 178.9651, 69.3484 ], [ 178.975, 69.3479 ], [ 178.9828, 69.3453 ], [ 178.9859, 69.3401 ], [ 179.0063, 69.3333 ], [ 179.0146, 69.3333 ], [ 179.0172, 69.3297 ], [ 179.0271, 69.3292 ], [ 179.0349, 69.3266 ], [ 179.0359, 69.3234 ], [ 179.0458, 69.3229 ], [ 179.0484, 69.3193 ], [ 179.0583, 69.3187 ], [ 179.0609, 69.3151 ], [ 179.0813, 69.3083 ], [ 179.0896, 69.3083 ], [ 179.0932, 69.3047 ], [ 179.0833, 69.3042 ], [ 179.0766, 69.3005 ], [ 179.0667, 69.2979 ], [ 179.0625, 69.3 ], [ 179.0479, 69.3 ], [ 179.0437, 69.3021 ], [ 179.0354, 69.3021 ], [ 179.0328, 69.3057 ], [ 179.0213, 69.3068 ], [ 179.0188, 69.3104 ], [ 179.0063, 69.3104 ], [ 178.9896, 69.3042 ], [ 178.9771, 69.3042 ], [ 178.9729, 69.3021 ], [ 178.9667, 69.3021 ], [ 178.9588, 69.2995 ], [ 178.9578, 69.2963 ], [ 178.95, 69.2937 ], [ 178.9484, 69.2943 ], [ 178.9484, 69.2974 ], [ 178.9516, 69.2984 ], [ 178.9526, 69.3037 ], [ 178.9557, 69.3047 ], [ 178.9505, 69.3078 ], [ 178.9557, 69.3089 ], [ 178.9568, 69.3141 ], [ 178.9599, 69.3151 ], [ 178.9609, 69.3182 ], [ 178.9661, 69.3193 ], [ 178.9651, 69.3245 ], [ 178.9703, 69.3255 ], [ 178.9708, 69.3271 ], [ 178.9682, 69.3307 ], [ 178.9505, 69.3359 ], [ 178.9495, 69.3391 ], [ 178.9417, 69.3417 ], [ 178.9333, 69.3417 ], [ 178.9229, 69.3458 ], [ 178.9042, 69.3458 ], [ 178.9, 69.3438 ], [ 178.8854, 69.3438 ], [ 178.8839, 69.3443 ], [ 178.8828, 69.3495 ], [ 178.8568, 69.3568 ], [ 178.8542, 69.3604 ], [ 178.8229, 69.3583 ], [ 178.8188, 69.3562 ], [ 178.7937, 69.3562 ], [ 178.7896, 69.3542 ], [ 178.7849, 69.3578 ], [ 178.7771, 69.3604 ], [ 178.7583, 69.3625 ], [ 178.7521, 69.3667 ], [ 178.7396, 69.3667 ], [ 178.7318, 69.3641 ], [ 178.7307, 69.3609 ], [ 178.7276, 69.3599 ], [ 178.7276, 69.3568 ], [ 178.7437, 69.3562 ], [ 178.7479, 69.3583 ], [ 178.7516, 69.3557 ], [ 178.7526, 69.3526 ], [ 178.7578, 69.3505 ], [ 178.7464, 69.3495 ], [ 178.7479, 69.3458 ], [ 178.7474, 69.3422 ], [ 178.738, 69.3391 ], [ 178.737, 69.3339 ], [ 178.7339, 69.3328 ], [ 178.7328, 69.3276 ], [ 178.7297, 69.3266 ], [ 178.7292, 69.3208 ], [ 178.7307, 69.3172 ], [ 178.7255, 69.3161 ], [ 178.7245, 69.313 ], [ 178.7203, 69.3109 ], [ 178.7151, 69.3099 ], [ 178.7099, 69.3026 ], [ 178.7047, 69.3016 ], [ 178.7047, 69.2984 ], [ 178.712, 69.2974 ], [ 178.7125, 69.2958 ], [ 178.712, 69.288 ], [ 178.7088, 69.2859 ], [ 178.7203, 69.2828 ], [ 178.7151, 69.2797 ], [ 178.7229, 69.2771 ], [ 178.7312, 69.2771 ], [ 178.7359, 69.2734 ], [ 178.7437, 69.2708 ], [ 178.7563, 69.2708 ], [ 178.7609, 69.2672 ], [ 178.7672, 69.2651 ], [ 178.7771, 69.2646 ], [ 178.7875, 69.2604 ], [ 178.8, 69.2604 ], [ 178.8146, 69.2646 ], [ 178.8229, 69.2646 ], [ 178.8255, 69.2609 ], [ 178.8354, 69.2604 ], [ 178.8432, 69.263 ], [ 178.8422, 69.2662 ], [ 178.8438, 69.2667 ], [ 178.8583, 69.2667 ], [ 178.8729, 69.2708 ], [ 178.9042, 69.2729 ], [ 178.9083, 69.275 ], [ 178.9208, 69.275 ], [ 178.9312, 69.2708 ], [ 178.9625, 69.2708 ], [ 178.9667, 69.2729 ], [ 178.9812, 69.2729 ], [ 178.9828, 69.2714 ], [ 178.9776, 69.2693 ], [ 178.9828, 69.2682 ], [ 178.9854, 69.2646 ], [ 179.0167, 69.2646 ], [ 179.0208, 69.2667 ], [ 179.0292, 69.2667 ], [ 179.0333, 69.2646 ], [ 179.0396, 69.2646 ], [ 179.0437, 69.2667 ], [ 179.05, 69.2646 ], [ 179.0646, 69.2646 ], [ 179.0688, 69.2667 ], [ 179.0813, 69.2667 ], [ 179.0891, 69.2693 ], [ 179.088, 69.2724 ], [ 179.0932, 69.2734 ], [ 179.0943, 69.2766 ], [ 179.0979, 69.2771 ], [ 179.1021, 69.275 ], [ 179.1099, 69.2745 ], [ 179.113, 69.2714 ], [ 179.1182, 69.2703 ], [ 179.1193, 69.2672 ], [ 179.1271, 69.2646 ], [ 179.1354, 69.2646 ], [ 179.1396, 69.2625 ], [ 179.1458, 69.2646 ], [ 179.1583, 69.2646 ], [ 179.1625, 69.2625 ], [ 179.1771, 69.2625 ], [ 179.1833, 69.2604 ], [ 179.1875, 69.2625 ], [ 179.1938, 69.2625 ], [ 179.2042, 69.2667 ], [ 179.2292, 69.2667 ], [ 179.2396, 69.2625 ], [ 179.2479, 69.2625 ], [ 179.2521, 69.2604 ], [ 179.2646, 69.2604 ], [ 179.2786, 69.2557 ], [ 179.2833, 69.2521 ], [ 179.2974, 69.2526 ], [ 179.2958, 69.2542 ], [ 179.2859, 69.2547 ], [ 179.2875, 69.2562 ], [ 179.3021, 69.2562 ], [ 179.3062, 69.2542 ], [ 179.3146, 69.2542 ], [ 179.3172, 69.2505 ], [ 179.3286, 69.2495 ], [ 179.3234, 69.2464 ], [ 179.3333, 69.2458 ], [ 179.35, 69.2396 ], [ 179.3583, 69.2396 ], [ 179.3599, 69.2391 ], [ 179.3609, 69.2338 ], [ 179.3724, 69.2328 ], [ 179.3734, 69.2276 ], [ 179.3849, 69.2266 ], [ 179.3818, 69.2234 ], [ 179.3932, 69.2203 ], [ 179.3943, 69.2151 ], [ 179.3995, 69.2141 ], [ 179.4005, 69.2109 ], [ 179.4208, 69.2042 ], [ 179.4292, 69.2042 ], [ 179.4318, 69.2005 ], [ 179.4417, 69.2 ], [ 179.4464, 69.1964 ], [ 179.4542, 69.1937 ], [ 179.462, 69.1932 ], [ 179.4651, 69.1901 ], [ 179.4828, 69.1849 ], [ 179.4839, 69.1818 ], [ 179.5016, 69.1766 ], [ 179.5026, 69.1734 ], [ 179.5104, 69.1708 ], [ 179.5203, 69.1703 ], [ 179.5213, 69.1672 ], [ 179.5391, 69.162 ], [ 179.5401, 69.1589 ], [ 179.5661, 69.1495 ], [ 179.5672, 69.1464 ], [ 179.5849, 69.1412 ], [ 179.5859, 69.138 ], [ 179.5974, 69.1349 ], [ 179.5984, 69.1318 ], [ 179.6125, 69.1271 ], [ 179.6224, 69.1266 ], [ 179.6234, 69.1234 ], [ 179.6349, 69.1203 ], [ 179.6359, 69.1172 ], [ 179.6536, 69.112 ], [ 179.6568, 69.1068 ], [ 179.6771, 69.1 ], [ 179.6854, 69.1 ], [ 179.6932, 69.0974 ], [ 179.688, 69.0943 ], [ 179.6995, 69.0932 ], [ 179.7005, 69.0901 ], [ 179.7057, 69.0891 ], [ 179.7068, 69.0859 ], [ 179.712, 69.0849 ], [ 179.713, 69.0818 ], [ 179.7229, 69.0813 ], [ 179.7255, 69.0776 ], [ 179.7318, 69.0755 ], [ 179.7458, 69.0708 ], [ 179.7557, 69.0703 ], [ 179.7568, 69.0672 ], [ 179.7646, 69.0646 ], [ 179.7745, 69.0641 ], [ 179.7651, 69.0609 ], [ 179.7766, 69.0599 ], [ 179.7776, 69.0568 ], [ 179.7891, 69.0537 ], [ 179.7901, 69.0505 ], [ 179.7953, 69.0495 ], [ 179.7964, 69.0463 ], [ 179.8062, 69.0458 ], [ 179.8141, 69.0432 ], [ 179.8151, 69.0401 ], [ 179.8292, 69.0354 ], [ 179.8391, 69.0349 ], [ 179.8401, 69.0318 ], [ 179.8479, 69.0292 ], [ 179.8625, 69.0292 ], [ 179.8641, 69.0286 ], [ 179.8651, 69.0234 ], [ 179.8703, 69.0224 ], [ 179.8687, 69.0208 ], [ 179.8562, 69.0208 ], [ 179.8547, 69.0193 ], [ 179.8875, 69.0188 ], [ 179.8953, 69.0162 ], [ 179.8963, 69.0109 ], [ 179.9078, 69.0078 ], [ 179.9089, 69.0047 ], [ 179.9141, 69.0036 ], [ 179.9146, 69.0021 ], [ 179.9125, 69.0 ], [ 179.9, 69.0 ], [ 179.8984, 68.9984 ], [ 179.925, 68.9979 ], [ 179.9328, 68.9953 ], [ 179.9359, 68.9922 ], [ 179.9625, 68.9833 ], [ 179.9688, 68.9833 ], [ 179.9766, 68.9797 ], [ 179.9625, 68.9792 ], [ 179.9583, 68.9771 ], [ 179.9458, 68.9771 ], [ 179.938, 68.9797 ], [ 179.937, 68.9828 ], [ 179.9271, 68.9833 ], [ 179.9245, 68.987 ], [ 179.9146, 68.9875 ], [ 179.912, 68.9912 ], [ 179.9042, 68.9938 ], [ 179.8958, 68.9917 ], [ 179.8818, 68.9964 ], [ 179.8807, 69.0016 ], [ 179.8693, 69.0047 ], [ 179.8682, 69.0078 ], [ 179.8479, 69.0146 ], [ 179.8396, 69.0146 ], [ 179.8255, 69.0193 ], [ 179.8286, 69.0224 ], [ 179.8234, 69.0234 ], [ 179.8224, 69.0266 ], [ 179.8083, 69.0271 ], [ 179.8057, 69.0307 ], [ 179.7917, 69.0354 ], [ 179.7818, 69.0359 ], [ 179.7792, 69.0396 ], [ 179.7583, 69.0396 ], [ 179.75, 69.0354 ], [ 179.7437, 69.0375 ], [ 179.7354, 69.0375 ], [ 179.7339, 69.0391 ], [ 179.7432, 69.0422 ], [ 179.7432, 69.0516 ], [ 179.738, 69.0526 ], [ 179.7333, 69.0563 ], [ 179.7146, 69.0583 ], [ 179.7125, 69.0604 ], [ 179.7146, 69.0625 ], [ 179.7271, 69.0625 ], [ 179.7287, 69.0641 ], [ 179.7063, 69.0667 ], [ 179.7036, 69.0703 ], [ 179.6984, 69.0714 ], [ 179.6974, 69.0745 ], [ 179.6896, 69.0771 ], [ 179.6812, 69.0771 ], [ 179.6672, 69.0818 ], [ 179.6724, 69.0849 ], [ 179.6589, 69.0901 ], [ 179.6578, 69.0932 ], [ 179.6526, 69.0943 ], [ 179.6516, 69.0995 ], [ 179.6292, 69.1021 ], [ 179.6276, 69.1026 ], [ 179.6266, 69.1078 ], [ 179.6042, 69.1104 ], [ 179.6, 69.1125 ], [ 179.5859, 69.1099 ], [ 179.5911, 69.1068 ], [ 179.5818, 69.1057 ], [ 179.5807, 69.0964 ], [ 179.5776, 69.0953 ], [ 179.5766, 69.0922 ], [ 179.5714, 69.0911 ], [ 179.5734, 69.088 ], [ 179.5813, 69.0875 ], [ 179.5854, 69.0896 ], [ 179.6016, 69.0891 ], [ 179.5964, 69.087 ], [ 179.5911, 69.0797 ], [ 179.5859, 69.0787 ], [ 179.587, 69.0714 ], [ 179.5818, 69.0693 ], [ 179.5958, 69.0646 ], [ 179.6104, 69.0646 ], [ 179.6146, 69.0625 ], [ 179.6271, 69.0625 ], [ 179.6313, 69.0604 ], [ 179.6396, 69.0604 ], [ 179.6474, 69.0578 ], [ 179.6484, 69.0547 ], [ 179.6547, 69.0526 ], [ 179.6687, 69.0479 ], [ 179.6771, 69.0479 ], [ 179.6911, 69.0432 ], [ 179.6911, 69.0401 ], [ 179.688, 69.038 ], [ 179.6995, 69.0349 ], [ 179.6943, 69.0328 ], [ 179.6964, 69.0255 ], [ 179.7078, 69.0224 ], [ 179.6984, 69.0182 ], [ 179.7036, 69.0162 ], [ 179.6984, 69.0141 ], [ 179.6979, 69.0125 ], [ 179.6984, 69.0109 ], [ 179.7036, 69.0089 ], [ 179.7005, 69.0078 ], [ 179.6995, 69.0047 ], [ 179.6943, 69.0026 ], [ 179.7057, 69.0016 ], [ 179.7068, 68.9984 ], [ 179.7182, 68.9974 ], [ 179.7208, 68.9938 ], [ 179.7354, 68.9938 ], [ 179.7458, 68.9896 ], [ 179.7583, 68.9896 ], [ 179.7625, 68.9875 ], [ 179.7708, 68.9875 ], [ 179.7813, 68.9833 ], [ 179.7896, 68.9833 ], [ 179.7937, 68.9792 ], [ 179.8, 68.9812 ], [ 179.8062, 68.9812 ], [ 179.8141, 68.9786 ], [ 179.8188, 68.975 ], [ 179.8229, 68.9771 ], [ 179.8292, 68.975 ], [ 179.8542, 68.975 ], [ 179.8583, 68.9729 ], [ 179.8667, 68.9729 ], [ 179.8708, 68.9708 ], [ 179.8792, 68.9708 ], [ 179.8938, 68.975 ], [ 179.9063, 68.975 ], [ 179.9104, 68.9729 ], [ 179.925, 68.9729 ], [ 179.9354, 68.9688 ], [ 179.9417, 68.9688 ], [ 179.9479, 68.9708 ], [ 179.9583, 68.9667 ], [ 179.9667, 68.9667 ], [ 179.9708, 68.9688 ], [ 179.9833, 68.9688 ], [ 179.9875, 68.9708 ], [ 179.9953, 68.9714 ], [ 179.9901, 68.9745 ], [ 179.9953, 68.9755 ], [ 179.9964, 68.9807 ], [ 179.9979, 68.9812 ], [ 180.0, 68.9497 ], [ 180.0, 65.9841 ], [ 179.9979, 65.0271 ], [ 179.9875, 65.0313 ], [ 179.9771, 65.0313 ], [ 179.9703, 65.0276 ], [ 179.9646, 65.0271 ], [ 179.9578, 65.0234 ], [ 179.9479, 65.0208 ], [ 179.9271, 65.0208 ], [ 179.9229, 65.0188 ], [ 179.9125, 65.0188 ], [ 179.9016, 65.013 ], [ 179.8958, 65.0125 ], [ 179.8901, 65.0099 ], [ 179.8875, 65.0062 ], [ 179.8792, 65.0062 ], [ 179.8708, 65.0021 ], [ 179.8604, 65.0021 ], [ 179.8562, 65.0 ], [ 179.8484, 64.9995 ], [ 179.8495, 64.9964 ], [ 179.8422, 64.9932 ], [ 179.8411, 64.9901 ], [ 179.8313, 64.9854 ], [ 179.8255, 64.9849 ], [ 179.8245, 64.9818 ], [ 179.8172, 64.9807 ], [ 179.8161, 64.9776 ], [ 179.8005, 64.9703 ], [ 179.8016, 64.9672 ], [ 179.7891, 64.9609 ], [ 179.7797, 64.9599 ], [ 179.7807, 64.9568 ], [ 179.7776, 64.9557 ], [ 179.7766, 64.9526 ], [ 179.7693, 64.9516 ], [ 179.7682, 64.9484 ], [ 179.7526, 64.9411 ], [ 179.7536, 64.938 ], [ 179.7505, 64.937 ], [ 179.7536, 64.9349 ], [ 179.7505, 64.9328 ], [ 179.7516, 64.9297 ], [ 179.7422, 64.9287 ], [ 179.7432, 64.9255 ], [ 179.7339, 64.9245 ], [ 179.7255, 64.9203 ], [ 179.7266, 64.9172 ], [ 179.7208, 64.9146 ], [ 179.713, 64.9141 ], [ 179.7141, 64.9109 ], [ 179.7016, 64.9047 ], [ 179.6922, 64.9036 ], [ 179.6891, 64.8984 ], [ 179.6818, 64.8974 ], [ 179.6651, 64.8891 ], [ 179.6641, 64.8859 ], [ 179.6583, 64.8854 ], [ 179.6526, 64.8828 ], [ 179.6516, 64.8797 ], [ 179.6182, 64.863 ], [ 179.6125, 64.8625 ], [ 179.6068, 64.8599 ], [ 179.6057, 64.8568 ], [ 179.5901, 64.8495 ], [ 179.5911, 64.8464 ], [ 179.5854, 64.8438 ], [ 179.5797, 64.8432 ], [ 179.5755, 64.8411 ], [ 179.5745, 64.838 ], [ 179.5464, 64.8245 ], [ 179.5474, 64.8214 ], [ 179.5359, 64.8161 ], [ 179.5349, 64.813 ], [ 179.5318, 64.812 ], [ 179.5328, 64.8089 ], [ 179.5234, 64.8078 ], [ 179.5245, 64.8047 ], [ 179.5213, 64.8037 ], [ 179.5245, 64.8016 ], [ 179.5229, 64.8 ], [ 179.5104, 64.7979 ], [ 179.5063, 64.7958 ], [ 179.4958, 64.7958 ], [ 179.4917, 64.7937 ], [ 179.4771, 64.7937 ], [ 179.4693, 64.7963 ], [ 179.4682, 64.7995 ], [ 179.4646, 64.8 ], [ 179.4604, 64.7979 ], [ 179.4312, 64.7979 ], [ 179.4271, 64.7958 ], [ 179.4187, 64.7958 ], [ 179.4146, 64.7937 ], [ 179.3938, 64.7937 ], [ 179.3896, 64.7917 ], [ 179.3708, 64.7917 ], [ 179.3667, 64.7896 ], [ 179.3604, 64.7917 ], [ 179.3562, 64.7896 ], [ 179.3229, 64.7896 ], [ 179.3188, 64.7875 ], [ 179.2937, 64.7875 ], [ 179.2896, 64.7854 ], [ 179.2792, 64.7854 ], [ 179.2667, 64.7813 ], [ 179.2625, 64.7813 ], [ 179.2542, 64.7771 ], [ 179.2396, 64.7771 ], [ 179.2297, 64.7745 ], [ 179.2229, 64.7708 ], [ 179.2167, 64.7708 ], [ 179.2042, 64.7667 ], [ 179.1938, 64.7667 ], [ 179.1896, 64.7646 ], [ 179.1854, 64.7667 ], [ 179.1812, 64.7646 ], [ 179.1667, 64.7646 ], [ 179.1625, 64.7625 ], [ 179.1479, 64.7625 ], [ 179.1438, 64.7604 ], [ 179.1333, 64.7604 ], [ 179.1266, 64.7568 ], [ 179.1068, 64.7516 ], [ 179.0958, 64.7458 ], [ 179.0917, 64.7458 ], [ 179.0875, 64.7438 ], [ 179.0771, 64.7438 ], [ 179.062, 64.7359 ], [ 179.0562, 64.7354 ], [ 179.0354, 64.7271 ], [ 179.0292, 64.7271 ], [ 179.0224, 64.7234 ], [ 179.0151, 64.7224 ], [ 178.9984, 64.7141 ], [ 178.9953, 64.7109 ], [ 178.9859, 64.7099 ], [ 178.9661, 64.6984 ], [ 178.9568, 64.6974 ], [ 178.9578, 64.6943 ], [ 178.9505, 64.6932 ], [ 178.9354, 64.6854 ], [ 178.9276, 64.6849 ], [ 178.9287, 64.6818 ], [ 178.9214, 64.6807 ], [ 178.9078, 64.6734 ], [ 178.9005, 64.6724 ], [ 178.8828, 64.663 ], [ 178.8771, 64.6625 ], [ 178.8662, 64.6568 ], [ 178.8589, 64.6557 ], [ 178.8578, 64.6526 ], [ 178.8505, 64.6516 ], [ 178.8411, 64.6464 ], [ 178.8338, 64.6453 ], [ 178.8271, 64.6417 ], [ 178.8193, 64.6412 ], [ 178.8146, 64.6375 ], [ 178.8068, 64.637 ], [ 178.7958, 64.6312 ], [ 178.7896, 64.6312 ], [ 178.788, 64.6349 ], [ 178.7964, 64.6391 ] ] ], [ [ [ -171.9261, 65.4774 ], [ -171.9292, 65.4771 ], [ -171.9307, 65.4786 ], [ -171.9271, 65.4792 ], [ -171.9255, 65.4786 ], [ -171.9261, 65.4774 ] ] ], [ [ [ -171.8479, 65.4812 ], [ -171.8443, 65.4797 ], [ -171.8578, 65.4797 ], [ -171.8562, 65.4812 ], [ -171.8479, 65.4812 ] ] ], [ [ [ -171.6432, 65.5099 ], [ -171.6401, 65.5099 ], [ -171.6401, 65.5089 ], [ -171.6432, 65.5089 ], [ -171.6432, 65.5099 ] ] ], [ [ [ -172.6599, 65.2203 ], [ -172.6568, 65.2203 ], [ -172.6568, 65.2193 ], [ -172.6599, 65.2193 ], [ -172.6599, 65.2203 ] ] ], [ [ [ -172.2187, 65.2271 ], [ -172.213, 65.2276 ], [ -172.212, 65.2307 ], [ -172.2083, 65.2333 ], [ -172.2057, 65.2297 ], [ -172.2026, 65.2286 ], [ -172.2083, 65.2208 ], [ -172.2266, 65.2214 ], [ -172.2245, 65.2266 ], [ -172.2187, 65.2271 ] ] ], [ [ [ -172.2021, 65.4125 ], [ -172.2036, 65.4141 ], [ -172.1984, 65.4141 ], [ -172.1984, 65.413 ], [ -172.2021, 65.4125 ] ] ], [ [ [ -172.3104, 65.4146 ], [ -172.312, 65.4161 ], [ -172.3068, 65.4161 ], [ -172.3068, 65.4151 ], [ -172.3104, 65.4146 ] ] ], [ [ [ -172.2922, 65.4151 ], [ -172.3, 65.4146 ], [ -172.3016, 65.4161 ], [ -172.2937, 65.4167 ], [ -172.2922, 65.4151 ] ] ], [ [ [ -172.2375, 65.425 ], [ -172.2333, 65.425 ], [ -172.2318, 65.4234 ], [ -172.2354, 65.4208 ], [ -172.2474, 65.4213 ], [ -172.2453, 65.4245 ], [ -172.2375, 65.425 ] ] ], [ [ [ -172.1161, 65.462 ], [ -172.1109, 65.463 ], [ -172.1099, 65.4661 ], [ -172.1047, 65.4672 ], [ -172.0953, 65.4786 ], [ -172.0901, 65.4797 ], [ -172.0932, 65.4828 ], [ -172.0838, 65.4828 ], [ -172.088, 65.4776 ], [ -172.0932, 65.4766 ], [ -172.1005, 65.463 ], [ -172.1078, 65.462 ], [ -172.1109, 65.4547 ], [ -172.1161, 65.4547 ], [ -172.1146, 65.4583 ], [ -172.1161, 65.462 ] ] ], [ [ [ -172.0583, 65.4792 ], [ -172.0526, 65.4786 ], [ -172.0526, 65.4755 ], [ -172.0562, 65.475 ], [ -172.062, 65.4776 ], [ -172.0583, 65.4792 ] ] ], [ [ [ -174.7547, 64.7682 ], [ -174.7464, 64.7662 ], [ -174.7276, 64.7547 ], [ -174.7391, 64.7516 ], [ -174.7401, 64.7484 ], [ -174.7479, 64.7458 ], [ -174.7667, 64.75 ], [ -174.7682, 64.7505 ], [ -174.7672, 64.7557 ], [ -174.7734, 64.7599 ], [ -174.7807, 64.7609 ], [ -174.7792, 64.7667 ], [ -174.7828, 64.7703 ], [ -174.7729, 64.7729 ], [ -174.7547, 64.7682 ] ] ], [ [ [ -174.8141, 64.7724 ], [ -174.8109, 64.7724 ], [ -174.8109, 64.7714 ], [ -174.8141, 64.7714 ], [ -174.8141, 64.7724 ] ] ], [ [ [ -174.837, 64.7766 ], [ -174.8338, 64.7766 ], [ -174.8338, 64.7755 ], [ -174.837, 64.7755 ], [ -174.837, 64.7766 ] ] ], [ [ [ -174.8411, 64.7849 ], [ -174.838, 64.7849 ], [ -174.838, 64.7839 ], [ -174.8411, 64.7839 ], [ -174.8411, 64.7849 ] ] ], [ [ [ -174.962, 64.8141 ], [ -174.9588, 64.8141 ], [ -174.9588, 64.813 ], [ -174.962, 64.813 ], [ -174.962, 64.8141 ] ] ], [ [ [ -172.4026, 65.537 ], [ -172.4016, 65.5339 ], [ -172.3953, 65.5297 ], [ -172.387, 65.5255 ], [ -172.3813, 65.525 ], [ -172.3755, 65.5224 ], [ -172.3766, 65.5193 ], [ -172.3708, 65.5167 ], [ -172.3651, 65.5162 ], [ -172.362, 65.5109 ], [ -172.3484, 65.5099 ], [ -172.3495, 65.5068 ], [ -172.3276, 65.4953 ], [ -172.3286, 65.4922 ], [ -172.3193, 65.4912 ], [ -172.313, 65.487 ], [ -172.312, 65.4838 ], [ -172.3047, 65.4828 ], [ -172.3057, 65.4797 ], [ -172.2984, 65.4766 ], [ -172.3026, 65.4714 ], [ -172.3104, 65.4688 ], [ -172.325, 65.4688 ], [ -172.3318, 65.4724 ], [ -172.3417, 65.475 ], [ -172.3479, 65.4729 ], [ -172.3729, 65.4729 ], [ -172.3771, 65.475 ], [ -172.3875, 65.475 ], [ -172.3917, 65.4771 ], [ -172.4062, 65.4771 ], [ -172.4104, 65.475 ], [ -172.4187, 65.475 ], [ -172.4266, 65.4724 ], [ -172.4312, 65.4667 ], [ -172.4438, 65.4667 ], [ -172.4474, 65.4641 ], [ -172.4458, 65.4625 ], [ -172.4354, 65.4625 ], [ -172.4245, 65.4568 ], [ -172.4146, 65.4563 ], [ -172.4062, 65.4521 ], [ -172.4021, 65.4542 ], [ -172.3984, 65.4537 ], [ -172.3995, 65.4505 ], [ -172.3963, 65.4495 ], [ -172.3979, 65.4458 ], [ -172.3932, 65.4359 ], [ -172.3875, 65.4333 ], [ -172.3818, 65.4328 ], [ -172.3828, 65.4297 ], [ -172.3786, 65.4276 ], [ -172.3729, 65.4271 ], [ -172.3646, 65.4229 ], [ -172.3422, 65.4182 ], [ -172.338, 65.4161 ], [ -172.3391, 65.4109 ], [ -172.3292, 65.4083 ], [ -172.3188, 65.4083 ], [ -172.3125, 65.4125 ], [ -172.3021, 65.4125 ], [ -172.2937, 65.4083 ], [ -172.2854, 65.4083 ], [ -172.2812, 65.4063 ], [ -172.275, 65.4063 ], [ -172.2708, 65.4083 ], [ -172.2583, 65.4083 ], [ -172.2542, 65.4104 ], [ -172.2375, 65.4104 ], [ -172.2328, 65.4068 ], [ -172.2292, 65.4063 ], [ -172.2214, 65.4089 ], [ -172.2182, 65.412 ], [ -172.2125, 65.4125 ], [ -172.2109, 65.412 ], [ -172.2109, 65.4089 ], [ -172.2161, 65.4078 ], [ -172.2182, 65.4047 ], [ -172.2125, 65.4042 ], [ -172.2057, 65.4005 ], [ -172.1958, 65.3979 ], [ -172.1922, 65.3984 ], [ -172.1896, 65.4021 ], [ -172.1797, 65.4026 ], [ -172.1776, 65.4068 ], [ -172.1818, 65.4099 ], [ -172.1891, 65.4109 ], [ -172.1974, 65.4151 ], [ -172.1958, 65.4167 ], [ -172.1875, 65.4167 ], [ -172.1828, 65.4203 ], [ -172.1776, 65.4213 ], [ -172.1776, 65.4245 ], [ -172.1833, 65.4271 ], [ -172.1891, 65.4276 ], [ -172.1911, 65.4307 ], [ -172.175, 65.4313 ], [ -172.1625, 65.425 ], [ -172.1526, 65.4245 ], [ -172.1526, 65.4213 ], [ -172.1547, 65.4193 ], [ -172.1599, 65.4182 ], [ -172.1568, 65.4151 ], [ -172.1609, 65.4109 ], [ -172.1662, 65.4099 ], [ -172.163, 65.4078 ], [ -172.1651, 65.4047 ], [ -172.1766, 65.4016 ], [ -172.1734, 65.3984 ], [ -172.1776, 65.3901 ], [ -172.1797, 65.388 ], [ -172.1849, 65.387 ], [ -172.1818, 65.3839 ], [ -172.1901, 65.3714 ], [ -172.2016, 65.3703 ], [ -172.2, 65.3688 ], [ -172.1943, 65.3682 ], [ -172.1943, 65.3651 ], [ -172.1974, 65.3599 ], [ -172.1943, 65.3568 ], [ -172.1984, 65.3526 ], [ -172.2036, 65.3516 ], [ -172.2078, 65.3453 ], [ -172.2099, 65.3401 ], [ -172.2068, 65.338 ], [ -172.212, 65.337 ], [ -172.2141, 65.3349 ], [ -172.2161, 65.3297 ], [ -172.213, 65.3276 ], [ -172.2182, 65.3203 ], [ -172.2151, 65.3182 ], [ -172.2161, 65.3151 ], [ -172.213, 65.3141 ], [ -172.2161, 65.3068 ], [ -172.2088, 65.3057 ], [ -172.2063, 65.3021 ], [ -172.2021, 65.3021 ], [ -172.1979, 65.3 ], [ -172.1896, 65.3 ], [ -172.1797, 65.2953 ], [ -172.1807, 65.2922 ], [ -172.1776, 65.2901 ], [ -172.1917, 65.2688 ], [ -172.1896, 65.2667 ], [ -172.1839, 65.2662 ], [ -172.1849, 65.263 ], [ -172.1818, 65.2609 ], [ -172.1932, 65.2578 ], [ -172.1964, 65.2526 ], [ -172.2042, 65.25 ], [ -172.2125, 65.25 ], [ -172.2167, 65.2479 ], [ -172.2266, 65.2505 ], [ -172.2333, 65.2542 ], [ -172.2521, 65.2542 ], [ -172.2646, 65.2604 ], [ -172.2896, 65.2604 ], [ -172.2937, 65.2625 ], [ -172.3083, 65.2625 ], [ -172.3125, 65.2604 ], [ -172.3354, 65.2604 ], [ -172.3396, 65.2583 ], [ -172.35, 65.2583 ], [ -172.3542, 65.2604 ], [ -172.3583, 65.2583 ], [ -172.375, 65.2583 ], [ -172.3792, 65.2604 ], [ -172.4146, 65.2604 ], [ -172.4349, 65.2536 ], [ -172.4396, 65.25 ], [ -172.4542, 65.25 ], [ -172.4625, 65.2542 ], [ -172.4729, 65.2542 ], [ -172.4771, 65.2521 ], [ -172.4854, 65.2521 ], [ -172.4896, 65.25 ], [ -172.5125, 65.2479 ], [ -172.5229, 65.2438 ], [ -172.5375, 65.2438 ], [ -172.5391, 65.2453 ], [ -172.5339, 65.2474 ], [ -172.537, 65.2484 ], [ -172.538, 65.2516 ], [ -172.5562, 65.2562 ], [ -172.5646, 65.2562 ], [ -172.5688, 65.2583 ], [ -172.5896, 65.2583 ], [ -172.6, 65.2646 ], [ -172.6271, 65.2646 ], [ -172.6453, 65.2599 ], [ -172.65, 65.2562 ], [ -172.6604, 65.2562 ], [ -172.6745, 65.2516 ], [ -172.6797, 65.2464 ], [ -172.6891, 65.2453 ], [ -172.6917, 65.2417 ], [ -172.6896, 65.2375 ], [ -172.6911, 65.2338 ], [ -172.688, 65.2318 ], [ -172.6932, 65.2297 ], [ -172.6839, 65.2234 ], [ -172.6953, 65.2193 ], [ -172.688, 65.2182 ], [ -172.687, 65.2151 ], [ -172.6833, 65.2125 ], [ -172.6625, 65.2125 ], [ -172.6557, 65.2182 ], [ -172.625, 65.225 ], [ -172.6167, 65.2208 ], [ -172.6109, 65.2203 ], [ -172.612, 65.2172 ], [ -172.6047, 65.2141 ], [ -172.6016, 65.2109 ], [ -172.5958, 65.2083 ], [ -172.5901, 65.2078 ], [ -172.5833, 65.2042 ], [ -172.5729, 65.2042 ], [ -172.5688, 65.2021 ], [ -172.5542, 65.2042 ], [ -172.5417, 65.2 ], [ -172.5354, 65.2 ], [ -172.5313, 65.2021 ], [ -172.525, 65.2 ], [ -172.5109, 65.2047 ], [ -172.5083, 65.2083 ], [ -172.4896, 65.2083 ], [ -172.475, 65.2125 ], [ -172.4542, 65.2125 ], [ -172.45, 65.2104 ], [ -172.4312, 65.2104 ], [ -172.4271, 65.2083 ], [ -172.4208, 65.2104 ], [ -172.4062, 65.2104 ], [ -172.4021, 65.2083 ], [ -172.3917, 65.2083 ], [ -172.3875, 65.2063 ], [ -172.3729, 65.2063 ], [ -172.3687, 65.2042 ], [ -172.3646, 65.2042 ], [ -172.3604, 65.2063 ], [ -172.3333, 65.2063 ], [ -172.3292, 65.2083 ], [ -172.3208, 65.2083 ], [ -172.3062, 65.2125 ], [ -172.2688, 65.2125 ], [ -172.2646, 65.2146 ], [ -172.2542, 65.2146 ], [ -172.2526, 65.2141 ], [ -172.2536, 65.2109 ], [ -172.2521, 65.2104 ], [ -172.2417, 65.2125 ], [ -172.2359, 65.2099 ], [ -172.2328, 65.2047 ], [ -172.2229, 65.2021 ], [ -172.2125, 65.2021 ], [ -172.1984, 65.1974 ], [ -172.1995, 65.1943 ], [ -172.1964, 65.1932 ], [ -172.1974, 65.1901 ], [ -172.1943, 65.1891 ], [ -172.1953, 65.1839 ], [ -172.1901, 65.1807 ], [ -172.1911, 65.1755 ], [ -172.1839, 65.1745 ], [ -172.1849, 65.1693 ], [ -172.1755, 65.1641 ], [ -172.1766, 65.1609 ], [ -172.1693, 65.1578 ], [ -172.1703, 65.1547 ], [ -172.1526, 65.1453 ], [ -172.1536, 65.1422 ], [ -172.1505, 65.1401 ], [ -172.1542, 65.1375 ], [ -172.1583, 65.1396 ], [ -172.1646, 65.1396 ], [ -172.1687, 65.1375 ], [ -172.1771, 65.1417 ], [ -172.1891, 65.1412 ], [ -172.1943, 65.1339 ], [ -172.2057, 65.1307 ], [ -172.2099, 65.1266 ], [ -172.2088, 65.1214 ], [ -172.2141, 65.1203 ], [ -172.2125, 65.1188 ], [ -172.2047, 65.1182 ], [ -172.2057, 65.1151 ], [ -172.2, 65.1146 ], [ -172.1943, 65.112 ], [ -172.1953, 65.1088 ], [ -172.188, 65.1078 ], [ -172.1797, 65.1036 ], [ -172.1807, 65.1005 ], [ -172.1776, 65.0995 ], [ -172.175, 65.0958 ], [ -172.1787, 65.0901 ], [ -172.1672, 65.0849 ], [ -172.1651, 65.0818 ], [ -172.1703, 65.0797 ], [ -172.163, 65.0787 ], [ -172.1625, 65.0771 ], [ -172.1651, 65.0734 ], [ -172.1703, 65.0714 ], [ -172.1646, 65.0708 ], [ -172.1562, 65.0667 ], [ -172.1417, 65.0667 ], [ -172.125, 65.0729 ], [ -172.1187, 65.0729 ], [ -172.1125, 65.0708 ], [ -172.1104, 65.0729 ], [ -172.1141, 65.0755 ], [ -172.112, 65.0787 ], [ -172.1005, 65.0797 ], [ -172.0974, 65.0849 ], [ -172.0896, 65.0875 ], [ -172.0838, 65.0849 ], [ -172.0891, 65.0828 ], [ -172.0859, 65.0797 ], [ -172.0901, 65.0734 ], [ -172.0953, 65.0724 ], [ -172.0974, 65.0703 ], [ -172.0943, 65.0672 ], [ -172.0995, 65.0661 ], [ -172.0984, 65.063 ], [ -172.1037, 65.062 ], [ -172.1068, 65.0568 ], [ -172.1089, 65.0547 ], [ -172.1141, 65.0537 ], [ -172.1172, 65.0484 ], [ -172.125, 65.0458 ], [ -172.1328, 65.0453 ], [ -172.1359, 65.0422 ], [ -172.1474, 65.0391 ], [ -172.1505, 65.0359 ], [ -172.162, 65.0328 ], [ -172.1651, 65.0276 ], [ -172.1854, 65.0208 ], [ -172.1917, 65.0208 ], [ -172.1964, 65.0172 ], [ -172.2078, 65.0141 ], [ -172.2099, 65.012 ], [ -172.2068, 65.0099 ], [ -172.2088, 65.0047 ], [ -172.2109, 65.0026 ], [ -172.2161, 65.0016 ], [ -172.2193, 64.9984 ], [ -172.2245, 64.9974 ], [ -172.2276, 64.9922 ], [ -172.2297, 64.9901 ], [ -172.2349, 64.9891 ], [ -172.2401, 64.9818 ], [ -172.2453, 64.9807 ], [ -172.2505, 64.9734 ], [ -172.2557, 64.9724 ], [ -172.2526, 64.9693 ], [ -172.2641, 64.9661 ], [ -172.2672, 64.963 ], [ -172.275, 64.9604 ], [ -172.2828, 64.9599 ], [ -172.2875, 64.9563 ], [ -172.2979, 64.9563 ], [ -172.3083, 64.9521 ], [ -172.3208, 64.9521 ], [ -172.3349, 64.9474 ], [ -172.338, 64.9443 ], [ -172.362, 64.937 ], [ -172.3651, 64.9339 ], [ -172.3703, 64.9328 ], [ -172.3714, 64.9297 ], [ -172.375, 64.9271 ], [ -172.3813, 64.9271 ], [ -172.3917, 64.9229 ], [ -172.4, 64.9229 ], [ -172.4104, 64.9187 ], [ -172.4167, 64.9187 ], [ -172.4208, 64.9167 ], [ -172.4271, 64.9167 ], [ -172.4333, 64.9146 ], [ -172.4375, 64.9167 ], [ -172.4563, 64.9167 ], [ -172.4745, 64.9213 ], [ -172.4734, 64.9245 ], [ -172.475, 64.925 ], [ -172.4792, 64.925 ], [ -172.4833, 64.9271 ], [ -172.4937, 64.9271 ], [ -172.512, 64.9318 ], [ -172.5188, 64.9354 ], [ -172.5333, 64.9354 ], [ -172.5375, 64.9333 ], [ -172.5437, 64.9354 ], [ -172.5479, 64.9333 ], [ -172.5542, 64.9333 ], [ -172.5557, 64.9339 ], [ -172.5547, 64.937 ], [ -172.5646, 64.9396 ], [ -172.575, 64.9354 ], [ -172.5875, 64.9354 ], [ -172.5891, 64.9339 ], [ -172.5792, 64.9313 ], [ -172.5458, 64.9313 ], [ -172.5417, 64.9292 ], [ -172.5313, 64.9292 ], [ -172.5271, 64.9271 ], [ -172.5229, 64.9292 ], [ -172.5188, 64.9271 ], [ -172.5146, 64.9271 ], [ -172.5125, 64.925 ], [ -172.5151, 64.9213 ], [ -172.5245, 64.9224 ], [ -172.5213, 64.9203 ], [ -172.5224, 64.9172 ], [ -172.5167, 64.9146 ], [ -172.5063, 64.9146 ], [ -172.4964, 64.9109 ], [ -172.5042, 64.9083 ], [ -172.5104, 64.9104 ], [ -172.525, 64.9104 ], [ -172.5292, 64.9083 ], [ -172.5354, 64.9104 ], [ -172.5495, 64.9057 ], [ -172.5505, 64.9026 ], [ -172.562, 64.8995 ], [ -172.5651, 64.8964 ], [ -172.5729, 64.8958 ], [ -172.5776, 64.8922 ], [ -172.5979, 64.8854 ], [ -172.6146, 64.8854 ], [ -172.6187, 64.8833 ], [ -172.6271, 64.8833 ], [ -172.6542, 64.875 ], [ -172.6604, 64.875 ], [ -172.6682, 64.8724 ], [ -172.6714, 64.8693 ], [ -172.6766, 64.8682 ], [ -172.6818, 64.8588 ], [ -172.687, 64.8578 ], [ -172.6901, 64.8547 ], [ -172.6979, 64.8521 ], [ -172.7057, 64.8516 ], [ -172.7088, 64.8484 ], [ -172.7141, 64.8474 ], [ -172.7187, 64.8438 ], [ -172.7417, 64.8417 ], [ -172.7443, 64.838 ], [ -172.7495, 64.837 ], [ -172.7542, 64.8333 ], [ -172.7682, 64.838 ], [ -172.7672, 64.8411 ], [ -172.7688, 64.8417 ], [ -172.775, 64.8396 ], [ -172.7854, 64.8396 ], [ -172.7922, 64.8432 ], [ -172.8021, 64.8458 ], [ -172.8208, 64.8458 ], [ -172.825, 64.8479 ], [ -172.8479, 64.8479 ], [ -172.9042, 64.8625 ], [ -172.9187, 64.8625 ], [ -172.9229, 64.8604 ], [ -172.9417, 64.8625 ], [ -172.9458, 64.8646 ], [ -172.9687, 64.8625 ], [ -172.9729, 64.8583 ], [ -172.9792, 64.8604 ], [ -172.9995, 64.8536 ], [ -172.9964, 64.8505 ], [ -173.0, 64.8479 ], [ -173.0208, 64.8479 ], [ -173.0255, 64.8443 ], [ -173.0333, 64.8438 ], [ -173.0412, 64.8411 ], [ -173.0443, 64.8359 ], [ -173.0479, 64.8333 ], [ -173.075, 64.8333 ], [ -173.0828, 64.8307 ], [ -173.0891, 64.8203 ], [ -173.0859, 64.8172 ], [ -173.0911, 64.8151 ], [ -173.0771, 64.8104 ], [ -173.0729, 64.8125 ], [ -173.063, 64.8099 ], [ -173.062, 64.8068 ], [ -173.0589, 64.8057 ], [ -173.0562, 64.8021 ], [ -173.0609, 64.7963 ], [ -173.0849, 64.7891 ], [ -173.0875, 64.7854 ], [ -173.1062, 64.7833 ], [ -173.1208, 64.7792 ], [ -173.125, 64.7813 ], [ -173.1375, 64.7813 ], [ -173.1521, 64.775 ], [ -173.1833, 64.775 ], [ -173.187, 64.7724 ], [ -173.1839, 64.7703 ], [ -173.187, 64.7651 ], [ -173.1839, 64.7641 ], [ -173.1849, 64.7589 ], [ -173.1818, 64.7578 ], [ -173.1771, 64.7521 ], [ -173.1667, 64.7521 ], [ -173.1438, 64.7604 ], [ -173.1292, 64.7604 ], [ -173.1208, 64.7562 ], [ -173.1062, 64.7562 ], [ -173.0984, 64.7589 ], [ -173.0937, 64.7625 ], [ -173.0755, 64.7651 ], [ -173.0708, 64.7688 ], [ -173.0583, 64.7688 ], [ -173.0542, 64.7708 ], [ -173.0464, 64.7714 ], [ -173.0432, 64.7766 ], [ -173.0297, 64.7818 ], [ -173.0266, 64.787 ], [ -173.013, 64.7922 ], [ -173.0161, 64.7953 ], [ -173.0109, 64.7963 ], [ -173.0099, 64.7995 ], [ -172.9833, 64.8083 ], [ -172.975, 64.8083 ], [ -172.9547, 64.8151 ], [ -172.9516, 64.8203 ], [ -172.9463, 64.8214 ], [ -172.9443, 64.8234 ], [ -172.9479, 64.8271 ], [ -172.9453, 64.8307 ], [ -172.9375, 64.8313 ], [ -172.9333, 64.8333 ], [ -172.9005, 64.8328 ], [ -172.9057, 64.8307 ], [ -172.9026, 64.8287 ], [ -172.9047, 64.8255 ], [ -172.9162, 64.8224 ], [ -172.9203, 64.8151 ], [ -172.9062, 64.8167 ], [ -172.9021, 64.8208 ], [ -172.8958, 64.8208 ], [ -172.8917, 64.8187 ], [ -172.8875, 64.8208 ], [ -172.8813, 64.8208 ], [ -172.8771, 64.8187 ], [ -172.8667, 64.8187 ], [ -172.8651, 64.8172 ], [ -172.8682, 64.813 ], [ -172.8609, 64.8099 ], [ -172.8578, 64.8068 ], [ -172.8458, 64.8063 ], [ -172.8375, 64.8021 ], [ -172.8151, 64.7995 ], [ -172.8109, 64.7963 ], [ -172.8292, 64.7958 ], [ -172.8307, 64.7943 ], [ -172.8172, 64.7932 ], [ -172.8141, 64.7901 ], [ -172.8068, 64.7891 ], [ -172.8036, 64.7839 ], [ -172.7896, 64.7813 ], [ -172.7812, 64.7771 ], [ -172.7708, 64.7771 ], [ -172.7667, 64.775 ], [ -172.7568, 64.7745 ], [ -172.7464, 64.7682 ], [ -172.7479, 64.7646 ], [ -172.7443, 64.7609 ], [ -172.7484, 64.7568 ], [ -172.7536, 64.7557 ], [ -172.7557, 64.7505 ], [ -172.7526, 64.7484 ], [ -172.7547, 64.7464 ], [ -172.7599, 64.7453 ], [ -172.763, 64.7422 ], [ -172.7708, 64.7396 ], [ -172.7917, 64.7396 ], [ -172.7953, 64.737 ], [ -172.7937, 64.7354 ], [ -172.7875, 64.7354 ], [ -172.7833, 64.7375 ], [ -172.7755, 64.737 ], [ -172.7766, 64.7338 ], [ -172.7734, 64.7328 ], [ -172.7755, 64.7276 ], [ -172.7937, 64.7271 ], [ -172.7979, 64.7292 ], [ -172.8021, 64.7271 ], [ -172.825, 64.7333 ], [ -172.8297, 64.7297 ], [ -172.8391, 64.7286 ], [ -172.8432, 64.7224 ], [ -172.8401, 64.7193 ], [ -172.8453, 64.7172 ], [ -172.838, 64.7161 ], [ -172.8391, 64.713 ], [ -172.8359, 64.712 ], [ -172.837, 64.7089 ], [ -172.8338, 64.7078 ], [ -172.8313, 64.7021 ], [ -172.838, 64.6922 ], [ -172.8625, 64.6854 ], [ -172.8687, 64.6854 ], [ -172.8813, 64.6896 ], [ -172.9021, 64.6896 ], [ -172.9062, 64.6875 ], [ -172.9271, 64.6875 ], [ -172.9312, 64.6854 ], [ -172.9479, 64.6854 ], [ -172.9625, 64.6813 ], [ -172.9792, 64.6813 ], [ -172.987, 64.6787 ], [ -172.9917, 64.675 ], [ -173.012, 64.6724 ], [ -173.0208, 64.6667 ], [ -173.0412, 64.6641 ], [ -173.0422, 64.6609 ], [ -173.0521, 64.6604 ], [ -173.0661, 64.6557 ], [ -173.0693, 64.6505 ], [ -173.0807, 64.6474 ], [ -173.0708, 64.6417 ], [ -173.0667, 64.6438 ], [ -173.0599, 64.6401 ], [ -173.05, 64.6375 ], [ -173.0458, 64.6396 ], [ -173.0354, 64.6396 ], [ -173.0312, 64.6375 ], [ -173.025, 64.6375 ], [ -173.0234, 64.6391 ], [ -173.0266, 64.6412 ], [ -173.0213, 64.6422 ], [ -173.0161, 64.6474 ], [ -173.0083, 64.6479 ], [ -173.0036, 64.6516 ], [ -172.9958, 64.6521 ], [ -172.9854, 64.6563 ], [ -172.9812, 64.6542 ], [ -172.9797, 64.6547 ], [ -172.9807, 64.6578 ], [ -172.9729, 64.6583 ], [ -172.9687, 64.6604 ], [ -172.9578, 64.6547 ], [ -172.9505, 64.6536 ], [ -172.9516, 64.6505 ], [ -172.9438, 64.65 ], [ -172.9359, 64.6547 ], [ -172.9307, 64.662 ], [ -172.9255, 64.663 ], [ -172.9203, 64.6682 ], [ -172.9125, 64.6708 ], [ -172.8984, 64.6682 ], [ -172.8943, 64.6661 ], [ -172.8953, 64.663 ], [ -172.888, 64.662 ], [ -172.8813, 64.6583 ], [ -172.8583, 64.6563 ], [ -172.8542, 64.6542 ], [ -172.8338, 64.6536 ], [ -172.838, 64.6464 ], [ -172.8458, 64.6458 ], [ -172.8537, 64.6432 ], [ -172.8589, 64.638 ], [ -172.8641, 64.637 ], [ -172.8672, 64.6318 ], [ -172.8724, 64.6307 ], [ -172.8776, 64.6214 ], [ -172.8818, 64.6172 ], [ -172.8938, 64.6167 ], [ -172.9016, 64.6141 ], [ -172.9068, 64.6088 ], [ -172.9099, 64.6078 ], [ -172.9089, 64.6026 ], [ -172.9193, 64.588 ], [ -172.9271, 64.5875 ], [ -172.9536, 64.5787 ], [ -172.9583, 64.575 ], [ -172.9646, 64.575 ], [ -172.9724, 64.5724 ], [ -172.9745, 64.5703 ], [ -172.9745, 64.5672 ], [ -172.9563, 64.5625 ], [ -172.9438, 64.5625 ], [ -172.9396, 64.5604 ], [ -172.9333, 64.5604 ], [ -172.9229, 64.5646 ], [ -172.9104, 64.5646 ], [ -172.9062, 64.5667 ], [ -172.8854, 64.5667 ], [ -172.8818, 64.5693 ], [ -172.8849, 64.5724 ], [ -172.8776, 64.5766 ], [ -172.8849, 64.5776 ], [ -172.8875, 64.5813 ], [ -172.8911, 64.5787 ], [ -172.8922, 64.5755 ], [ -172.9042, 64.575 ], [ -172.9141, 64.5807 ], [ -172.9062, 64.5813 ], [ -172.8922, 64.5859 ], [ -172.8859, 64.5943 ], [ -172.8891, 64.5974 ], [ -172.8813, 64.6 ], [ -172.8646, 64.6 ], [ -172.8542, 64.6042 ], [ -172.8458, 64.6042 ], [ -172.8443, 64.6047 ], [ -172.8474, 64.6068 ], [ -172.8458, 64.6083 ], [ -172.8354, 64.6083 ], [ -172.8313, 64.6104 ], [ -172.8146, 64.6104 ], [ -172.8104, 64.6125 ], [ -172.8042, 64.6104 ], [ -172.7854, 64.6104 ], [ -172.7792, 64.6125 ], [ -172.775, 64.6104 ], [ -172.7667, 64.6104 ], [ -172.7599, 64.6068 ], [ -172.7417, 64.6021 ], [ -172.7375, 64.6021 ], [ -172.7333, 64.6042 ], [ -172.7229, 64.6042 ], [ -172.7172, 64.6016 ], [ -172.7172, 64.5984 ], [ -172.7203, 64.5943 ], [ -172.7172, 64.5932 ], [ -172.7182, 64.5901 ], [ -172.7151, 64.5891 ], [ -172.7161, 64.5859 ], [ -172.712, 64.5839 ], [ -172.7021, 64.5813 ], [ -172.6938, 64.5813 ], [ -172.6896, 64.5792 ], [ -172.6833, 64.5792 ], [ -172.6687, 64.5833 ], [ -172.6625, 64.5813 ], [ -172.6479, 64.5833 ], [ -172.6354, 64.5792 ], [ -172.625, 64.5792 ], [ -172.6208, 64.5813 ], [ -172.6141, 64.5776 ], [ -172.6083, 64.5771 ], [ -172.6026, 64.5745 ], [ -172.6057, 64.5693 ], [ -172.6026, 64.5682 ], [ -172.6037, 64.5651 ], [ -172.6005, 64.5641 ], [ -172.6016, 64.5609 ], [ -172.5943, 64.5599 ], [ -172.5911, 64.5547 ], [ -172.5792, 64.5542 ], [ -172.5729, 64.55 ], [ -172.5583, 64.55 ], [ -172.5542, 64.5479 ], [ -172.5208, 64.5479 ], [ -172.5167, 64.5458 ], [ -172.5083, 64.5458 ], [ -172.5042, 64.5437 ], [ -172.4937, 64.5437 ], [ -172.4922, 64.5432 ], [ -172.4932, 64.5401 ], [ -172.4833, 64.5375 ], [ -172.4792, 64.5396 ], [ -172.4687, 64.5396 ], [ -172.4646, 64.5375 ], [ -172.4479, 64.5375 ], [ -172.4438, 64.5417 ], [ -172.4375, 64.5396 ], [ -172.4172, 64.5391 ], [ -172.4146, 64.5354 ], [ -172.4162, 64.5318 ], [ -172.413, 64.5307 ], [ -172.4141, 64.5276 ], [ -172.4109, 64.5266 ], [ -172.413, 64.5234 ], [ -172.4182, 64.5224 ], [ -172.4203, 64.5203 ], [ -172.4187, 64.5188 ], [ -172.413, 64.5182 ], [ -172.413, 64.5151 ], [ -172.4162, 64.5109 ], [ -172.4104, 64.5104 ], [ -172.4047, 64.5078 ], [ -172.4057, 64.5047 ], [ -172.3984, 64.5016 ], [ -172.3995, 64.4984 ], [ -172.3963, 64.4974 ], [ -172.3974, 64.4943 ], [ -172.3943, 64.4932 ], [ -172.3974, 64.488 ], [ -172.3917, 64.4854 ], [ -172.3755, 64.4849 ], [ -172.3792, 64.4812 ], [ -172.3896, 64.4812 ], [ -172.3938, 64.4833 ], [ -172.4, 64.4833 ], [ -172.4016, 64.4828 ], [ -172.4036, 64.4776 ], [ -172.3979, 64.4771 ], [ -172.3911, 64.4734 ], [ -172.3813, 64.4708 ], [ -172.3625, 64.4708 ], [ -172.3578, 64.4672 ], [ -172.3505, 64.4661 ], [ -172.3464, 64.4641 ], [ -172.3474, 64.4609 ], [ -172.3417, 64.4604 ], [ -172.3359, 64.4578 ], [ -172.337, 64.4547 ], [ -172.3338, 64.4537 ], [ -172.3349, 64.4505 ], [ -172.3318, 64.4484 ], [ -172.3396, 64.4458 ], [ -172.3474, 64.4453 ], [ -172.3521, 64.4417 ], [ -172.3682, 64.4505 ], [ -172.3672, 64.4537 ], [ -172.3714, 64.4557 ], [ -172.3938, 64.4563 ], [ -172.3958, 64.4542 ], [ -172.3943, 64.4505 ], [ -172.4021, 64.45 ], [ -172.4099, 64.4474 ], [ -172.413, 64.4443 ], [ -172.4182, 64.4432 ], [ -172.4151, 64.4411 ], [ -172.4182, 64.4339 ], [ -172.413, 64.4307 ], [ -172.4104, 64.425 ], [ -172.4172, 64.4151 ], [ -172.4224, 64.4141 ], [ -172.4245, 64.4099 ], [ -172.4234, 64.4068 ], [ -172.4276, 64.4047 ], [ -172.4375, 64.4042 ], [ -172.4417, 64.4021 ], [ -172.4521, 64.4021 ], [ -172.4536, 64.4016 ], [ -172.4526, 64.3984 ], [ -172.462, 64.3974 ], [ -172.4651, 64.3943 ], [ -172.4703, 64.3932 ], [ -172.4714, 64.3901 ], [ -172.4812, 64.3896 ], [ -172.4917, 64.3854 ], [ -172.4958, 64.3875 ], [ -172.5, 64.3854 ], [ -172.5188, 64.3833 ], [ -172.5229, 64.3812 ], [ -172.5271, 64.3833 ], [ -172.5313, 64.3812 ], [ -172.5542, 64.3812 ], [ -172.5583, 64.3792 ], [ -172.5688, 64.3792 ], [ -172.5854, 64.3729 ], [ -172.5958, 64.3729 ], [ -172.6, 64.375 ], [ -172.6083, 64.375 ], [ -172.6151, 64.3786 ], [ -172.625, 64.3812 ], [ -172.6333, 64.3812 ], [ -172.6375, 64.3833 ], [ -172.6479, 64.3833 ], [ -172.6521, 64.3854 ], [ -172.6562, 64.3854 ], [ -172.6625, 64.3917 ], [ -172.675, 64.3917 ], [ -172.6792, 64.3938 ], [ -172.6875, 64.3938 ], [ -172.6917, 64.3958 ], [ -172.7063, 64.3958 ], [ -172.7104, 64.3979 ], [ -172.7266, 64.3984 ], [ -172.7229, 64.4042 ], [ -172.7245, 64.4078 ], [ -172.7193, 64.4089 ], [ -172.7109, 64.4213 ], [ -172.7141, 64.4245 ], [ -172.7088, 64.4349 ], [ -172.7182, 64.4401 ], [ -172.7172, 64.4432 ], [ -172.7245, 64.4464 ], [ -172.738, 64.4557 ], [ -172.7516, 64.4568 ], [ -172.7505, 64.4599 ], [ -172.7536, 64.4609 ], [ -172.7526, 64.4641 ], [ -172.7557, 64.4651 ], [ -172.7526, 64.4693 ], [ -172.7557, 64.4714 ], [ -172.7526, 64.4786 ], [ -172.7557, 64.4807 ], [ -172.7505, 64.4828 ], [ -172.7557, 64.4859 ], [ -172.7547, 64.4891 ], [ -172.7578, 64.4901 ], [ -172.7568, 64.4932 ], [ -172.7641, 64.4943 ], [ -172.7563, 64.5021 ], [ -172.7588, 64.5078 ], [ -172.7604, 64.5083 ], [ -172.7646, 64.5083 ], [ -172.7688, 64.5104 ], [ -172.7729, 64.5083 ], [ -172.7854, 64.5083 ], [ -172.7901, 64.5026 ], [ -172.7953, 64.5016 ], [ -172.7922, 64.4995 ], [ -172.7937, 64.4979 ], [ -172.8146, 64.4979 ], [ -172.8188, 64.5 ], [ -172.8375, 64.5 ], [ -172.8417, 64.4979 ], [ -172.85, 64.4979 ], [ -172.8542, 64.4958 ], [ -172.8729, 64.4958 ], [ -172.8771, 64.4979 ], [ -172.8833, 64.4979 ], [ -172.8875, 64.4958 ], [ -172.8938, 64.4979 ], [ -172.8979, 64.4958 ], [ -172.9125, 64.4958 ], [ -172.9229, 64.4917 ], [ -172.9292, 64.4917 ], [ -172.9333, 64.4938 ], [ -172.9375, 64.4917 ], [ -172.9604, 64.4917 ], [ -172.9651, 64.4859 ], [ -172.9729, 64.4854 ], [ -172.9766, 64.4828 ], [ -172.9755, 64.4755 ], [ -172.9833, 64.4729 ], [ -172.9917, 64.4729 ], [ -173.0021, 64.4688 ], [ -173.0083, 64.4688 ], [ -173.0161, 64.4661 ], [ -173.0255, 64.4589 ], [ -173.0307, 64.4578 ], [ -173.0333, 64.4542 ], [ -173.0318, 64.4505 ], [ -173.037, 64.4484 ], [ -173.0312, 64.4458 ], [ -173.0109, 64.4453 ], [ -173.0188, 64.4417 ], [ -173.0266, 64.4411 ], [ -173.0286, 64.438 ], [ -173.0042, 64.4333 ], [ -172.9964, 64.4359 ], [ -172.9932, 64.4411 ], [ -172.9854, 64.4437 ], [ -172.9812, 64.4417 ], [ -172.9708, 64.4417 ], [ -172.9667, 64.4437 ], [ -172.95, 64.4437 ], [ -172.9458, 64.4458 ], [ -172.9417, 64.4437 ], [ -172.9354, 64.4437 ], [ -172.9307, 64.4474 ], [ -172.9255, 64.4484 ], [ -172.9245, 64.4516 ], [ -172.9193, 64.4526 ], [ -172.9203, 64.4557 ], [ -172.9125, 64.4563 ], [ -172.9083, 64.4583 ], [ -172.9005, 64.4578 ], [ -172.9016, 64.4547 ], [ -172.8984, 64.4526 ], [ -172.9036, 64.4516 ], [ -172.9021, 64.45 ], [ -172.8979, 64.45 ], [ -172.8938, 64.4521 ], [ -172.8771, 64.4521 ], [ -172.8734, 64.4547 ], [ -172.8724, 64.4578 ], [ -172.8646, 64.4583 ], [ -172.8562, 64.4542 ], [ -172.8437, 64.4542 ], [ -172.8313, 64.45 ], [ -172.825, 64.4521 ], [ -172.8193, 64.4516 ], [ -172.8203, 64.4464 ], [ -172.813, 64.4453 ], [ -172.8146, 64.4417 ], [ -172.812, 64.4359 ], [ -172.8047, 64.4349 ], [ -172.8078, 64.4297 ], [ -172.8047, 64.4287 ], [ -172.8057, 64.4255 ], [ -172.8026, 64.4245 ], [ -172.8005, 64.4193 ], [ -172.8057, 64.4182 ], [ -172.8089, 64.413 ], [ -172.8203, 64.4099 ], [ -172.8151, 64.4047 ], [ -172.8266, 64.4016 ], [ -172.8193, 64.3964 ], [ -172.825, 64.3875 ], [ -172.8229, 64.3833 ], [ -172.8271, 64.3771 ], [ -172.8255, 64.3734 ], [ -172.8307, 64.3714 ], [ -172.8276, 64.3703 ], [ -172.8286, 64.3672 ], [ -172.8213, 64.3662 ], [ -172.813, 64.362 ], [ -172.8104, 64.3583 ], [ -172.8151, 64.3526 ], [ -172.8203, 64.3516 ], [ -172.8193, 64.3443 ], [ -172.8266, 64.3349 ], [ -172.8297, 64.3276 ], [ -172.8318, 64.3255 ], [ -172.837, 64.3245 ], [ -172.8313, 64.3187 ], [ -172.8354, 64.3125 ], [ -172.8338, 64.3089 ], [ -172.8391, 64.3078 ], [ -172.8359, 64.3057 ], [ -172.838, 64.3005 ], [ -172.8521, 64.2958 ], [ -172.8583, 64.2979 ], [ -172.8729, 64.2937 ], [ -172.9, 64.2917 ], [ -172.9016, 64.2932 ], [ -172.8901, 64.2963 ], [ -172.8859, 64.3005 ], [ -172.8859, 64.3037 ], [ -172.9068, 64.3161 ], [ -172.9141, 64.3172 ], [ -172.9203, 64.3214 ], [ -172.9193, 64.3245 ], [ -172.9266, 64.3255 ], [ -172.9276, 64.3287 ], [ -172.9349, 64.3297 ], [ -172.9297, 64.3318 ], [ -172.937, 64.3349 ], [ -172.9255, 64.3505 ], [ -172.9266, 64.3578 ], [ -172.9214, 64.3588 ], [ -172.9151, 64.3672 ], [ -172.9182, 64.3693 ], [ -172.9172, 64.3724 ], [ -172.9203, 64.3734 ], [ -172.9193, 64.3766 ], [ -172.9266, 64.3797 ], [ -172.9292, 64.3833 ], [ -172.9245, 64.3912 ], [ -172.9193, 64.3922 ], [ -172.9125, 64.4021 ], [ -172.9151, 64.4057 ], [ -172.9229, 64.4063 ], [ -172.9266, 64.4036 ], [ -172.9297, 64.3964 ], [ -172.9432, 64.3912 ], [ -172.9505, 64.3818 ], [ -172.9557, 64.3807 ], [ -172.9526, 64.3786 ], [ -172.9557, 64.3714 ], [ -172.9484, 64.3672 ], [ -172.9536, 64.3599 ], [ -172.9505, 64.3568 ], [ -172.9547, 64.3526 ], [ -172.9599, 64.3516 ], [ -172.9568, 64.3495 ], [ -172.9583, 64.3458 ], [ -172.9563, 64.3417 ], [ -172.9661, 64.3266 ], [ -172.963, 64.3234 ], [ -172.9687, 64.3146 ], [ -172.9641, 64.3047 ], [ -172.9588, 64.3016 ], [ -172.9599, 64.2984 ], [ -172.9526, 64.2974 ], [ -172.9474, 64.2859 ], [ -172.9401, 64.2849 ], [ -172.9411, 64.2818 ], [ -172.9339, 64.2807 ], [ -172.9349, 64.2776 ], [ -172.9276, 64.2745 ], [ -172.9328, 64.2724 ], [ -172.937, 64.2682 ], [ -172.9339, 64.2662 ], [ -172.9359, 64.2609 ], [ -172.9729, 64.2604 ], [ -172.9771, 64.2583 ], [ -173.0167, 64.2583 ], [ -173.0208, 64.2604 ], [ -173.0312, 64.2604 ], [ -173.0521, 64.2542 ], [ -173.0646, 64.2542 ], [ -173.0672, 64.2505 ], [ -173.0724, 64.2495 ], [ -173.0755, 64.2464 ], [ -173.0937, 64.2438 ], [ -173.0979, 64.2417 ], [ -173.1146, 64.2417 ], [ -173.1187, 64.2438 ], [ -173.1271, 64.2438 ], [ -173.1313, 64.2458 ], [ -173.15, 64.2458 ], [ -173.1542, 64.2479 ], [ -173.1625, 64.2479 ], [ -173.1667, 64.25 ], [ -173.1708, 64.2479 ], [ -173.1771, 64.2479 ], [ -173.1786, 64.2484 ], [ -173.1776, 64.2516 ], [ -173.1849, 64.2526 ], [ -173.1859, 64.2557 ], [ -173.1922, 64.2599 ], [ -173.2245, 64.2672 ], [ -173.2229, 64.2688 ], [ -173.2151, 64.2693 ], [ -173.2172, 64.2724 ], [ -173.2229, 64.275 ], [ -173.2479, 64.2729 ], [ -173.2912, 64.2839 ], [ -173.287, 64.2911 ], [ -173.2755, 64.2922 ], [ -173.2786, 64.2943 ], [ -173.275, 64.3021 ], [ -173.2766, 64.3078 ], [ -173.2724, 64.312 ], [ -173.2609, 64.3151 ], [ -173.262, 64.3203 ], [ -173.2547, 64.3193 ], [ -173.2505, 64.3255 ], [ -173.2536, 64.3287 ], [ -173.2495, 64.3328 ], [ -173.2443, 64.3339 ], [ -173.2458, 64.3396 ], [ -173.2422, 64.3474 ], [ -173.2495, 64.3484 ], [ -173.2484, 64.3516 ], [ -173.2557, 64.3547 ], [ -173.2516, 64.362 ], [ -173.2464, 64.363 ], [ -173.2443, 64.3651 ], [ -173.25, 64.3688 ], [ -173.262, 64.3682 ], [ -173.2672, 64.3588 ], [ -173.2807, 64.3536 ], [ -173.2776, 64.3505 ], [ -173.2839, 64.3401 ], [ -173.2891, 64.3391 ], [ -173.2912, 64.337 ], [ -173.288, 64.3349 ], [ -173.2901, 64.3297 ], [ -173.2937, 64.3271 ], [ -173.2979, 64.3292 ], [ -173.3016, 64.3287 ], [ -173.3, 64.3229 ], [ -173.3047, 64.3172 ], [ -173.3141, 64.3161 ], [ -173.3193, 64.3089 ], [ -173.3245, 64.3078 ], [ -173.3276, 64.3026 ], [ -173.3328, 64.3016 ], [ -173.3328, 64.2984 ], [ -173.3271, 64.2979 ], [ -173.3255, 64.2963 ], [ -173.3354, 64.2958 ], [ -173.337, 64.2943 ], [ -173.3297, 64.2901 ], [ -173.3625, 64.2896 ], [ -173.3661, 64.2922 ], [ -173.3708, 64.3021 ], [ -173.3667, 64.3083 ], [ -173.3687, 64.3125 ], [ -173.3667, 64.3167 ], [ -173.3682, 64.3224 ], [ -173.3641, 64.3266 ], [ -173.3589, 64.3276 ], [ -173.3557, 64.3349 ], [ -173.3484, 64.3443 ], [ -173.3516, 64.3464 ], [ -173.3505, 64.3495 ], [ -173.3537, 64.3505 ], [ -173.3526, 64.3557 ], [ -173.3557, 64.3568 ], [ -173.3578, 64.3599 ], [ -173.35, 64.3625 ], [ -173.3458, 64.3604 ], [ -173.3375, 64.3604 ], [ -173.3307, 64.3568 ], [ -173.3276, 64.3568 ], [ -173.3297, 64.3599 ], [ -173.3328, 64.3609 ], [ -173.3307, 64.3662 ], [ -173.3193, 64.3672 ], [ -173.3167, 64.3708 ], [ -173.3125, 64.3688 ], [ -173.3089, 64.3693 ], [ -173.312, 64.3724 ], [ -173.3099, 64.3766 ], [ -173.3047, 64.3776 ], [ -173.3057, 64.3807 ], [ -173.288, 64.3818 ], [ -173.2849, 64.3849 ], [ -173.2797, 64.3859 ], [ -173.2724, 64.3974 ], [ -173.2646, 64.4 ], [ -173.2505, 64.4016 ], [ -173.2516, 64.3984 ], [ -173.2484, 64.3974 ], [ -173.2495, 64.3943 ], [ -173.2464, 64.3922 ], [ -173.2536, 64.388 ], [ -173.2437, 64.3854 ], [ -173.2401, 64.3859 ], [ -173.2432, 64.3891 ], [ -173.238, 64.3922 ], [ -173.2391, 64.3953 ], [ -173.2271, 64.3958 ], [ -173.2203, 64.4016 ], [ -173.2083, 64.4021 ], [ -173.2005, 64.4047 ], [ -173.1974, 64.4078 ], [ -173.1922, 64.4089 ], [ -173.1953, 64.412 ], [ -173.1891, 64.4203 ], [ -173.1729, 64.4208 ], [ -173.1714, 64.4213 ], [ -173.1724, 64.4245 ], [ -173.1589, 64.4255 ], [ -173.1604, 64.4271 ], [ -173.1662, 64.4276 ], [ -173.1687, 64.4313 ], [ -173.1662, 64.4349 ], [ -173.1583, 64.4354 ], [ -173.1547, 64.438 ], [ -173.1589, 64.4411 ], [ -173.1875, 64.4437 ], [ -173.1922, 64.4401 ], [ -173.2036, 64.437 ], [ -173.2088, 64.4297 ], [ -173.2141, 64.4287 ], [ -173.2172, 64.4234 ], [ -173.225, 64.4229 ], [ -173.2354, 64.4187 ], [ -173.2563, 64.4187 ], [ -173.2578, 64.4203 ], [ -173.2526, 64.4213 ], [ -173.2557, 64.4245 ], [ -173.2464, 64.4297 ], [ -173.2495, 64.4328 ], [ -173.2443, 64.4339 ], [ -173.2495, 64.438 ], [ -173.2484, 64.4432 ], [ -173.2542, 64.4458 ], [ -173.2786, 64.4484 ], [ -173.2724, 64.4578 ], [ -173.2667, 64.4563 ], [ -173.2604, 64.4563 ], [ -173.2588, 64.4578 ], [ -173.2667, 64.4625 ], [ -173.2745, 64.4609 ], [ -173.2734, 64.4641 ], [ -173.2766, 64.4661 ], [ -173.2713, 64.4672 ], [ -173.2713, 64.4703 ], [ -173.2828, 64.4714 ], [ -173.2776, 64.4745 ], [ -173.2854, 64.475 ], [ -173.2901, 64.4693 ], [ -173.2953, 64.4682 ], [ -173.2943, 64.4609 ], [ -173.3057, 64.4599 ], [ -173.3042, 64.4542 ], [ -173.3078, 64.4464 ], [ -173.3005, 64.4453 ], [ -173.3057, 64.4432 ], [ -173.3042, 64.4417 ], [ -173.2984, 64.4411 ], [ -173.3005, 64.438 ], [ -173.3057, 64.437 ], [ -173.3089, 64.4318 ], [ -173.3141, 64.4307 ], [ -173.3151, 64.4276 ], [ -173.3203, 64.4266 ], [ -173.3255, 64.4213 ], [ -173.3333, 64.4208 ], [ -173.3474, 64.4161 ], [ -173.3526, 64.4089 ], [ -173.3578, 64.4078 ], [ -173.3609, 64.4005 ], [ -173.3724, 64.3995 ], [ -173.3755, 64.3943 ], [ -173.3807, 64.3932 ], [ -173.3838, 64.388 ], [ -173.4078, 64.3807 ], [ -173.4125, 64.3771 ], [ -173.4203, 64.3766 ], [ -173.4172, 64.3734 ], [ -173.425, 64.3708 ], [ -173.4328, 64.3703 ], [ -173.4443, 64.363 ], [ -173.4521, 64.3625 ], [ -173.4599, 64.3599 ], [ -173.4625, 64.3562 ], [ -173.4687, 64.3562 ], [ -173.4729, 64.3583 ], [ -173.4833, 64.3542 ], [ -173.5021, 64.3521 ], [ -173.5229, 64.3458 ], [ -173.5271, 64.3479 ], [ -173.5307, 64.3453 ], [ -173.5292, 64.3438 ], [ -173.5151, 64.3432 ], [ -173.5141, 64.3401 ], [ -173.5109, 64.3391 ], [ -173.5125, 64.3375 ], [ -173.5208, 64.3375 ], [ -173.5312, 64.3333 ], [ -173.537, 64.337 ], [ -173.5297, 64.3411 ], [ -173.5354, 64.3438 ], [ -173.5432, 64.3411 ], [ -173.5479, 64.3354 ], [ -173.5688, 64.3354 ], [ -173.5729, 64.3375 ], [ -173.5771, 64.3375 ], [ -173.5813, 64.3354 ], [ -173.5937, 64.3354 ], [ -173.6167, 64.3271 ], [ -173.6229, 64.3271 ], [ -173.6271, 64.325 ], [ -173.6417, 64.325 ], [ -173.6562, 64.3333 ], [ -173.6604, 64.3333 ], [ -173.6812, 64.3396 ], [ -173.6917, 64.3396 ], [ -173.6995, 64.3443 ], [ -173.6984, 64.3474 ], [ -173.7026, 64.3495 ], [ -173.7292, 64.3562 ], [ -173.7437, 64.3562 ], [ -173.75, 64.3604 ], [ -173.7646, 64.3604 ], [ -173.7688, 64.3625 ], [ -173.7729, 64.3604 ], [ -173.7833, 64.3604 ], [ -173.7901, 64.3641 ], [ -173.7974, 64.3651 ], [ -173.812, 64.3734 ], [ -173.8104, 64.3771 ], [ -173.812, 64.3828 ], [ -173.8068, 64.3839 ], [ -173.8021, 64.3875 ], [ -173.7979, 64.3854 ], [ -173.7901, 64.3859 ], [ -173.7974, 64.3912 ], [ -173.7922, 64.3922 ], [ -173.7901, 64.3943 ], [ -173.7828, 64.4057 ], [ -173.7776, 64.4068 ], [ -173.7807, 64.4099 ], [ -173.7708, 64.4104 ], [ -173.7651, 64.413 ], [ -173.7661, 64.4161 ], [ -173.7609, 64.4172 ], [ -173.7641, 64.4203 ], [ -173.7599, 64.4266 ], [ -173.7505, 64.4276 ], [ -173.7536, 64.4297 ], [ -173.7516, 64.4328 ], [ -173.7401, 64.4359 ], [ -173.7349, 64.4432 ], [ -173.7271, 64.4458 ], [ -173.7193, 64.4464 ], [ -173.7172, 64.4516 ], [ -173.7203, 64.4526 ], [ -173.7234, 64.4578 ], [ -173.7292, 64.4625 ], [ -173.7271, 64.4646 ], [ -173.7172, 64.4651 ], [ -173.7187, 64.4667 ], [ -173.7292, 64.4667 ], [ -173.7328, 64.4641 ], [ -173.7339, 64.4609 ], [ -173.7375, 64.4604 ], [ -173.7417, 64.4625 ], [ -173.7557, 64.4578 ], [ -173.7599, 64.4516 ], [ -173.7568, 64.4484 ], [ -173.7646, 64.4458 ], [ -173.7724, 64.4453 ], [ -173.7713, 64.4422 ], [ -173.7771, 64.4437 ], [ -173.7849, 64.4432 ], [ -173.7943, 64.4255 ], [ -173.7984, 64.4213 ], [ -173.8203, 64.4161 ], [ -173.8213, 64.413 ], [ -173.8266, 64.412 ], [ -173.8297, 64.4068 ], [ -173.8432, 64.4016 ], [ -173.8526, 64.3943 ], [ -173.8578, 64.3932 ], [ -173.8599, 64.3912 ], [ -173.8568, 64.3891 ], [ -173.8583, 64.3875 ], [ -173.8729, 64.3875 ], [ -173.8771, 64.3896 ], [ -173.8917, 64.3896 ], [ -173.8958, 64.3917 ], [ -173.9042, 64.3917 ], [ -173.9167, 64.3979 ], [ -173.9266, 64.3984 ], [ -173.9255, 64.4016 ], [ -173.9354, 64.4042 ], [ -173.9396, 64.4042 ], [ -173.9438, 64.4063 ], [ -173.9542, 64.4063 ], [ -173.9583, 64.4083 ], [ -173.9708, 64.4083 ], [ -173.9818, 64.4141 ], [ -173.9891, 64.4151 ], [ -173.987, 64.4203 ], [ -173.9818, 64.4213 ], [ -173.9797, 64.4245 ], [ -173.9891, 64.4297 ], [ -173.9922, 64.4349 ], [ -173.9995, 64.4359 ], [ -174.0078, 64.4401 ], [ -174.0109, 64.4453 ], [ -174.0203, 64.4505 ], [ -174.0151, 64.4537 ], [ -174.0208, 64.4542 ], [ -174.0266, 64.4568 ], [ -174.0234, 64.4641 ], [ -174.0286, 64.4672 ], [ -174.0276, 64.4724 ], [ -174.0412, 64.4734 ], [ -174.0339, 64.4776 ], [ -174.0354, 64.4792 ], [ -174.0412, 64.4797 ], [ -174.0412, 64.4828 ], [ -174.0359, 64.4838 ], [ -174.0375, 64.4875 ], [ -174.0349, 64.4912 ], [ -174.0297, 64.4922 ], [ -174.0354, 64.4958 ], [ -174.0396, 64.4938 ], [ -174.0432, 64.4943 ], [ -174.037, 64.5057 ], [ -174.0318, 64.5068 ], [ -174.0333, 64.5104 ], [ -174.0312, 64.5146 ], [ -174.0328, 64.5182 ], [ -174.0276, 64.5193 ], [ -174.0391, 64.5255 ], [ -174.0339, 64.5286 ], [ -174.0412, 64.5297 ], [ -174.0432, 64.5318 ], [ -174.0432, 64.5349 ], [ -174.038, 64.5359 ], [ -174.0359, 64.538 ], [ -174.0359, 64.5411 ], [ -174.0453, 64.5391 ], [ -174.05, 64.5354 ], [ -174.0599, 64.538 ], [ -174.0729, 64.5458 ], [ -174.0792, 64.5437 ], [ -174.088, 64.5495 ], [ -174.0953, 64.5505 ], [ -174.1062, 64.5563 ], [ -174.112, 64.5568 ], [ -174.1193, 64.562 ], [ -174.1328, 64.563 ], [ -174.1359, 64.5661 ], [ -174.1432, 64.5672 ], [ -174.1542, 64.5729 ], [ -174.1599, 64.5734 ], [ -174.1672, 64.5787 ], [ -174.1792, 64.5792 ], [ -174.1875, 64.5833 ], [ -174.2016, 64.5859 ], [ -174.2088, 64.5911 ], [ -174.2146, 64.5917 ], [ -174.2245, 64.5964 ], [ -174.2234, 64.5995 ], [ -174.2307, 64.6005 ], [ -174.2297, 64.6057 ], [ -174.2437, 64.6083 ], [ -174.2521, 64.6125 ], [ -174.2604, 64.6125 ], [ -174.2661, 64.6151 ], [ -174.2609, 64.6172 ], [ -174.2641, 64.6203 ], [ -174.2604, 64.6229 ], [ -174.25, 64.6208 ], [ -174.2458, 64.6229 ], [ -174.2354, 64.6229 ], [ -174.225, 64.6271 ], [ -174.2187, 64.6271 ], [ -174.2109, 64.6297 ], [ -174.213, 64.6349 ], [ -174.2146, 64.6354 ], [ -174.2333, 64.6354 ], [ -174.2391, 64.638 ], [ -174.2318, 64.6422 ], [ -174.2349, 64.6453 ], [ -174.2297, 64.6464 ], [ -174.2276, 64.6495 ], [ -174.2437, 64.6521 ], [ -174.2474, 64.6495 ], [ -174.2484, 64.6464 ], [ -174.2625, 64.6458 ], [ -174.2729, 64.6417 ], [ -174.3042, 64.6417 ], [ -174.3083, 64.6375 ], [ -174.3266, 64.637 ], [ -174.3313, 64.6312 ], [ -174.3562, 64.6312 ], [ -174.3604, 64.6333 ], [ -174.3708, 64.6333 ], [ -174.388, 64.6432 ], [ -174.3938, 64.6438 ], [ -174.4021, 64.6479 ], [ -174.412, 64.6484 ], [ -174.4146, 64.6521 ], [ -174.4245, 64.6526 ], [ -174.4193, 64.6557 ], [ -174.425, 64.6563 ], [ -174.4333, 64.6604 ], [ -174.4417, 64.6604 ], [ -174.4542, 64.6646 ], [ -174.4646, 64.6646 ], [ -174.4979, 64.675 ], [ -174.5125, 64.675 ], [ -174.5141, 64.6755 ], [ -174.513, 64.6787 ], [ -174.5208, 64.6771 ], [ -174.5224, 64.6776 ], [ -174.5213, 64.6807 ], [ -174.5229, 64.6813 ], [ -174.5333, 64.6813 ], [ -174.5625, 64.6896 ], [ -174.5667, 64.6896 ], [ -174.5734, 64.6932 ], [ -174.5833, 64.6958 ], [ -174.5917, 64.6958 ], [ -174.6042, 64.7 ], [ -174.6313, 64.7042 ], [ -174.637, 64.7068 ], [ -174.6359, 64.7099 ], [ -174.6432, 64.7109 ], [ -174.6422, 64.7182 ], [ -174.6479, 64.7188 ], [ -174.6536, 64.7214 ], [ -174.6599, 64.7255 ], [ -174.6609, 64.7286 ], [ -174.6708, 64.7292 ], [ -174.6766, 64.7318 ], [ -174.6729, 64.7354 ], [ -174.6609, 64.7338 ], [ -174.6641, 64.737 ], [ -174.6589, 64.7474 ], [ -174.6662, 64.7516 ], [ -174.6641, 64.7536 ], [ -174.6526, 64.7568 ], [ -174.6526, 64.7599 ], [ -174.6583, 64.7646 ], [ -174.6557, 64.7682 ], [ -174.6505, 64.7703 ], [ -174.6578, 64.7745 ], [ -174.6463, 64.7776 ], [ -174.6495, 64.7807 ], [ -174.6443, 64.7818 ], [ -174.6422, 64.7839 ], [ -174.6401, 64.7891 ], [ -174.6432, 64.7901 ], [ -174.6422, 64.7932 ], [ -174.6453, 64.7953 ], [ -174.6359, 64.7963 ], [ -174.6328, 64.8016 ], [ -174.6068, 64.8109 ], [ -174.6161, 64.8141 ], [ -174.6109, 64.8151 ], [ -174.6068, 64.8193 ], [ -174.6068, 64.8224 ], [ -174.6099, 64.8234 ], [ -174.6078, 64.8287 ], [ -174.5964, 64.8318 ], [ -174.5995, 64.8349 ], [ -174.5943, 64.8359 ], [ -174.5922, 64.8391 ], [ -174.5995, 64.8422 ], [ -174.6026, 64.8474 ], [ -174.612, 64.8526 ], [ -174.612, 64.8557 ], [ -174.6047, 64.8609 ], [ -174.6068, 64.8662 ], [ -174.612, 64.8693 ], [ -174.6109, 64.8745 ], [ -174.6167, 64.8771 ], [ -174.6266, 64.8776 ], [ -174.6292, 64.8812 ], [ -174.6391, 64.8818 ], [ -174.637, 64.8891 ], [ -174.6318, 64.8901 ], [ -174.6297, 64.8932 ], [ -174.6604, 64.9021 ], [ -174.6682, 64.9026 ], [ -174.6672, 64.9057 ], [ -174.6729, 64.9083 ], [ -174.6786, 64.9089 ], [ -174.6854, 64.9125 ], [ -174.7063, 64.9125 ], [ -174.7083, 64.9104 ], [ -174.7016, 64.9047 ], [ -174.6849, 64.8964 ], [ -174.675, 64.8958 ], [ -174.6729, 64.8938 ], [ -174.6755, 64.8901 ], [ -174.6807, 64.8891 ], [ -174.688, 64.8776 ], [ -174.6932, 64.8766 ], [ -174.6932, 64.8734 ], [ -174.6901, 64.8714 ], [ -174.6953, 64.8703 ], [ -174.6995, 64.8641 ], [ -174.6963, 64.862 ], [ -174.6974, 64.8588 ], [ -174.6943, 64.8568 ], [ -174.6963, 64.8547 ], [ -174.7016, 64.8536 ], [ -174.6943, 64.8484 ], [ -174.6984, 64.8422 ], [ -174.7036, 64.8411 ], [ -174.7057, 64.8359 ], [ -174.7026, 64.8349 ], [ -174.7026, 64.8318 ], [ -174.7109, 64.8193 ], [ -174.7187, 64.8187 ], [ -174.7328, 64.8141 ], [ -174.7359, 64.8089 ], [ -174.7437, 64.8063 ], [ -174.75, 64.8063 ], [ -174.7547, 64.8026 ], [ -174.7625, 64.8021 ], [ -174.7667, 64.8 ], [ -174.7875, 64.8 ], [ -174.7979, 64.8063 ], [ -174.8125, 64.8063 ], [ -174.8167, 64.8083 ], [ -174.8229, 64.8083 ], [ -174.837, 64.8037 ], [ -174.8401, 64.7984 ], [ -174.8479, 64.7979 ], [ -174.8521, 64.7958 ], [ -174.8604, 64.8 ], [ -174.875, 64.8 ], [ -174.8792, 64.8021 ], [ -174.8896, 64.8021 ], [ -174.9021, 64.8063 ], [ -174.9078, 64.8057 ], [ -174.913, 64.8005 ], [ -174.9162, 64.7995 ], [ -174.9125, 64.7958 ], [ -174.8818, 64.7953 ], [ -174.8786, 64.788 ], [ -174.8687, 64.7854 ], [ -174.8526, 64.7849 ], [ -174.8547, 64.7797 ], [ -174.8562, 64.7792 ], [ -174.8625, 64.7792 ], [ -174.8667, 64.7813 ], [ -174.8854, 64.7813 ], [ -174.8896, 64.7833 ], [ -174.9, 64.7833 ], [ -174.9125, 64.7875 ], [ -174.9516, 64.7901 ], [ -174.95, 64.7937 ], [ -174.9521, 64.7979 ], [ -174.9484, 64.8047 ], [ -174.9516, 64.8078 ], [ -174.9438, 64.8104 ], [ -174.9292, 64.8083 ], [ -174.9276, 64.8089 ], [ -174.9286, 64.812 ], [ -174.9245, 64.8141 ], [ -174.9146, 64.8146 ], [ -174.9068, 64.8172 ], [ -174.9104, 64.8208 ], [ -174.9089, 64.8245 ], [ -174.9187, 64.8292 ], [ -174.9245, 64.8297 ], [ -174.9307, 64.8339 ], [ -174.9255, 64.838 ], [ -174.9286, 64.8401 ], [ -174.9271, 64.8438 ], [ -174.9292, 64.8479 ], [ -174.9271, 64.8521 ], [ -174.9307, 64.8547 ], [ -174.9255, 64.8578 ], [ -174.9307, 64.862 ], [ -174.9255, 64.8641 ], [ -174.9375, 64.8667 ], [ -174.9417, 64.8646 ], [ -174.9521, 64.8646 ], [ -174.9563, 64.8625 ], [ -174.975, 64.8604 ], [ -174.9787, 64.8578 ], [ -174.9807, 64.8526 ], [ -174.9776, 64.8505 ], [ -174.9828, 64.8495 ], [ -174.9797, 64.8464 ], [ -174.9849, 64.8443 ], [ -174.9734, 64.8391 ], [ -174.9745, 64.8359 ], [ -174.9714, 64.8339 ], [ -174.9734, 64.8318 ], [ -174.9787, 64.8307 ], [ -174.9807, 64.8255 ], [ -174.9776, 64.8234 ], [ -174.9812, 64.8167 ], [ -174.9776, 64.8141 ], [ -174.9787, 64.8109 ], [ -174.9755, 64.8099 ], [ -174.9766, 64.8047 ], [ -174.9714, 64.8016 ], [ -174.9724, 64.7943 ], [ -174.9661, 64.7901 ], [ -174.9604, 64.7896 ], [ -174.9588, 64.788 ], [ -174.9682, 64.7849 ], [ -174.9667, 64.7813 ], [ -174.9693, 64.7776 ], [ -174.9771, 64.775 ], [ -174.9833, 64.775 ], [ -174.9896, 64.7708 ], [ -174.9974, 64.7703 ], [ -175.0, 64.7667 ], [ -175.0063, 64.7688 ], [ -175.0208, 64.7688 ], [ -175.0312, 64.7646 ], [ -175.0375, 64.7667 ], [ -175.0417, 64.7646 ], [ -175.0521, 64.7646 ], [ -175.0562, 64.7625 ], [ -175.0625, 64.7625 ], [ -175.0641, 64.763 ], [ -175.063, 64.7662 ], [ -175.0646, 64.7667 ], [ -175.0854, 64.7667 ], [ -175.0896, 64.7688 ], [ -175.0979, 64.7688 ], [ -175.1021, 64.7708 ], [ -175.1167, 64.7708 ], [ -175.1208, 64.7729 ], [ -175.1396, 64.7729 ], [ -175.1438, 64.775 ], [ -175.1833, 64.775 ], [ -175.1911, 64.7724 ], [ -175.188, 64.7693 ], [ -175.2021, 64.7667 ], [ -175.2063, 64.7688 ], [ -175.2146, 64.7688 ], [ -175.2187, 64.7708 ], [ -175.2229, 64.7708 ], [ -175.2312, 64.775 ], [ -175.2417, 64.775 ], [ -175.2458, 64.7771 ], [ -175.2542, 64.7771 ], [ -175.2583, 64.7792 ], [ -175.2646, 64.7771 ], [ -175.2688, 64.7792 ], [ -175.2875, 64.7792 ], [ -175.2917, 64.7813 ], [ -175.3021, 64.7813 ], [ -175.3062, 64.7792 ], [ -175.3188, 64.7792 ], [ -175.3229, 64.7771 ], [ -175.337, 64.7818 ], [ -175.338, 64.7849 ], [ -175.3443, 64.7891 ], [ -175.35, 64.7896 ], [ -175.3583, 64.7937 ], [ -175.3687, 64.7937 ], [ -175.3729, 64.7958 ], [ -175.3786, 64.7953 ], [ -175.3833, 64.7917 ], [ -175.3896, 64.7917 ], [ -175.3974, 64.7891 ], [ -175.4005, 64.7839 ], [ -175.4089, 64.7776 ], [ -175.4245, 64.7755 ], [ -175.4276, 64.7807 ], [ -175.4307, 64.7818 ], [ -175.4271, 64.7896 ], [ -175.4297, 64.7953 ], [ -175.4396, 64.7958 ], [ -175.4422, 64.7995 ], [ -175.4479, 64.8 ], [ -175.4495, 64.8016 ], [ -175.4417, 64.8021 ], [ -175.4375, 64.8 ], [ -175.4271, 64.8042 ], [ -175.4146, 64.8042 ], [ -175.4026, 64.8089 ], [ -175.4036, 64.8141 ], [ -175.3833, 64.8208 ], [ -175.3755, 64.8203 ], [ -175.3766, 64.8172 ], [ -175.375, 64.8167 ], [ -175.363, 64.8172 ], [ -175.3599, 64.8203 ], [ -175.3505, 64.8234 ], [ -175.3516, 64.8287 ], [ -175.3422, 64.8276 ], [ -175.3453, 64.8307 ], [ -175.3411, 64.8391 ], [ -175.3359, 64.8401 ], [ -175.3338, 64.8432 ], [ -175.3453, 64.8484 ], [ -175.3443, 64.8516 ], [ -175.3458, 64.8521 ], [ -175.3521, 64.8521 ], [ -175.3562, 64.85 ], [ -175.3687, 64.85 ], [ -175.3729, 64.8479 ], [ -175.3771, 64.85 ], [ -175.4021, 64.85 ], [ -175.4099, 64.8474 ], [ -175.4068, 64.8443 ], [ -175.4089, 64.8422 ], [ -175.4141, 64.8411 ], [ -175.4167, 64.8375 ], [ -175.4563, 64.8375 ], [ -175.4661, 64.8422 ], [ -175.4651, 64.8453 ], [ -175.4766, 64.8505 ], [ -175.4714, 64.8526 ], [ -175.4745, 64.8557 ], [ -175.4708, 64.8625 ], [ -175.4766, 64.8682 ], [ -175.4687, 64.8708 ], [ -175.4609, 64.8714 ], [ -175.4599, 64.8745 ], [ -175.4547, 64.8766 ], [ -175.4703, 64.8797 ], [ -175.4828, 64.8859 ], [ -175.4859, 64.8891 ], [ -175.4896, 64.8896 ], [ -175.4974, 64.887 ], [ -175.4974, 64.8839 ], [ -175.4943, 64.8828 ], [ -175.4958, 64.8812 ], [ -175.5063, 64.8833 ], [ -175.5167, 64.8792 ], [ -175.5229, 64.8792 ], [ -175.5349, 64.8859 ], [ -175.5255, 64.8891 ], [ -175.5312, 64.8917 ], [ -175.537, 64.8922 ], [ -175.5412, 64.8943 ], [ -175.5422, 64.8974 ], [ -175.5604, 64.9 ], [ -175.563, 64.9036 ], [ -175.5729, 64.9042 ], [ -175.5838, 64.9099 ], [ -175.6021, 64.9146 ], [ -175.6125, 64.9125 ], [ -175.6208, 64.9167 ], [ -175.6313, 64.9167 ], [ -175.6354, 64.9187 ], [ -175.6396, 64.9187 ], [ -175.6453, 64.9213 ], [ -175.6443, 64.9245 ], [ -175.65, 64.9271 ], [ -175.6625, 64.9292 ], [ -175.6667, 64.9313 ], [ -175.6729, 64.9313 ], [ -175.6771, 64.9333 ], [ -175.6917, 64.9333 ], [ -175.7042, 64.9375 ], [ -175.7229, 64.9375 ], [ -175.7271, 64.9396 ], [ -175.7375, 64.9396 ], [ -175.7417, 64.9417 ], [ -175.7563, 64.9417 ], [ -175.7604, 64.9437 ], [ -175.7729, 64.9437 ], [ -175.7771, 64.9458 ], [ -175.7833, 64.9458 ], [ -175.787, 64.9495 ], [ -175.763, 64.9505 ], [ -175.763, 64.9537 ], [ -175.7745, 64.9547 ], [ -175.7693, 64.9568 ], [ -175.7651, 64.9661 ], [ -175.7776, 64.9724 ], [ -175.7849, 64.9734 ], [ -175.7943, 64.9786 ], [ -175.8, 64.9792 ], [ -175.8083, 64.9833 ], [ -175.8313, 64.9854 ], [ -175.8437, 64.9896 ], [ -175.8542, 64.9896 ], [ -175.8641, 64.9922 ], [ -175.863, 64.9974 ], [ -175.8729, 64.9979 ], [ -175.8786, 65.0005 ], [ -175.8734, 65.0026 ], [ -175.8708, 65.0062 ], [ -175.8667, 65.0042 ], [ -175.863, 65.0047 ], [ -175.862, 65.0078 ], [ -175.8568, 65.0089 ], [ -175.8599, 65.0109 ], [ -175.8583, 65.0125 ], [ -175.8521, 65.0104 ], [ -175.8458, 65.0104 ], [ -175.8375, 65.0146 ], [ -175.825, 65.0146 ], [ -175.8208, 65.0167 ], [ -175.8146, 65.0167 ], [ -175.8109, 65.0193 ], [ -175.8099, 65.0224 ], [ -175.7984, 65.0234 ], [ -175.8016, 65.0266 ], [ -175.7937, 65.0271 ], [ -175.7859, 65.0297 ], [ -175.7891, 65.0318 ], [ -175.788, 65.0349 ], [ -175.7912, 65.037 ], [ -175.7818, 65.0401 ], [ -175.7792, 65.0437 ], [ -175.7812, 65.0479 ], [ -175.7797, 65.0516 ], [ -175.787, 65.0568 ], [ -175.7833, 65.0625 ], [ -175.787, 65.0661 ], [ -175.7818, 65.0682 ], [ -175.7849, 65.0693 ], [ -175.7839, 65.0724 ], [ -175.7912, 65.0755 ], [ -175.7891, 65.0807 ], [ -175.7818, 65.0859 ], [ -175.7797, 65.0901 ], [ -175.7828, 65.0922 ], [ -175.7818, 65.0953 ], [ -175.7849, 65.0964 ], [ -175.7849, 65.0995 ], [ -175.7708, 65.1042 ], [ -175.763, 65.1047 ], [ -175.7609, 65.1068 ], [ -175.7641, 65.1088 ], [ -175.762, 65.1141 ], [ -175.7443, 65.1193 ], [ -175.7474, 65.1224 ], [ -175.7422, 65.1234 ], [ -175.7391, 65.1307 ], [ -175.737, 65.1328 ], [ -175.7318, 65.1339 ], [ -175.7292, 65.1396 ], [ -175.7328, 65.1422 ], [ -175.7318, 65.1453 ], [ -175.7349, 65.1464 ], [ -175.7318, 65.1536 ], [ -175.7474, 65.1589 ], [ -175.7422, 65.162 ], [ -175.7479, 65.1646 ], [ -175.7557, 65.1651 ], [ -175.7542, 65.1687 ], [ -175.7568, 65.1724 ], [ -175.7661, 65.1734 ], [ -175.7807, 65.1818 ], [ -175.7839, 65.187 ], [ -175.7896, 65.1896 ], [ -175.7958, 65.1896 ], [ -175.7995, 65.1922 ], [ -175.7995, 65.1953 ], [ -175.7979, 65.1958 ], [ -175.7693, 65.1964 ], [ -175.7672, 65.2005 ], [ -175.7703, 65.2026 ], [ -175.7693, 65.2057 ], [ -175.7792, 65.2104 ], [ -175.7854, 65.2083 ], [ -175.7979, 65.2125 ], [ -175.8042, 65.2125 ], [ -175.8083, 65.2104 ], [ -175.8167, 65.2146 ], [ -175.8245, 65.2151 ], [ -175.8292, 65.2188 ], [ -175.837, 65.2193 ], [ -175.8297, 65.2234 ], [ -175.8328, 65.2255 ], [ -175.8338, 65.2286 ], [ -175.8411, 65.2297 ], [ -175.8401, 65.2328 ], [ -175.8417, 65.2333 ], [ -175.8495, 65.2318 ], [ -175.8505, 65.2349 ], [ -175.8568, 65.2391 ], [ -175.8724, 65.2464 ], [ -175.8714, 65.2495 ], [ -175.8807, 65.2505 ], [ -175.8797, 65.2536 ], [ -175.887, 65.2547 ], [ -175.888, 65.2578 ], [ -175.8911, 65.2589 ], [ -175.8859, 65.2609 ], [ -175.8828, 65.2662 ], [ -175.8734, 65.2672 ], [ -175.8766, 65.2693 ], [ -175.8755, 65.2724 ], [ -175.8849, 65.2714 ], [ -175.8833, 65.275 ], [ -175.887, 65.2786 ], [ -175.8792, 65.2792 ], [ -175.875, 65.2771 ], [ -175.863, 65.2776 ], [ -175.8599, 65.2828 ], [ -175.8547, 65.2839 ], [ -175.8526, 65.2859 ], [ -175.8505, 65.2911 ], [ -175.8578, 65.2963 ], [ -175.8562, 65.3 ], [ -175.8589, 65.3057 ], [ -175.8703, 65.3109 ], [ -175.8776, 65.3161 ], [ -175.887, 65.3172 ], [ -175.8859, 65.3203 ], [ -175.8901, 65.3224 ], [ -175.8974, 65.3234 ], [ -175.9099, 65.3297 ], [ -175.9099, 65.3328 ], [ -175.9078, 65.3349 ], [ -175.8984, 65.3339 ], [ -175.8984, 65.337 ], [ -175.9016, 65.3391 ], [ -175.888, 65.3443 ], [ -175.8849, 65.3516 ], [ -175.8797, 65.3526 ], [ -175.8776, 65.3557 ], [ -175.887, 65.3568 ], [ -175.8838, 65.3641 ], [ -175.8911, 65.3651 ], [ -175.8901, 65.3724 ], [ -175.8974, 65.3755 ], [ -175.8943, 65.3828 ], [ -175.8974, 65.3839 ], [ -175.8984, 65.387 ], [ -175.9036, 65.3901 ], [ -175.9005, 65.3922 ], [ -175.9026, 65.3974 ], [ -175.9099, 65.4005 ], [ -175.9047, 65.4036 ], [ -175.9266, 65.4151 ], [ -175.9276, 65.4182 ], [ -175.95, 65.425 ], [ -175.9646, 65.4271 ], [ -175.9755, 65.4328 ], [ -175.9896, 65.4354 ], [ -175.9979, 65.4396 ], [ -176.0083, 65.4396 ], [ -176.0125, 65.4417 ], [ -176.0229, 65.4417 ], [ -176.0328, 65.4443 ], [ -176.037, 65.4464 ], [ -176.0359, 65.4495 ], [ -176.0391, 65.4516 ], [ -176.0312, 65.4542 ], [ -176.0208, 65.4542 ], [ -176.0172, 65.4568 ], [ -176.0188, 65.4583 ], [ -176.0266, 65.4589 ], [ -176.0312, 65.4625 ], [ -176.0375, 65.4604 ], [ -176.0688, 65.4604 ], [ -176.0792, 65.4563 ], [ -176.0854, 65.4583 ], [ -176.088, 65.4547 ], [ -176.0958, 65.4542 ], [ -176.1, 65.4563 ], [ -176.1146, 65.4563 ], [ -176.1187, 65.4583 ], [ -176.1292, 65.4583 ], [ -176.1333, 65.4604 ], [ -176.1438, 65.4604 ], [ -176.1479, 65.4625 ], [ -176.1625, 65.4625 ], [ -176.1667, 65.4646 ], [ -176.1729, 65.4625 ], [ -176.1854, 65.4667 ], [ -176.1958, 65.4667 ], [ -176.2, 65.4688 ], [ -176.2146, 65.4688 ], [ -176.2229, 65.4729 ], [ -176.2563, 65.475 ], [ -176.2708, 65.4792 ], [ -176.275, 65.4771 ], [ -176.2833, 65.4812 ], [ -176.3083, 65.4833 ], [ -176.3292, 65.4896 ], [ -176.3354, 65.4896 ], [ -176.3396, 65.4917 ], [ -176.35, 65.4917 ], [ -176.3708, 65.4979 ], [ -176.3771, 65.4979 ], [ -176.387, 65.5005 ], [ -176.3938, 65.5042 ], [ -176.4042, 65.5042 ], [ -176.4167, 65.5083 ], [ -176.4208, 65.5083 ], [ -176.4224, 65.5089 ], [ -176.4224, 65.512 ], [ -176.4172, 65.513 ], [ -176.4146, 65.5167 ], [ -176.4151, 65.5203 ], [ -176.4187, 65.5229 ], [ -176.45, 65.5229 ], [ -176.4557, 65.5255 ], [ -176.4547, 65.5286 ], [ -176.4563, 65.5292 ], [ -176.4625, 65.5292 ], [ -176.4672, 65.5255 ], [ -176.4729, 65.525 ], [ -176.4854, 65.5313 ], [ -176.4932, 65.5318 ], [ -176.4911, 65.5349 ], [ -176.4859, 65.5359 ], [ -176.4859, 65.5391 ], [ -176.4891, 65.5401 ], [ -176.488, 65.5432 ], [ -176.5021, 65.55 ], [ -176.5083, 65.55 ], [ -176.5125, 65.5521 ], [ -176.5229, 65.5521 ], [ -176.5333, 65.5479 ], [ -176.5437, 65.5479 ], [ -176.5479, 65.55 ], [ -176.5521, 65.5479 ], [ -176.575, 65.5479 ], [ -176.5792, 65.5458 ], [ -176.5854, 65.5458 ], [ -176.5896, 65.5479 ], [ -176.6042, 65.5479 ], [ -176.6083, 65.55 ], [ -176.6229, 65.55 ], [ -176.6255, 65.5537 ], [ -176.6328, 65.5547 ], [ -176.6359, 65.5599 ], [ -176.6438, 65.5604 ], [ -176.6479, 65.5625 ], [ -176.6646, 65.5625 ], [ -176.6687, 65.5604 ], [ -176.6792, 65.5604 ], [ -176.6875, 65.5646 ], [ -176.6979, 65.5646 ], [ -176.7021, 65.5667 ], [ -176.7167, 65.5667 ], [ -176.7375, 65.5729 ], [ -176.7479, 65.5729 ], [ -176.7521, 65.575 ], [ -176.7667, 65.575 ], [ -176.7708, 65.5771 ], [ -176.7875, 65.5771 ], [ -176.8083, 65.5833 ], [ -176.8188, 65.5833 ], [ -176.8271, 65.5875 ], [ -176.8375, 65.5875 ], [ -176.85, 65.5917 ], [ -176.8604, 65.5917 ], [ -176.8646, 65.5938 ], [ -176.8708, 65.5938 ], [ -176.8792, 65.5979 ], [ -176.8875, 65.5979 ], [ -176.8917, 65.6 ], [ -176.9125, 65.6 ], [ -176.9167, 65.6021 ], [ -176.9312, 65.6021 ], [ -176.9354, 65.6042 ], [ -176.95, 65.6042 ], [ -176.9516, 65.6047 ], [ -176.9516, 65.6078 ], [ -176.9359, 65.6088 ], [ -176.9328, 65.6141 ], [ -176.9276, 65.6151 ], [ -176.9276, 65.6182 ], [ -176.9396, 65.6188 ], [ -176.9563, 65.6271 ], [ -176.9625, 65.6271 ], [ -176.9667, 65.625 ], [ -176.9807, 65.6318 ], [ -176.9787, 65.6391 ], [ -176.9734, 65.6401 ], [ -176.9687, 65.6438 ], [ -176.9625, 65.6438 ], [ -176.95, 65.6396 ], [ -176.9187, 65.6396 ], [ -176.9172, 65.6401 ], [ -176.9146, 65.6458 ], [ -176.9224, 65.6516 ], [ -176.9146, 65.6542 ], [ -176.8979, 65.6542 ], [ -176.8938, 65.6521 ], [ -176.8875, 65.6521 ], [ -176.8833, 65.6542 ], [ -176.8755, 65.6547 ], [ -176.8734, 65.6568 ], [ -176.8792, 65.6604 ], [ -176.8854, 65.6604 ], [ -176.8917, 65.6563 ], [ -176.8958, 65.6604 ], [ -176.9, 65.6604 ], [ -176.9068, 65.6641 ], [ -176.9167, 65.6667 ], [ -176.9229, 65.6667 ], [ -176.9432, 65.6599 ], [ -176.9463, 65.6526 ], [ -176.9479, 65.6521 ], [ -176.975, 65.6521 ], [ -176.9797, 65.6484 ], [ -176.9875, 65.6479 ], [ -176.9953, 65.6453 ], [ -176.9964, 65.6422 ], [ -177.0016, 65.6412 ], [ -177.0036, 65.6391 ], [ -177.0005, 65.637 ], [ -176.9995, 65.6339 ], [ -176.9964, 65.6328 ], [ -176.9984, 65.6276 ], [ -177.0125, 65.6271 ], [ -177.0229, 65.6229 ], [ -177.0292, 65.6229 ], [ -177.0339, 65.6172 ], [ -177.0417, 65.6167 ], [ -177.0599, 65.612 ], [ -177.063, 65.6088 ], [ -177.0682, 65.6078 ], [ -177.0708, 65.6042 ], [ -177.1057, 65.5995 ], [ -177.1089, 65.5964 ], [ -177.1141, 65.5953 ], [ -177.1141, 65.5922 ], [ -177.1109, 65.5911 ], [ -177.1125, 65.5896 ], [ -177.125, 65.5896 ], [ -177.1458, 65.5833 ], [ -177.1583, 65.5833 ], [ -177.1662, 65.5807 ], [ -177.1693, 65.5755 ], [ -177.1786, 65.5745 ], [ -177.1797, 65.5714 ], [ -177.1911, 65.5703 ], [ -177.1922, 65.5672 ], [ -177.1974, 65.5661 ], [ -177.1984, 65.563 ], [ -177.2036, 65.562 ], [ -177.2068, 65.5589 ], [ -177.2161, 65.5557 ], [ -177.2193, 65.5526 ], [ -177.2287, 65.5495 ], [ -177.2318, 65.5463 ], [ -177.237, 65.5453 ], [ -177.238, 65.5422 ], [ -177.2432, 65.5411 ], [ -177.2453, 65.5391 ], [ -177.2464, 65.5339 ], [ -177.2563, 65.5333 ], [ -177.2588, 65.5297 ], [ -177.2703, 65.5266 ], [ -177.2713, 65.5234 ], [ -177.2766, 65.5224 ], [ -177.2828, 65.5182 ], [ -177.2901, 65.5068 ], [ -177.2953, 65.5057 ], [ -177.2964, 65.5026 ], [ -177.3078, 65.4995 ], [ -177.3047, 65.4964 ], [ -177.3141, 65.4932 ], [ -177.3172, 65.4901 ], [ -177.3313, 65.4854 ], [ -177.3479, 65.4854 ], [ -177.3583, 65.4812 ], [ -177.375, 65.4812 ], [ -177.3792, 65.4792 ], [ -177.3854, 65.4792 ], [ -177.4125, 65.4708 ], [ -177.4167, 65.4729 ], [ -177.4229, 65.4729 ], [ -177.4271, 65.4708 ], [ -177.4396, 65.4708 ], [ -177.4438, 65.4688 ], [ -177.4458, 65.4708 ], [ -177.4443, 65.4745 ], [ -177.4526, 65.4786 ], [ -177.4708, 65.4792 ], [ -177.4734, 65.4755 ], [ -177.4849, 65.4724 ], [ -177.4734, 65.4682 ], [ -177.475, 65.4667 ], [ -177.4958, 65.4667 ], [ -177.5, 65.4646 ], [ -177.5104, 65.4646 ], [ -177.5146, 65.4667 ], [ -177.5208, 65.4646 ], [ -177.5271, 65.4667 ], [ -177.5312, 65.4646 ], [ -177.5479, 65.4646 ], [ -177.5521, 65.4625 ], [ -177.5562, 65.4646 ], [ -177.5625, 65.4646 ], [ -177.5667, 65.4625 ], [ -177.5771, 65.4625 ], [ -177.5813, 65.4646 ], [ -177.5875, 65.4646 ], [ -177.5917, 65.4625 ], [ -177.5979, 65.4646 ], [ -177.6125, 65.4604 ], [ -177.6167, 65.4625 ], [ -177.6229, 65.4625 ], [ -177.6271, 65.4604 ], [ -177.6396, 65.4604 ], [ -177.6438, 65.4583 ], [ -177.65, 65.4583 ], [ -177.6542, 65.4604 ], [ -177.6687, 65.4563 ], [ -177.675, 65.4583 ], [ -177.6792, 65.4563 ], [ -177.6854, 65.4563 ], [ -177.6896, 65.4583 ], [ -177.6938, 65.4563 ], [ -177.7021, 65.4563 ], [ -177.7063, 65.4542 ], [ -177.7417, 65.4542 ], [ -177.7458, 65.4521 ], [ -177.7542, 65.4521 ], [ -177.7583, 65.45 ], [ -177.7792, 65.45 ], [ -177.7807, 65.4505 ], [ -177.7797, 65.4557 ], [ -177.787, 65.4568 ], [ -177.7937, 65.4604 ], [ -177.8083, 65.4625 ], [ -177.8125, 65.4646 ], [ -177.8271, 65.4646 ], [ -177.8313, 65.4667 ], [ -177.8417, 65.4667 ], [ -177.8458, 65.4646 ], [ -177.8521, 65.4667 ], [ -177.8687, 65.4604 ], [ -177.8792, 65.4604 ], [ -177.8854, 65.4563 ], [ -177.8958, 65.4563 ], [ -177.9, 65.4583 ], [ -177.9063, 65.4583 ], [ -177.9104, 65.4563 ], [ -177.9146, 65.4583 ], [ -177.9208, 65.4583 ], [ -177.925, 65.4563 ], [ -177.9292, 65.4583 ], [ -177.9354, 65.4583 ], [ -177.9396, 65.4563 ], [ -177.9458, 65.4563 ], [ -177.95, 65.4583 ], [ -178.0063, 65.4583 ], [ -178.0104, 65.4563 ], [ -178.0167, 65.4563 ], [ -178.0208, 65.4583 ], [ -178.0312, 65.4583 ], [ -178.0437, 65.4625 ], [ -178.0833, 65.4625 ], [ -178.0979, 65.4667 ], [ -178.1333, 65.4667 ], [ -178.1375, 65.4688 ], [ -178.1417, 65.4688 ], [ -178.1521, 65.4646 ], [ -178.1583, 65.4667 ], [ -178.1625, 65.4667 ], [ -178.1667, 65.4646 ], [ -178.1792, 65.4646 ], [ -178.1833, 65.4667 ], [ -178.1979, 65.4646 ], [ -178.2042, 65.4667 ], [ -178.2187, 65.4604 ], [ -178.2287, 65.4599 ], [ -178.2312, 65.4563 ], [ -178.2688, 65.4542 ], [ -178.2786, 65.4568 ], [ -178.2812, 65.4604 ], [ -178.3, 65.4604 ], [ -178.3042, 65.4625 ], [ -178.3354, 65.4583 ], [ -178.3396, 65.4604 ], [ -178.3708, 65.4604 ], [ -178.3833, 65.4646 ], [ -178.3896, 65.4625 ], [ -178.3938, 65.4646 ], [ -178.4042, 65.4646 ], [ -178.4083, 65.4667 ], [ -178.4229, 65.4667 ], [ -178.4271, 65.4688 ], [ -178.4391, 65.4693 ], [ -178.4474, 65.4734 ], [ -178.4484, 65.4766 ], [ -178.4557, 65.4797 ], [ -178.4547, 65.4828 ], [ -178.4641, 65.4838 ], [ -178.463, 65.487 ], [ -178.4807, 65.4901 ], [ -178.4797, 65.4953 ], [ -178.4839, 65.4974 ], [ -178.5063, 65.5 ], [ -178.5088, 65.4964 ], [ -178.5125, 65.4958 ], [ -178.5182, 65.4984 ], [ -178.513, 65.5005 ], [ -178.5161, 65.5026 ], [ -178.5172, 65.5057 ], [ -178.5375, 65.5062 ], [ -178.5391, 65.5078 ], [ -178.5339, 65.5109 ], [ -178.537, 65.513 ], [ -178.537, 65.5162 ], [ -178.5318, 65.5193 ], [ -178.5266, 65.5266 ], [ -178.5203, 65.5307 ], [ -178.5151, 65.5318 ], [ -178.5063, 65.5375 ], [ -178.5, 65.5354 ], [ -178.4974, 65.5391 ], [ -178.4833, 65.5396 ], [ -178.4807, 65.5432 ], [ -178.4755, 65.5443 ], [ -178.4828, 65.5484 ], [ -178.4828, 65.5516 ], [ -178.4807, 65.5557 ], [ -178.4755, 65.5568 ], [ -178.4724, 65.5641 ], [ -178.4672, 65.5651 ], [ -178.4667, 65.5667 ], [ -178.4703, 65.5693 ], [ -178.4687, 65.5729 ], [ -178.4724, 65.5755 ], [ -178.4714, 65.5807 ], [ -178.4787, 65.5839 ], [ -178.4797, 65.587 ], [ -178.4828, 65.588 ], [ -178.4818, 65.5911 ], [ -178.4849, 65.5922 ], [ -178.4849, 65.5953 ], [ -178.4771, 65.5958 ], [ -178.4693, 65.5984 ], [ -178.4682, 65.6016 ], [ -178.4583, 65.6021 ], [ -178.4557, 65.6057 ], [ -178.4505, 65.6068 ], [ -178.4505, 65.6099 ], [ -178.4536, 65.612 ], [ -178.4484, 65.613 ], [ -178.4484, 65.6162 ], [ -178.4542, 65.6167 ], [ -178.4557, 65.6182 ], [ -178.4505, 65.6193 ], [ -178.4484, 65.6245 ], [ -178.4516, 65.6266 ], [ -178.4464, 65.6297 ], [ -178.4443, 65.6339 ], [ -178.4474, 65.6359 ], [ -178.4474, 65.6391 ], [ -178.4422, 65.6401 ], [ -178.4458, 65.6438 ], [ -178.4443, 65.6495 ], [ -178.4474, 65.6516 ], [ -178.4422, 65.6526 ], [ -178.4453, 65.6557 ], [ -178.4401, 65.6578 ], [ -178.4453, 65.6599 ], [ -178.4401, 65.6609 ], [ -178.437, 65.6661 ], [ -178.4318, 65.6672 ], [ -178.4307, 65.6703 ], [ -178.4193, 65.6713 ], [ -178.4182, 65.6766 ], [ -178.4068, 65.6797 ], [ -178.4104, 65.6833 ], [ -178.4099, 65.6849 ], [ -178.4047, 65.6859 ], [ -178.4026, 65.688 ], [ -178.4026, 65.6932 ], [ -178.4057, 65.6943 ], [ -178.4026, 65.7037 ], [ -178.4104, 65.7042 ], [ -178.4162, 65.7068 ], [ -178.4109, 65.7099 ], [ -178.4182, 65.7109 ], [ -178.4172, 65.7203 ], [ -178.437, 65.7297 ], [ -178.438, 65.7328 ], [ -178.4453, 65.7359 ], [ -178.4443, 65.7391 ], [ -178.4474, 65.7401 ], [ -178.4484, 65.7432 ], [ -178.4625, 65.7479 ], [ -178.4687, 65.7479 ], [ -178.4771, 65.7521 ], [ -178.5125, 65.7521 ], [ -178.5167, 65.75 ], [ -178.525, 65.75 ], [ -178.5396, 65.7458 ], [ -178.5458, 65.7458 ], [ -178.5792, 65.7354 ], [ -178.5953, 65.738 ], [ -178.5995, 65.7401 ], [ -178.6005, 65.7432 ], [ -178.6078, 65.7443 ], [ -178.6161, 65.7484 ], [ -178.6146, 65.75 ], [ -178.6068, 65.7505 ], [ -178.6068, 65.7536 ], [ -178.6099, 65.7547 ], [ -178.6089, 65.7578 ], [ -178.6167, 65.7604 ], [ -178.625, 65.7604 ], [ -178.6292, 65.7625 ], [ -178.637, 65.763 ], [ -178.638, 65.7662 ], [ -178.6411, 65.7682 ], [ -178.6338, 65.7724 ], [ -178.6432, 65.7734 ], [ -178.638, 65.7755 ], [ -178.637, 65.7786 ], [ -178.6318, 65.7797 ], [ -178.6297, 65.7849 ], [ -178.6333, 65.7875 ], [ -178.6307, 65.7953 ], [ -178.6255, 65.7963 ], [ -178.6286, 65.7995 ], [ -178.6172, 65.8037 ], [ -178.6396, 65.8042 ], [ -178.65, 65.8 ], [ -178.6771, 65.8 ], [ -178.6875, 65.7958 ], [ -178.6911, 65.7963 ], [ -178.6995, 65.8005 ], [ -178.6979, 65.8042 ], [ -178.7016, 65.8078 ], [ -178.6963, 65.8089 ], [ -178.7, 65.8125 ], [ -178.6963, 65.8214 ], [ -178.7, 65.825 ], [ -178.6979, 65.8313 ], [ -178.6984, 65.8328 ], [ -178.7057, 65.8359 ], [ -178.7068, 65.8391 ], [ -178.7099, 65.8401 ], [ -178.7088, 65.8432 ], [ -178.7146, 65.8458 ], [ -178.7224, 65.8464 ], [ -178.7234, 65.8495 ], [ -178.7401, 65.8578 ], [ -178.7458, 65.8583 ], [ -178.7516, 65.8609 ], [ -178.7526, 65.8641 ], [ -178.7604, 65.8646 ], [ -178.7813, 65.8729 ], [ -178.7875, 65.8729 ], [ -178.7943, 65.8766 ], [ -178.8016, 65.8776 ], [ -178.8026, 65.8807 ], [ -178.8083, 65.8812 ], [ -178.8213, 65.887 ], [ -178.8286, 65.888 ], [ -178.8411, 65.8943 ], [ -178.837, 65.8974 ], [ -178.8318, 65.8984 ], [ -178.8297, 65.9016 ], [ -178.8479, 65.9021 ], [ -178.8495, 65.9036 ], [ -178.8417, 65.9042 ], [ -178.8354, 65.9083 ], [ -178.8313, 65.9063 ], [ -178.825, 65.9063 ], [ -178.8213, 65.9089 ], [ -178.8266, 65.912 ], [ -178.8213, 65.913 ], [ -178.8193, 65.9151 ], [ -178.8224, 65.9172 ], [ -178.8224, 65.9203 ], [ -178.8172, 65.9213 ], [ -178.8167, 65.9229 ], [ -178.8203, 65.9255 ], [ -178.8193, 65.9307 ], [ -178.8224, 65.9318 ], [ -178.8172, 65.9339 ], [ -178.8224, 65.9359 ], [ -178.8188, 65.9437 ], [ -178.8224, 65.9464 ], [ -178.8234, 65.9495 ], [ -178.8307, 65.9526 ], [ -178.8318, 65.9557 ], [ -178.8349, 65.9568 ], [ -178.8338, 65.962 ], [ -178.8411, 65.9651 ], [ -178.8401, 65.9724 ], [ -178.8474, 65.9776 ], [ -178.8422, 65.9797 ], [ -178.8422, 65.9828 ], [ -178.8453, 65.9838 ], [ -178.8443, 65.987 ], [ -178.8562, 65.9875 ], [ -178.8724, 65.9943 ], [ -178.8687, 65.9979 ], [ -178.8646, 65.9958 ], [ -178.8583, 65.9958 ], [ -178.85, 65.9917 ], [ -178.838, 65.9912 ], [ -178.8391, 65.988 ], [ -178.8313, 65.9875 ], [ -178.8229, 65.9833 ], [ -178.8167, 65.9833 ], [ -178.8042, 65.9792 ], [ -178.7937, 65.9792 ], [ -178.7792, 65.975 ], [ -178.7688, 65.975 ], [ -178.7646, 65.9771 ], [ -178.7583, 65.975 ], [ -178.7333, 65.975 ], [ -178.7297, 65.9776 ], [ -178.7328, 65.9797 ], [ -178.7328, 65.9849 ], [ -178.7245, 65.9912 ], [ -178.7088, 65.9922 ], [ -178.712, 65.9943 ], [ -178.712, 65.9974 ], [ -178.7042, 65.9979 ], [ -178.6938, 66.0021 ], [ -178.6812, 66.0021 ], [ -178.6771, 66.0042 ], [ -178.6693, 66.0047 ], [ -178.6724, 66.0068 ], [ -178.6724, 66.0099 ], [ -178.6672, 66.0109 ], [ -178.6599, 66.0162 ], [ -178.6484, 66.0193 ], [ -178.6484, 66.0224 ], [ -178.6516, 66.0234 ], [ -178.6505, 66.0266 ], [ -178.6542, 66.0292 ], [ -178.6521, 66.0354 ], [ -178.6526, 66.0391 ], [ -178.6557, 66.0401 ], [ -178.6547, 66.0432 ], [ -178.662, 66.0463 ], [ -178.6625, 66.0479 ], [ -178.662, 66.0495 ], [ -178.6568, 66.0505 ], [ -178.6557, 66.0557 ], [ -178.6536, 66.0578 ], [ -178.6484, 66.0589 ], [ -178.6516, 66.062 ], [ -178.6422, 66.0651 ], [ -178.6453, 66.0672 ], [ -178.6443, 66.0703 ], [ -178.6495, 66.0714 ], [ -178.6458, 66.0792 ], [ -178.65, 66.0833 ], [ -178.6474, 66.087 ], [ -178.6396, 66.0854 ], [ -178.625, 66.0917 ], [ -178.6187, 66.0917 ], [ -178.6146, 66.0896 ], [ -178.5833, 66.0896 ], [ -178.5792, 66.0875 ], [ -178.5729, 66.0875 ], [ -178.5589, 66.0922 ], [ -178.5578, 66.0953 ], [ -178.5526, 66.0964 ], [ -178.5516, 66.0995 ], [ -178.5479, 66.1021 ], [ -178.5312, 66.1042 ], [ -178.5229, 66.1 ], [ -178.5167, 66.1 ], [ -178.512, 66.1057 ], [ -178.5078, 66.1078 ], [ -178.4964, 66.1088 ], [ -178.4964, 66.112 ], [ -178.5, 66.1146 ], [ -178.4974, 66.1203 ], [ -178.4922, 66.1214 ], [ -178.4911, 66.1245 ], [ -178.4859, 66.1255 ], [ -178.4828, 66.1307 ], [ -178.4776, 66.1318 ], [ -178.4766, 66.1349 ], [ -178.4714, 66.138 ], [ -178.4693, 66.1412 ], [ -178.4766, 66.1443 ], [ -178.4776, 66.1495 ], [ -178.4807, 66.1506 ], [ -178.4797, 66.1536 ], [ -178.4854, 66.1563 ], [ -178.4932, 66.1568 ], [ -178.4943, 66.1599 ], [ -178.5016, 66.1609 ], [ -178.5083, 66.1646 ], [ -178.5146, 66.1646 ], [ -178.5161, 66.1661 ], [ -178.5109, 66.1672 ], [ -178.5078, 66.1766 ], [ -178.5026, 66.1776 ], [ -178.4953, 66.1828 ], [ -178.4901, 66.1839 ], [ -178.4932, 66.187 ], [ -178.4818, 66.1901 ], [ -178.4849, 66.1932 ], [ -178.4797, 66.1943 ], [ -178.4734, 66.1984 ], [ -178.4766, 66.2016 ], [ -178.4672, 66.2047 ], [ -178.4703, 66.2078 ], [ -178.4651, 66.2089 ], [ -178.4604, 66.2125 ], [ -178.4542, 66.2125 ], [ -178.4464, 66.2151 ], [ -178.4453, 66.2182 ], [ -178.4401, 66.2193 ], [ -178.4432, 66.2214 ], [ -178.4443, 66.2245 ], [ -178.4474, 66.2255 ], [ -178.4484, 66.2286 ], [ -178.4661, 66.2318 ], [ -178.4745, 66.2359 ], [ -178.4755, 66.2391 ], [ -178.4828, 66.2401 ], [ -178.4818, 66.2453 ], [ -178.4896, 66.2458 ], [ -178.4917, 66.2479 ], [ -178.4891, 66.2516 ], [ -178.4672, 66.2589 ], [ -178.4641, 66.262 ], [ -178.4547, 66.2651 ], [ -178.4464, 66.2714 ], [ -178.4453, 66.2745 ], [ -178.4339, 66.2776 ], [ -178.4333, 66.2792 ], [ -178.4339, 66.2807 ], [ -178.4453, 66.2859 ], [ -178.4453, 66.2891 ], [ -178.4401, 66.2911 ], [ -178.4474, 66.2943 ], [ -178.4484, 66.2974 ], [ -178.4516, 66.2984 ], [ -178.4495, 66.3057 ], [ -178.4443, 66.3068 ], [ -178.4438, 66.3083 ], [ -178.4474, 66.3109 ], [ -178.4464, 66.3141 ], [ -178.4495, 66.3151 ], [ -178.4505, 66.3182 ], [ -178.4542, 66.3208 ], [ -178.4516, 66.3245 ], [ -178.4464, 66.3276 ], [ -178.4432, 66.3328 ], [ -178.4401, 66.3339 ], [ -178.4432, 66.337 ], [ -178.438, 66.3391 ], [ -178.4453, 66.3401 ], [ -178.4458, 66.3438 ], [ -178.4453, 66.3453 ], [ -178.4401, 66.3464 ], [ -178.438, 66.3495 ], [ -178.4432, 66.3505 ], [ -178.4417, 66.3562 ], [ -178.4453, 66.3588 ], [ -178.4443, 66.362 ], [ -178.462, 66.3693 ], [ -178.4599, 66.3766 ], [ -178.4547, 66.3786 ], [ -178.4578, 66.3797 ], [ -178.4588, 66.3828 ], [ -178.4672, 66.387 ], [ -178.475, 66.3875 ], [ -178.4792, 66.3896 ], [ -178.4833, 66.3875 ], [ -178.4937, 66.3875 ], [ -178.5083, 66.3917 ], [ -178.5292, 66.3917 ], [ -178.5333, 66.3938 ], [ -178.537, 66.3932 ], [ -178.538, 66.388 ], [ -178.5432, 66.3859 ], [ -178.5359, 66.3828 ], [ -178.5375, 66.3771 ], [ -178.5339, 66.3745 ], [ -178.5391, 66.3724 ], [ -178.5412, 66.3682 ], [ -178.5359, 66.3662 ], [ -178.5412, 66.3641 ], [ -178.538, 66.362 ], [ -178.5396, 66.3583 ], [ -178.5359, 66.3557 ], [ -178.5359, 66.3505 ], [ -178.5458, 66.3458 ], [ -178.5729, 66.3438 ], [ -178.5755, 66.3474 ], [ -178.5786, 66.3484 ], [ -178.5776, 66.3516 ], [ -178.5854, 66.3542 ], [ -178.6021, 66.3542 ], [ -178.6037, 66.3536 ], [ -178.6047, 66.3484 ], [ -178.6099, 66.3474 ], [ -178.6109, 66.3443 ], [ -178.6224, 66.3411 ], [ -178.625, 66.3375 ], [ -178.6245, 66.3339 ], [ -178.613, 66.3287 ], [ -178.612, 66.3255 ], [ -178.6089, 66.3234 ], [ -178.6141, 66.3224 ], [ -178.6172, 66.3151 ], [ -178.6224, 66.3141 ], [ -178.6234, 66.3109 ], [ -178.6286, 66.3099 ], [ -178.6286, 66.3068 ], [ -178.6255, 66.3057 ], [ -178.6266, 66.3005 ], [ -178.6234, 66.2984 ], [ -178.6286, 66.2974 ], [ -178.6318, 66.288 ], [ -178.637, 66.287 ], [ -178.637, 66.2839 ], [ -178.6338, 66.2828 ], [ -178.6359, 66.2776 ], [ -178.6438, 66.2771 ], [ -178.6578, 66.2724 ], [ -178.6604, 66.2688 ], [ -178.6687, 66.2688 ], [ -178.687, 66.2641 ], [ -178.687, 66.2609 ], [ -178.6839, 66.2589 ], [ -178.6917, 66.2583 ], [ -178.6963, 66.2547 ], [ -178.7016, 66.2536 ], [ -178.7068, 66.238 ], [ -178.712, 66.237 ], [ -178.7172, 66.2234 ], [ -178.7224, 66.2224 ], [ -178.7255, 66.2172 ], [ -178.7307, 66.2161 ], [ -178.7333, 66.2083 ], [ -178.7328, 66.2068 ], [ -178.7255, 66.2037 ], [ -178.7276, 66.1943 ], [ -178.7354, 66.1917 ], [ -178.7479, 66.1917 ], [ -178.7557, 66.1891 ], [ -178.7568, 66.1859 ], [ -178.7646, 66.1833 ], [ -178.7833, 66.1813 ], [ -178.7901, 66.1776 ], [ -178.8203, 66.1682 ], [ -178.8213, 66.1651 ], [ -178.8292, 66.1646 ], [ -178.837, 66.162 ], [ -178.838, 66.1589 ], [ -178.8458, 66.1563 ], [ -178.8833, 66.1563 ], [ -178.8875, 66.1542 ], [ -178.9062, 66.1542 ], [ -178.9104, 66.1521 ], [ -178.9146, 66.1542 ], [ -178.925, 66.1542 ], [ -178.9396, 66.1583 ], [ -178.95, 66.1583 ], [ -178.9599, 66.1609 ], [ -178.9609, 66.1641 ], [ -178.9651, 66.1661 ], [ -178.9745, 66.1672 ], [ -178.9734, 66.1703 ], [ -178.9812, 66.1708 ], [ -178.987, 66.1734 ], [ -178.9818, 66.1766 ], [ -178.9896, 66.1771 ], [ -178.9995, 66.1818 ], [ -178.9901, 66.187 ], [ -178.9932, 66.188 ], [ -178.9922, 66.1911 ], [ -178.9953, 66.1922 ], [ -178.9964, 66.1953 ], [ -179.0036, 66.1984 ], [ -179.0047, 66.2016 ], [ -179.0078, 66.2026 ], [ -179.0088, 66.2057 ], [ -179.0297, 66.2161 ], [ -179.0432, 66.2193 ], [ -179.0422, 66.2245 ], [ -179.0583, 66.2292 ], [ -179.0646, 66.2292 ], [ -179.0786, 66.2359 ], [ -179.0786, 66.2412 ], [ -179.0734, 66.2422 ], [ -179.0766, 66.2443 ], [ -179.0766, 66.2474 ], [ -179.0724, 66.2536 ], [ -179.0693, 66.2547 ], [ -179.0724, 66.2578 ], [ -179.0625, 66.2583 ], [ -179.0536, 66.2641 ], [ -179.0443, 66.2651 ], [ -179.0417, 66.2729 ], [ -179.0453, 66.2766 ], [ -179.038, 66.2818 ], [ -179.05, 66.2937 ], [ -179.0474, 66.3016 ], [ -179.0453, 66.3037 ], [ -179.0401, 66.3047 ], [ -179.0453, 66.3068 ], [ -179.0443, 66.312 ], [ -179.0516, 66.3172 ], [ -179.0464, 66.3193 ], [ -179.0458, 66.3208 ], [ -179.0495, 66.3234 ], [ -179.0484, 66.3287 ], [ -179.0557, 66.3318 ], [ -179.0568, 66.3349 ], [ -179.0599, 66.3359 ], [ -179.0547, 66.338 ], [ -179.0536, 66.3411 ], [ -179.0484, 66.3422 ], [ -179.0479, 66.3438 ], [ -179.0484, 66.3453 ], [ -179.0557, 66.3484 ], [ -179.0505, 66.3505 ], [ -179.05, 66.3521 ], [ -179.0542, 66.3562 ], [ -179.0521, 66.3604 ], [ -179.0526, 66.362 ], [ -179.0599, 66.3651 ], [ -179.0568, 66.3682 ], [ -179.0667, 66.3729 ], [ -179.0708, 66.3708 ], [ -179.0807, 66.3703 ], [ -179.0818, 66.3672 ], [ -179.0896, 66.3667 ], [ -179.0958, 66.3625 ], [ -179.1, 66.3646 ], [ -179.1037, 66.3641 ], [ -179.1037, 66.3609 ], [ -179.1005, 66.3599 ], [ -179.1005, 66.3568 ], [ -179.1089, 66.3505 ], [ -179.1141, 66.3495 ], [ -179.1203, 66.3453 ], [ -179.1229, 66.3354 ], [ -179.1193, 66.3318 ], [ -179.1245, 66.3297 ], [ -179.1151, 66.3287 ], [ -179.1167, 66.3229 ], [ -179.113, 66.3193 ], [ -179.1182, 66.3182 ], [ -179.1203, 66.3161 ], [ -179.1151, 66.313 ], [ -179.1203, 66.312 ], [ -179.1224, 66.3099 ], [ -179.1229, 66.3063 ], [ -179.1151, 66.2984 ], [ -179.1266, 66.2953 ], [ -179.1276, 66.2922 ], [ -179.1328, 66.2911 ], [ -179.1328, 66.288 ], [ -179.1297, 66.287 ], [ -179.1307, 66.2818 ], [ -179.1276, 66.2807 ], [ -179.1328, 66.2786 ], [ -179.1297, 66.2766 ], [ -179.1297, 66.2734 ], [ -179.1479, 66.2708 ], [ -179.1526, 66.2672 ], [ -179.1604, 66.2646 ], [ -179.1687, 66.2688 ], [ -179.1766, 66.2693 ], [ -179.1875, 66.275 ], [ -179.2021, 66.2771 ], [ -179.2125, 66.2813 ], [ -179.2271, 66.2813 ], [ -179.2312, 66.2833 ], [ -179.2375, 66.2813 ], [ -179.2401, 66.2849 ], [ -179.25, 66.2875 ], [ -179.2625, 66.2875 ], [ -179.275, 66.2917 ], [ -179.2812, 66.2896 ], [ -179.287, 66.2922 ], [ -179.288, 66.2953 ], [ -179.3, 66.2958 ], [ -179.3026, 66.2995 ], [ -179.3099, 66.3005 ], [ -179.3109, 66.3037 ], [ -179.3203, 66.3047 ], [ -179.3193, 66.3099 ], [ -179.3224, 66.3109 ], [ -179.3234, 66.3141 ], [ -179.3359, 66.3203 ], [ -179.35, 66.3208 ], [ -179.3568, 66.3245 ], [ -179.3667, 66.3271 ], [ -179.3729, 66.3271 ], [ -179.3771, 66.3292 ], [ -179.3875, 66.3292 ], [ -179.4104, 66.3354 ], [ -179.4167, 66.3333 ], [ -179.4271, 66.3375 ], [ -179.4375, 66.3375 ], [ -179.4391, 66.3359 ], [ -179.4297, 66.3349 ], [ -179.4307, 66.3318 ], [ -179.4276, 66.3297 ], [ -179.4458, 66.3292 ], [ -179.45, 66.3271 ], [ -179.4578, 66.3266 ], [ -179.4563, 66.325 ], [ -179.4146, 66.325 ], [ -179.4089, 66.3214 ], [ -179.4167, 66.3208 ], [ -179.4182, 66.3193 ], [ -179.3958, 66.3187 ], [ -179.3901, 66.3161 ], [ -179.3891, 66.313 ], [ -179.3859, 66.3109 ], [ -179.3911, 66.3099 ], [ -179.3922, 66.3068 ], [ -179.3974, 66.3047 ], [ -179.3901, 66.2995 ], [ -179.3891, 66.2963 ], [ -179.3833, 66.2958 ], [ -179.3813, 66.2937 ], [ -179.3838, 66.2859 ], [ -179.3891, 66.2839 ], [ -179.3813, 66.2833 ], [ -179.3797, 66.2828 ], [ -179.3807, 66.2797 ], [ -179.3625, 66.2771 ], [ -179.3526, 66.2724 ], [ -179.3578, 66.2703 ], [ -179.3583, 66.2688 ], [ -179.3547, 66.2662 ], [ -179.3557, 66.2609 ], [ -179.3437, 66.2604 ], [ -179.3396, 66.2583 ], [ -179.3172, 66.2578 ], [ -179.3161, 66.2526 ], [ -179.3089, 66.2516 ], [ -179.3078, 66.2484 ], [ -179.3, 66.2479 ], [ -179.2943, 66.2453 ], [ -179.2953, 66.238 ], [ -179.2854, 66.2333 ], [ -179.2734, 66.2328 ], [ -179.2693, 66.2307 ], [ -179.2682, 66.2276 ], [ -179.2651, 66.2266 ], [ -179.2641, 66.2234 ], [ -179.2484, 66.2161 ], [ -179.2484, 66.213 ], [ -179.2563, 66.2146 ], [ -179.2578, 66.2141 ], [ -179.2578, 66.2109 ], [ -179.2505, 66.2078 ], [ -179.2479, 66.2042 ], [ -179.2437, 66.2063 ], [ -179.2401, 66.2057 ], [ -179.2453, 66.2005 ], [ -179.238, 66.1995 ], [ -179.237, 66.1964 ], [ -179.2339, 66.1953 ], [ -179.2349, 66.1901 ], [ -179.2292, 66.1875 ], [ -179.2109, 66.187 ], [ -179.2109, 66.1839 ], [ -179.2161, 66.1828 ], [ -179.2182, 66.1807 ], [ -179.2214, 66.1713 ], [ -179.2266, 66.1703 ], [ -179.2234, 66.1682 ], [ -179.2234, 66.163 ], [ -179.2287, 66.162 ], [ -179.2297, 66.1589 ], [ -179.2349, 66.1578 ], [ -179.238, 66.1547 ], [ -179.2474, 66.1536 ], [ -179.2484, 66.1505 ], [ -179.2599, 66.1495 ], [ -179.2547, 66.1464 ], [ -179.2661, 66.1453 ], [ -179.2672, 66.1422 ], [ -179.2724, 66.1412 ], [ -179.2734, 66.138 ], [ -179.2849, 66.137 ], [ -179.2859, 66.1339 ], [ -179.2912, 66.1328 ], [ -179.2922, 66.1297 ], [ -179.3104, 66.125 ], [ -179.3167, 66.125 ], [ -179.3333, 66.1188 ], [ -179.3396, 66.1208 ], [ -179.3562, 66.1146 ], [ -179.3771, 66.1146 ], [ -179.3813, 66.1125 ], [ -179.3875, 66.1146 ], [ -179.3917, 66.1125 ], [ -179.4083, 66.1125 ], [ -179.4125, 66.1104 ], [ -179.4292, 66.1104 ], [ -179.4333, 66.1125 ], [ -179.4854, 66.1125 ], [ -179.4917, 66.1104 ], [ -179.4958, 66.1125 ], [ -179.5021, 66.1125 ], [ -179.5063, 66.1104 ], [ -179.5333, 66.1104 ], [ -179.5375, 66.1083 ], [ -179.5417, 66.1104 ], [ -179.5542, 66.1104 ], [ -179.5599, 66.1141 ], [ -179.5547, 66.1151 ], [ -179.5536, 66.1182 ], [ -179.5354, 66.1167 ], [ -179.5276, 66.1224 ], [ -179.5474, 66.1318 ], [ -179.5484, 66.1349 ], [ -179.5557, 66.138 ], [ -179.5568, 66.1412 ], [ -179.5641, 66.1443 ], [ -179.5589, 66.1464 ], [ -179.5562, 66.1542 ], [ -179.5599, 66.1568 ], [ -179.5609, 66.1599 ], [ -179.5771, 66.1625 ], [ -179.5797, 66.1661 ], [ -179.5958, 66.1687 ], [ -179.5984, 66.1724 ], [ -179.6042, 66.1729 ], [ -179.6146, 66.1771 ], [ -179.6187, 66.175 ], [ -179.6313, 66.175 ], [ -179.6396, 66.1708 ], [ -179.6521, 66.1708 ], [ -179.6562, 66.1687 ], [ -179.6729, 66.1687 ], [ -179.6776, 66.1651 ], [ -179.6828, 66.1641 ], [ -179.6854, 66.1563 ], [ -179.6818, 66.1526 ], [ -179.687, 66.1516 ], [ -179.687, 66.1484 ], [ -179.6693, 66.1453 ], [ -179.6745, 66.1412 ], [ -179.6755, 66.1359 ], [ -179.6818, 66.1318 ], [ -179.6917, 66.1312 ], [ -179.6943, 66.1276 ], [ -179.7036, 66.1266 ], [ -179.7005, 66.1245 ], [ -179.7026, 66.1214 ], [ -179.712, 66.1203 ], [ -179.713, 66.1151 ], [ -179.7245, 66.112 ], [ -179.7255, 66.1088 ], [ -179.7521, 66.1 ], [ -179.7625, 66.1 ], [ -179.7667, 66.0979 ], [ -179.7729, 66.0979 ], [ -179.7771, 66.1 ], [ -179.7833, 66.0979 ], [ -179.7896, 66.1 ], [ -179.7953, 66.0974 ], [ -179.7922, 66.0943 ], [ -179.7974, 66.0922 ], [ -179.7943, 66.0911 ], [ -179.7932, 66.088 ], [ -179.7859, 66.0849 ], [ -179.7953, 66.0818 ], [ -179.788, 66.0807 ], [ -179.787, 66.0755 ], [ -179.7786, 66.0714 ], [ -179.7708, 66.0708 ], [ -179.7609, 66.0661 ], [ -179.7625, 66.0583 ], [ -179.7588, 66.0557 ], [ -179.7604, 66.0521 ], [ -179.7599, 66.0484 ], [ -179.7526, 66.0432 ], [ -179.7578, 66.0411 ], [ -179.7542, 66.0375 ], [ -179.7568, 66.0318 ], [ -179.762, 66.0307 ], [ -179.763, 66.0276 ], [ -179.7682, 66.0266 ], [ -179.7588, 66.0234 ], [ -179.7641, 66.0224 ], [ -179.7672, 66.0172 ], [ -179.7724, 66.0141 ], [ -179.7724, 66.0089 ], [ -179.7693, 66.0068 ], [ -179.7807, 66.0057 ], [ -179.7755, 66.0026 ], [ -179.7797, 65.9922 ], [ -179.7849, 65.9912 ], [ -179.7849, 65.988 ], [ -179.7818, 65.9859 ], [ -179.787, 65.9849 ], [ -179.7891, 65.9797 ], [ -179.7839, 65.9776 ], [ -179.7891, 65.9766 ], [ -179.7912, 65.9745 ], [ -179.7937, 65.9646 ], [ -179.7932, 65.9609 ], [ -179.7901, 65.9589 ], [ -179.7922, 65.9547 ], [ -179.7974, 65.9537 ], [ -179.7995, 65.9474 ], [ -179.7964, 65.9453 ], [ -179.7953, 65.9422 ], [ -179.7922, 65.9411 ], [ -179.7974, 65.9391 ], [ -179.7979, 65.9375 ], [ -179.7943, 65.9339 ], [ -179.7995, 65.9318 ], [ -179.7964, 65.9307 ], [ -179.7953, 65.9276 ], [ -179.7922, 65.9266 ], [ -179.7912, 65.9213 ], [ -179.788, 65.9203 ], [ -179.7932, 65.9182 ], [ -179.7896, 65.9146 ], [ -179.7912, 65.9109 ], [ -179.7859, 65.9099 ], [ -179.787, 65.9068 ], [ -179.7833, 65.9042 ], [ -179.7854, 65.8979 ], [ -179.7812, 65.8938 ], [ -179.7828, 65.8901 ], [ -179.7776, 65.8891 ], [ -179.7828, 65.887 ], [ -179.7797, 65.8839 ], [ -179.7912, 65.8797 ], [ -179.7818, 65.8786 ], [ -179.7734, 65.8745 ], [ -179.7724, 65.8714 ], [ -179.7682, 65.8693 ], [ -179.7609, 65.8682 ], [ -179.7599, 65.863 ], [ -179.7526, 65.8599 ], [ -179.7578, 65.8568 ], [ -179.7505, 65.8536 ], [ -179.7495, 65.8484 ], [ -179.7422, 65.8432 ], [ -179.7432, 65.838 ], [ -179.7401, 65.837 ], [ -179.7391, 65.8339 ], [ -179.7318, 65.8307 ], [ -179.7307, 65.8276 ], [ -179.7234, 65.8245 ], [ -179.7287, 65.8214 ], [ -179.7214, 65.8182 ], [ -179.7208, 65.8167 ], [ -179.7214, 65.813 ], [ -179.7266, 65.812 ], [ -179.7287, 65.8099 ], [ -179.7318, 65.8005 ], [ -179.7396, 65.7979 ], [ -179.7474, 65.7974 ], [ -179.7505, 65.788 ], [ -179.7557, 65.787 ], [ -179.7557, 65.7839 ], [ -179.7375, 65.7896 ], [ -179.7318, 65.787 ], [ -179.7307, 65.7839 ], [ -179.7276, 65.7828 ], [ -179.725, 65.7792 ], [ -179.7042, 65.7792 ], [ -179.7, 65.7813 ], [ -179.6938, 65.7813 ], [ -179.6839, 65.7766 ], [ -179.6849, 65.7734 ], [ -179.6797, 65.7724 ], [ -179.6672, 65.7662 ], [ -179.6662, 65.763 ], [ -179.6422, 65.7516 ], [ -179.6411, 65.7484 ], [ -179.6297, 65.7432 ], [ -179.6286, 65.7401 ], [ -179.5964, 65.7245 ], [ -179.5958, 65.7208 ], [ -179.5974, 65.7172 ], [ -179.5901, 65.7141 ], [ -179.5891, 65.7109 ], [ -179.5771, 65.7104 ], [ -179.5729, 65.7083 ], [ -179.5667, 65.7104 ], [ -179.5609, 65.7078 ], [ -179.5599, 65.7047 ], [ -179.5557, 65.7026 ], [ -179.5484, 65.7016 ], [ -179.5484, 65.6984 ], [ -179.5536, 65.6974 ], [ -179.5547, 65.6943 ], [ -179.5599, 65.6922 ], [ -179.5542, 65.6896 ], [ -179.5479, 65.6917 ], [ -179.5333, 65.6896 ], [ -179.5307, 65.6859 ], [ -179.5224, 65.6818 ], [ -179.5151, 65.6807 ], [ -179.5141, 65.6776 ], [ -179.5068, 65.6734 ], [ -179.525, 65.6729 ], [ -179.5266, 65.6713 ], [ -179.5234, 65.6703 ], [ -179.5224, 65.6672 ], [ -179.5099, 65.6609 ], [ -179.5005, 65.663 ], [ -179.4979, 65.6667 ], [ -179.4812, 65.6646 ], [ -179.4771, 65.6625 ], [ -179.4734, 65.6651 ], [ -179.4724, 65.6703 ], [ -179.4688, 65.6729 ], [ -179.4542, 65.6708 ], [ -179.4443, 65.6661 ], [ -179.4458, 65.6646 ], [ -179.4536, 65.6641 ], [ -179.4557, 65.6589 ], [ -179.4526, 65.6578 ], [ -179.4526, 65.6547 ], [ -179.4667, 65.65 ], [ -179.4745, 65.6495 ], [ -179.4812, 65.6312 ], [ -179.4792, 65.6292 ], [ -179.4625, 65.6312 ], [ -179.4609, 65.6307 ], [ -179.462, 65.6276 ], [ -179.4604, 65.6271 ], [ -179.4463, 65.6276 ], [ -179.4391, 65.6328 ], [ -179.4286, 65.637 ], [ -179.4193, 65.638 ], [ -179.4266, 65.6412 ], [ -179.4151, 65.6443 ], [ -179.4141, 65.6474 ], [ -179.4089, 65.6484 ], [ -179.412, 65.6516 ], [ -179.4062, 65.6542 ], [ -179.3938, 65.6542 ], [ -179.387, 65.6505 ], [ -179.3771, 65.6479 ], [ -179.3667, 65.6479 ], [ -179.3583, 65.6438 ], [ -179.3521, 65.6438 ], [ -179.3479, 65.6417 ], [ -179.3375, 65.6417 ], [ -179.3062, 65.6333 ], [ -179.2812, 65.6312 ], [ -179.2792, 65.6292 ], [ -179.2818, 65.6234 ], [ -179.2833, 65.6229 ], [ -179.2937, 65.6229 ], [ -179.2979, 65.625 ], [ -179.312, 65.6245 ], [ -179.3089, 65.6214 ], [ -179.312, 65.6203 ], [ -179.3141, 65.6172 ], [ -179.3109, 65.6162 ], [ -179.3099, 65.613 ], [ -179.3062, 65.6104 ], [ -179.3089, 65.6026 ], [ -179.3141, 65.6016 ], [ -179.3172, 65.5964 ], [ -179.3286, 65.5932 ], [ -179.3297, 65.588 ], [ -179.3453, 65.587 ], [ -179.3437, 65.5854 ], [ -179.3375, 65.5854 ], [ -179.3318, 65.5828 ], [ -179.3338, 65.5797 ], [ -179.3391, 65.5787 ], [ -179.3401, 65.5755 ], [ -179.3432, 65.5755 ], [ -179.3542, 65.5813 ], [ -179.3682, 65.5807 ], [ -179.3651, 65.5787 ], [ -179.3641, 65.5734 ], [ -179.3542, 65.5708 ], [ -179.3422, 65.5703 ], [ -179.3411, 65.5672 ], [ -179.338, 65.5661 ], [ -179.3401, 65.563 ], [ -179.3521, 65.5625 ], [ -179.3562, 65.5646 ], [ -179.3625, 65.5646 ], [ -179.3771, 65.5604 ], [ -179.3875, 65.5604 ], [ -179.3932, 65.563 ], [ -179.388, 65.5651 ], [ -179.3932, 65.5672 ], [ -179.3922, 65.5724 ], [ -179.3979, 65.575 ], [ -179.4021, 65.5729 ], [ -179.4266, 65.5724 ], [ -179.4266, 65.5672 ], [ -179.4234, 65.5651 ], [ -179.4333, 65.5646 ], [ -179.4359, 65.5609 ], [ -179.4411, 65.5599 ], [ -179.4411, 65.5568 ], [ -179.438, 65.5547 ], [ -179.4432, 65.5537 ], [ -179.4438, 65.5521 ], [ -179.4401, 65.5495 ], [ -179.4391, 65.5463 ], [ -179.4359, 65.5453 ], [ -179.437, 65.5422 ], [ -179.4193, 65.537 ], [ -179.4245, 65.5349 ], [ -179.4245, 65.5318 ], [ -179.4214, 65.5307 ], [ -179.4224, 65.5276 ], [ -179.413, 65.5266 ], [ -179.4182, 65.5234 ], [ -179.4089, 65.5224 ], [ -179.4141, 65.5193 ], [ -179.4026, 65.5141 ], [ -179.4016, 65.5109 ], [ -179.3984, 65.5099 ], [ -179.3995, 65.5047 ], [ -179.3963, 65.5036 ], [ -179.3953, 65.5005 ], [ -179.3797, 65.4953 ], [ -179.3786, 65.4922 ], [ -179.3755, 65.4901 ], [ -179.3776, 65.4838 ], [ -179.3875, 65.4833 ], [ -179.3958, 65.4792 ], [ -179.4036, 65.4786 ], [ -179.4083, 65.475 ], [ -179.4146, 65.475 ], [ -179.4286, 65.4703 ], [ -179.4286, 65.4651 ], [ -179.4255, 65.463 ], [ -179.4307, 65.4609 ], [ -179.4234, 65.4599 ], [ -179.4276, 65.4568 ], [ -179.4458, 65.4583 ], [ -179.4505, 65.4526 ], [ -179.4599, 65.4495 ], [ -179.4661, 65.4453 ], [ -179.463, 65.4422 ], [ -179.4745, 65.4391 ], [ -179.4714, 65.437 ], [ -179.4703, 65.4339 ], [ -179.4672, 65.4328 ], [ -179.4693, 65.4255 ], [ -179.4755, 65.4213 ], [ -179.4807, 65.4203 ], [ -179.4818, 65.4172 ], [ -179.4958, 65.4167 ], [ -179.5036, 65.4141 ], [ -179.5063, 65.4104 ], [ -179.5125, 65.4104 ], [ -179.5167, 65.4125 ], [ -179.5333, 65.4125 ], [ -179.5474, 65.4057 ], [ -179.538, 65.4016 ], [ -179.5417, 65.3979 ], [ -179.5521, 65.3979 ], [ -179.5646, 65.4021 ], [ -179.5708, 65.4021 ], [ -179.5771, 65.4 ], [ -179.5854, 65.4042 ], [ -179.6, 65.4063 ], [ -179.6042, 65.4083 ], [ -179.6083, 65.4063 ], [ -179.6125, 65.4083 ], [ -179.6229, 65.4083 ], [ -179.6271, 65.4104 ], [ -179.6438, 65.4083 ], [ -179.6536, 65.413 ], [ -179.6484, 65.4161 ], [ -179.6604, 65.4167 ], [ -179.6646, 65.4187 ], [ -179.6958, 65.4187 ], [ -179.7104, 65.4146 ], [ -179.7146, 65.4167 ], [ -179.7208, 65.4146 ], [ -179.7375, 65.4146 ], [ -179.7401, 65.4109 ], [ -179.7453, 65.4099 ], [ -179.7479, 65.4063 ], [ -179.7604, 65.4063 ], [ -179.7646, 65.4042 ], [ -179.7812, 65.4042 ], [ -179.7891, 65.4016 ], [ -179.7917, 65.3979 ], [ -179.7979, 65.3979 ], [ -179.8021, 65.4 ], [ -179.8125, 65.4 ], [ -179.8208, 65.4042 ], [ -179.825, 65.4042 ], [ -179.8292, 65.4021 ], [ -179.8458, 65.4021 ], [ -179.85, 65.4 ], [ -179.8583, 65.4 ], [ -179.8661, 65.3953 ], [ -179.8693, 65.3859 ], [ -179.8771, 65.3833 ], [ -179.8979, 65.3833 ], [ -179.9021, 65.3854 ], [ -179.9125, 65.3854 ], [ -179.9167, 65.3833 ], [ -179.9245, 65.3828 ], [ -179.9271, 65.3792 ], [ -179.937, 65.3786 ], [ -179.9297, 65.3745 ], [ -179.9354, 65.3708 ], [ -179.9542, 65.3688 ], [ -179.9583, 65.3667 ], [ -179.9703, 65.3662 ], [ -179.9724, 65.3588 ], [ -179.9651, 65.3578 ], [ -179.9641, 65.3547 ], [ -179.9583, 65.3542 ], [ -179.9568, 65.3526 ], [ -179.9646, 65.3521 ], [ -179.9724, 65.3495 ], [ -179.9734, 65.3443 ], [ -179.9787, 65.3422 ], [ -179.9687, 65.3375 ], [ -179.9526, 65.337 ], [ -179.9542, 65.3354 ], [ -179.9646, 65.3354 ], [ -179.9724, 65.3328 ], [ -179.9755, 65.3255 ], [ -179.9807, 65.3245 ], [ -179.9776, 65.3224 ], [ -179.9766, 65.3193 ], [ -179.9734, 65.3182 ], [ -179.9724, 65.3151 ], [ -179.9693, 65.3141 ], [ -179.9703, 65.3109 ], [ -179.962, 65.3068 ], [ -179.9542, 65.3063 ], [ -179.9474, 65.3026 ], [ -179.9375, 65.3 ], [ -179.9312, 65.3021 ], [ -179.925, 65.3021 ], [ -179.9208, 65.3 ], [ -179.9125, 65.3 ], [ -179.9083, 65.2979 ], [ -179.8979, 65.2979 ], [ -179.8938, 65.2958 ], [ -179.8729, 65.2958 ], [ -179.8547, 65.3005 ], [ -179.8578, 65.3026 ], [ -179.8578, 65.3057 ], [ -179.8464, 65.3089 ], [ -179.8422, 65.3203 ], [ -179.8495, 65.3234 ], [ -179.8505, 65.3266 ], [ -179.8589, 65.3307 ], [ -179.8667, 65.3313 ], [ -179.875, 65.3354 ], [ -179.8792, 65.3333 ], [ -179.8974, 65.3339 ], [ -179.8963, 65.337 ], [ -179.9036, 65.3401 ], [ -179.9089, 65.3474 ], [ -179.9203, 65.3526 ], [ -179.9193, 65.3557 ], [ -179.9411, 65.3609 ], [ -179.9411, 65.3641 ], [ -179.9229, 65.3667 ], [ -179.9089, 65.3714 ], [ -179.9078, 65.3766 ], [ -179.9062, 65.3771 ], [ -179.9, 65.3771 ], [ -179.8958, 65.375 ], [ -179.875, 65.375 ], [ -179.8672, 65.3776 ], [ -179.8661, 65.3807 ], [ -179.8609, 65.3818 ], [ -179.8599, 65.3849 ], [ -179.8547, 65.3859 ], [ -179.8516, 65.3912 ], [ -179.8437, 65.3938 ], [ -179.837, 65.3901 ], [ -179.8292, 65.3896 ], [ -179.8193, 65.3849 ], [ -179.8203, 65.3818 ], [ -179.8188, 65.3812 ], [ -179.7979, 65.3812 ], [ -179.7937, 65.3833 ], [ -179.7859, 65.3839 ], [ -179.7812, 65.3875 ], [ -179.7734, 65.388 ], [ -179.7724, 65.3912 ], [ -179.7583, 65.3958 ], [ -179.7479, 65.3958 ], [ -179.7401, 65.3984 ], [ -179.737, 65.4057 ], [ -179.7354, 65.4063 ], [ -179.7229, 65.4063 ], [ -179.7187, 65.4083 ], [ -179.6917, 65.4083 ], [ -179.6833, 65.4125 ], [ -179.6771, 65.4104 ], [ -179.6729, 65.4125 ], [ -179.6687, 65.4104 ], [ -179.6609, 65.4099 ], [ -179.6568, 65.4078 ], [ -179.6557, 65.4047 ], [ -179.6458, 65.4 ], [ -179.6354, 65.4 ], [ -179.6313, 65.3979 ], [ -179.6208, 65.4021 ], [ -179.6062, 65.4021 ], [ -179.5937, 65.3958 ], [ -179.5875, 65.3979 ], [ -179.5833, 65.3958 ], [ -179.5729, 65.3979 ], [ -179.5542, 65.3896 ], [ -179.5318, 65.3901 ], [ -179.5255, 65.3943 ], [ -179.5255, 65.3974 ], [ -179.5292, 65.4 ], [ -179.5266, 65.4036 ], [ -179.5104, 65.4042 ], [ -179.5078, 65.4005 ], [ -179.5005, 65.3974 ], [ -179.5057, 65.3953 ], [ -179.5057, 65.3922 ], [ -179.5026, 65.3912 ], [ -179.5016, 65.388 ], [ -179.4859, 65.3807 ], [ -179.487, 65.3755 ], [ -179.4797, 65.3724 ], [ -179.4792, 65.3708 ], [ -179.4818, 65.363 ], [ -179.487, 65.362 ], [ -179.4901, 65.3568 ], [ -179.4995, 65.3536 ], [ -179.5026, 65.3484 ], [ -179.5078, 65.3474 ], [ -179.5109, 65.338 ], [ -179.5161, 65.337 ], [ -179.513, 65.3339 ], [ -179.5245, 65.3307 ], [ -179.5213, 65.3287 ], [ -179.5245, 65.3266 ], [ -179.5213, 65.3234 ], [ -179.5234, 65.3172 ], [ -179.5286, 65.3161 ], [ -179.5307, 65.3141 ], [ -179.5307, 65.3109 ], [ -179.5276, 65.3089 ], [ -179.5328, 65.3078 ], [ -179.5333, 65.3042 ], [ -179.5297, 65.3005 ], [ -179.5349, 65.2995 ], [ -179.5312, 65.2958 ], [ -179.5328, 65.2901 ], [ -179.5297, 65.288 ], [ -179.5401, 65.2609 ], [ -179.5453, 65.2599 ], [ -179.55, 65.2479 ], [ -179.5495, 65.2443 ], [ -179.5422, 65.2432 ], [ -179.5443, 65.2338 ], [ -179.5557, 65.2307 ], [ -179.5568, 65.2276 ], [ -179.5682, 65.2245 ], [ -179.5693, 65.2193 ], [ -179.5734, 65.213 ], [ -179.5828, 65.212 ], [ -179.5838, 65.2089 ], [ -179.5891, 65.2078 ], [ -179.5901, 65.2047 ], [ -179.5953, 65.2037 ], [ -179.5984, 65.1984 ], [ -179.6047, 65.1943 ], [ -179.6203, 65.1891 ], [ -179.6214, 65.1859 ], [ -179.6266, 65.1849 ], [ -179.6297, 65.1797 ], [ -179.6349, 65.1787 ], [ -179.6359, 65.1755 ], [ -179.6453, 65.1745 ], [ -179.6484, 65.1693 ], [ -179.6536, 65.1682 ], [ -179.6547, 65.163 ], [ -179.6599, 65.162 ], [ -179.662, 65.1599 ], [ -179.6651, 65.1505 ], [ -179.6703, 65.1495 ], [ -179.6714, 65.1464 ], [ -179.6766, 65.1453 ], [ -179.6729, 65.1417 ], [ -179.6755, 65.138 ], [ -179.6917, 65.1375 ], [ -179.6974, 65.1401 ], [ -179.7, 65.1438 ], [ -179.7042, 65.1417 ], [ -179.7083, 65.1438 ], [ -179.7146, 65.1438 ], [ -179.7187, 65.1417 ], [ -179.7312, 65.1417 ], [ -179.7396, 65.1375 ], [ -179.7521, 65.1375 ], [ -179.7625, 65.1333 ], [ -179.7688, 65.1333 ], [ -179.7807, 65.1286 ], [ -179.7901, 65.1214 ], [ -179.812, 65.1141 ], [ -179.813, 65.1088 ], [ -179.8151, 65.1068 ], [ -179.8245, 65.1036 ], [ -179.8276, 65.0984 ], [ -179.8328, 65.0974 ], [ -179.8338, 65.0943 ], [ -179.8417, 65.0917 ], [ -179.8521, 65.0917 ], [ -179.8562, 65.0938 ], [ -179.8625, 65.0917 ], [ -179.8667, 65.0938 ], [ -179.8807, 65.0891 ], [ -179.8818, 65.0839 ], [ -179.8896, 65.0833 ], [ -179.8974, 65.0807 ], [ -179.9005, 65.0776 ], [ -179.9162, 65.0724 ], [ -179.9172, 65.0693 ], [ -179.9286, 65.0661 ], [ -179.9359, 65.0609 ], [ -179.9438, 65.0604 ], [ -179.9479, 65.0583 ], [ -179.9583, 65.0583 ], [ -179.9729, 65.0542 ], [ -179.9792, 65.0542 ], [ -179.9833, 65.0521 ], [ -179.9958, 65.0521 ], [ -179.9984, 65.0484 ], [ -180.0, 66.0307 ], [ -180.0, 67.9964 ], [ -179.9979, 68.9792 ], [ -179.9734, 68.9766 ], [ -179.9734, 68.9693 ], [ -179.9787, 68.9672 ], [ -179.9667, 68.9625 ], [ -179.9588, 68.962 ], [ -179.9458, 68.9563 ], [ -179.9375, 68.9563 ], [ -179.9318, 68.9537 ], [ -179.937, 68.9505 ], [ -179.9208, 68.95 ], [ -179.9167, 68.9479 ], [ -179.9104, 68.9479 ], [ -179.9047, 68.9453 ], [ -179.9036, 68.9422 ], [ -179.8922, 68.9411 ], [ -179.8932, 68.938 ], [ -179.8854, 68.9354 ], [ -179.8792, 68.9354 ], [ -179.8583, 68.9292 ], [ -179.8484, 68.9287 ], [ -179.8443, 68.9266 ], [ -179.8432, 68.9193 ], [ -179.8338, 68.9161 ], [ -179.8313, 68.9125 ], [ -179.825, 68.9125 ], [ -179.8104, 68.9083 ], [ -179.8021, 68.9083 ], [ -179.7979, 68.9063 ], [ -179.7792, 68.9063 ], [ -179.7667, 68.9104 ], [ -179.7651, 68.9089 ], [ -179.7672, 68.9047 ], [ -179.7771, 68.9042 ], [ -179.7786, 68.9026 ], [ -179.7708, 68.9 ], [ -179.7583, 68.9042 ], [ -179.7521, 68.9021 ], [ -179.7458, 68.9021 ], [ -179.7443, 68.9016 ], [ -179.7432, 68.8964 ], [ -179.7312, 68.8917 ], [ -179.725, 68.8917 ], [ -179.7146, 68.8958 ], [ -179.6938, 68.8958 ], [ -179.6896, 68.8979 ], [ -179.6833, 68.8979 ], [ -179.6792, 68.8958 ], [ -179.6729, 68.8979 ], [ -179.6667, 68.8979 ], [ -179.6625, 68.8958 ], [ -179.6542, 68.8979 ], [ -179.65, 68.8958 ], [ -179.6375, 68.8958 ], [ -179.6333, 68.8938 ], [ -179.6271, 68.8958 ], [ -179.6021, 68.8958 ], [ -179.5979, 68.8979 ], [ -179.5896, 68.8979 ], [ -179.5854, 68.8958 ], [ -179.5813, 68.8979 ], [ -179.5562, 68.8979 ], [ -179.5458, 68.9021 ], [ -179.525, 68.9042 ], [ -179.5208, 68.9063 ], [ -179.5083, 68.9063 ], [ -179.4943, 68.9109 ], [ -179.4911, 68.9141 ], [ -179.4833, 68.9146 ], [ -179.4792, 68.9167 ], [ -179.4708, 68.9167 ], [ -179.4667, 68.9187 ], [ -179.4583, 68.9167 ], [ -179.4542, 68.9187 ], [ -179.4479, 68.9187 ], [ -179.4463, 68.9172 ], [ -179.4516, 68.9161 ], [ -179.4516, 68.913 ], [ -179.4422, 68.9099 ], [ -179.4474, 68.9078 ], [ -179.4474, 68.9026 ], [ -179.4417, 68.9 ], [ -179.4354, 68.9021 ], [ -179.4255, 68.9016 ], [ -179.4307, 68.8984 ], [ -179.4276, 68.8974 ], [ -179.425, 68.8938 ], [ -179.4187, 68.8958 ], [ -179.4109, 68.8953 ], [ -179.4162, 68.8932 ], [ -179.4162, 68.888 ], [ -179.4083, 68.8875 ], [ -179.4068, 68.8859 ], [ -179.4208, 68.8854 ], [ -179.4224, 68.8849 ], [ -179.4224, 68.8818 ], [ -179.4146, 68.8833 ], [ -179.4104, 68.8812 ], [ -179.4042, 68.8812 ], [ -179.3875, 68.875 ], [ -179.3797, 68.8745 ], [ -179.3786, 68.8714 ], [ -179.3537, 68.8609 ], [ -179.3422, 68.8599 ], [ -179.3422, 68.8568 ], [ -179.3437, 68.8562 ], [ -179.3583, 68.8562 ], [ -179.3729, 68.8625 ], [ -179.3792, 68.8625 ], [ -179.387, 68.8599 ], [ -179.3896, 68.8562 ], [ -179.3875, 68.8542 ], [ -179.3813, 68.8542 ], [ -179.3734, 68.8516 ], [ -179.3724, 68.8464 ], [ -179.3693, 68.8453 ], [ -179.3682, 68.8422 ], [ -179.3641, 68.8401 ], [ -179.3526, 68.8391 ], [ -179.3641, 68.8349 ], [ -179.3641, 68.8318 ], [ -179.3609, 68.8307 ], [ -179.3599, 68.8276 ], [ -179.3479, 68.8229 ], [ -179.3417, 68.825 ], [ -179.3391, 68.8214 ], [ -179.3276, 68.8203 ], [ -179.3286, 68.8151 ], [ -179.3234, 68.8141 ], [ -179.325, 68.8104 ], [ -179.3229, 68.8083 ], [ -179.3167, 68.8083 ], [ -179.3125, 68.8063 ], [ -179.2979, 68.8063 ], [ -179.2833, 68.8021 ], [ -179.2708, 68.8021 ], [ -179.2667, 68.8042 ], [ -179.2583, 68.8042 ], [ -179.2542, 68.8063 ], [ -179.2292, 68.8063 ], [ -179.225, 68.8083 ], [ -179.2187, 68.8063 ], [ -179.2125, 68.8063 ], [ -179.2083, 68.8083 ], [ -179.1771, 68.8083 ], [ -179.1745, 68.8047 ], [ -179.1714, 68.8037 ], [ -179.1766, 68.8016 ], [ -179.1766, 68.7984 ], [ -179.1583, 68.7917 ], [ -179.1505, 68.7911 ], [ -179.1479, 68.7875 ], [ -179.1125, 68.7875 ], [ -179.1062, 68.7896 ], [ -179.0917, 68.7854 ], [ -179.0729, 68.7854 ], [ -179.0688, 68.7833 ], [ -179.0396, 68.7833 ], [ -179.0354, 68.7813 ], [ -179.0229, 68.7813 ], [ -179.0188, 68.7792 ], [ -179.0, 68.7792 ], [ -178.9958, 68.7771 ], [ -178.9833, 68.7771 ], [ -178.9687, 68.7708 ], [ -178.9625, 68.7729 ], [ -178.9563, 68.7708 ], [ -178.9438, 68.7708 ], [ -178.9422, 68.7703 ], [ -178.9432, 68.7672 ], [ -178.938, 68.7662 ], [ -178.9432, 68.7641 ], [ -178.9417, 68.7625 ], [ -178.9339, 68.763 ], [ -178.9292, 68.7688 ], [ -178.9167, 68.7688 ], [ -178.9151, 68.7672 ], [ -178.9203, 68.7662 ], [ -178.9203, 68.763 ], [ -178.9062, 68.7625 ], [ -178.9021, 68.7604 ], [ -178.8943, 68.7599 ], [ -178.8995, 68.7578 ], [ -178.8979, 68.7562 ], [ -178.8896, 68.7562 ], [ -178.8833, 68.7583 ], [ -178.8792, 68.7562 ], [ -178.8729, 68.7562 ], [ -178.8687, 68.7542 ], [ -178.8625, 68.7542 ], [ -178.8443, 68.7495 ], [ -178.8432, 68.7464 ], [ -178.8391, 68.7443 ], [ -178.8313, 68.7417 ], [ -178.825, 68.7417 ], [ -178.8026, 68.7328 ], [ -178.8078, 68.7297 ], [ -178.8, 68.7292 ], [ -178.788, 68.7245 ], [ -178.7932, 68.7224 ], [ -178.7932, 68.7193 ], [ -178.7854, 68.7188 ], [ -178.7672, 68.712 ], [ -178.7682, 68.7089 ], [ -178.7588, 68.7057 ], [ -178.7578, 68.7005 ], [ -178.738, 68.6932 ], [ -178.737, 68.6901 ], [ -178.7276, 68.687 ], [ -178.7287, 68.6818 ], [ -178.7193, 68.6787 ], [ -178.7307, 68.6734 ], [ -178.7255, 68.6724 ], [ -178.7266, 68.6693 ], [ -178.7214, 68.6682 ], [ -178.7245, 68.6568 ], [ -178.7193, 68.6557 ], [ -178.7182, 68.6526 ], [ -178.7141, 68.6505 ], [ -178.7088, 68.6495 ], [ -178.7099, 68.6443 ], [ -178.7005, 68.6412 ], [ -178.7042, 68.6396 ], [ -178.7083, 68.6417 ], [ -178.712, 68.6391 ], [ -178.7068, 68.637 ], [ -178.7063, 68.6354 ], [ -178.7083, 68.6312 ], [ -178.7078, 68.6255 ], [ -178.7005, 68.6214 ], [ -178.7057, 68.6203 ], [ -178.7068, 68.6172 ], [ -178.7182, 68.6141 ], [ -178.713, 68.612 ], [ -178.7141, 68.6088 ], [ -178.7088, 68.6078 ], [ -178.7141, 68.6057 ], [ -178.7146, 68.6042 ], [ -178.7109, 68.6016 ], [ -178.7099, 68.5964 ], [ -178.7063, 68.5938 ], [ -178.7068, 68.5922 ], [ -178.712, 68.5911 ], [ -178.7151, 68.5859 ], [ -178.7203, 68.5849 ], [ -178.7208, 68.5833 ], [ -178.7161, 68.5797 ], [ -178.7068, 68.5766 ], [ -178.7057, 68.5734 ], [ -178.6963, 68.5703 ], [ -178.7016, 68.5682 ], [ -178.7016, 68.563 ], [ -178.6938, 68.5646 ], [ -178.6917, 68.5625 ], [ -178.6922, 68.5609 ], [ -178.6974, 68.5599 ], [ -178.6984, 68.5568 ], [ -178.7036, 68.5557 ], [ -178.6963, 68.5516 ], [ -178.6963, 68.5463 ], [ -178.7104, 68.5417 ], [ -178.7187, 68.5417 ], [ -178.7203, 68.5401 ], [ -178.7125, 68.5375 ], [ -178.7063, 68.5375 ], [ -178.7021, 68.5396 ], [ -178.6833, 68.5396 ], [ -178.6687, 68.5333 ], [ -178.65, 68.5333 ], [ -178.6458, 68.5313 ], [ -178.638, 68.5307 ], [ -178.637, 68.5276 ], [ -178.6338, 68.5266 ], [ -178.6313, 68.5229 ], [ -178.625, 68.5229 ], [ -178.6208, 68.5208 ], [ -178.6083, 68.5208 ], [ -178.6042, 68.5188 ], [ -178.5875, 68.5167 ], [ -178.5833, 68.5146 ], [ -178.5771, 68.5146 ], [ -178.5667, 68.5104 ], [ -178.5542, 68.5104 ], [ -178.55, 68.5083 ], [ -178.5271, 68.5083 ], [ -178.5229, 68.5062 ], [ -178.5104, 68.5062 ], [ -178.5063, 68.5042 ], [ -178.5, 68.5062 ], [ -178.4812, 68.5062 ], [ -178.4667, 68.5021 ], [ -178.4375, 68.5 ], [ -178.4292, 68.4958 ], [ -178.4167, 68.4958 ], [ -178.4104, 68.4979 ], [ -178.3932, 68.4901 ], [ -178.3833, 68.4896 ], [ -178.3714, 68.4849 ], [ -178.3828, 68.4807 ], [ -178.3911, 68.4755 ], [ -178.363, 68.4745 ], [ -178.3651, 68.4714 ], [ -178.3729, 68.4708 ], [ -178.3745, 68.4693 ], [ -178.3687, 68.4667 ], [ -178.3609, 68.4682 ], [ -178.3599, 68.463 ], [ -178.3583, 68.4625 ], [ -178.3547, 68.463 ], [ -178.3542, 68.4646 ], [ -178.3547, 68.4682 ], [ -178.3578, 68.4703 ], [ -178.3526, 68.4714 ], [ -178.35, 68.475 ], [ -178.3359, 68.4745 ], [ -178.3359, 68.4714 ], [ -178.3474, 68.4682 ], [ -178.3417, 68.4625 ], [ -178.3292, 68.4625 ], [ -178.3245, 68.4682 ], [ -178.3208, 68.4688 ], [ -178.3193, 68.4682 ], [ -178.3203, 68.4651 ], [ -178.3188, 68.4646 ], [ -178.3125, 68.4667 ], [ -178.3062, 68.4646 ], [ -178.2937, 68.4646 ], [ -178.288, 68.462 ], [ -178.287, 68.4589 ], [ -178.2812, 68.4563 ], [ -178.2776, 68.4568 ], [ -178.2766, 68.4599 ], [ -178.2729, 68.4604 ], [ -178.2688, 68.4583 ], [ -178.2609, 68.4578 ], [ -178.2583, 68.4542 ], [ -178.2542, 68.4563 ], [ -178.2443, 68.4568 ], [ -178.2432, 68.4599 ], [ -178.2333, 68.4604 ], [ -178.2271, 68.4646 ], [ -178.2104, 68.4646 ], [ -178.2026, 68.462 ], [ -178.1974, 68.4547 ], [ -178.188, 68.4537 ], [ -178.1896, 68.4521 ], [ -178.1979, 68.4521 ], [ -178.1995, 68.4505 ], [ -178.1854, 68.45 ], [ -178.1792, 68.4521 ], [ -178.1766, 68.4484 ], [ -178.1672, 68.4474 ], [ -178.1662, 68.4443 ], [ -178.1568, 68.4432 ], [ -178.1526, 68.4411 ], [ -178.1516, 68.438 ], [ -178.1458, 68.4354 ], [ -178.1333, 68.4354 ], [ -178.1187, 68.4292 ], [ -178.1125, 68.4292 ], [ -178.1021, 68.425 ], [ -178.0854, 68.4229 ], [ -178.0813, 68.4208 ], [ -178.0625, 68.4208 ], [ -178.0583, 68.4229 ], [ -178.0542, 68.4208 ], [ -178.0458, 68.4208 ], [ -178.0417, 68.4187 ], [ -178.0188, 68.4167 ], [ -178.0172, 68.4161 ], [ -178.0182, 68.413 ], [ -178.0026, 68.4078 ], [ -178.0042, 68.4042 ], [ -178.0005, 68.4016 ], [ -177.9995, 68.3964 ], [ -177.9901, 68.3932 ], [ -177.9911, 68.3901 ], [ -177.9818, 68.387 ], [ -177.9828, 68.3818 ], [ -177.9776, 68.3807 ], [ -177.9787, 68.3755 ], [ -177.9734, 68.3745 ], [ -177.9776, 68.3588 ], [ -177.9797, 68.3568 ], [ -177.9911, 68.3536 ], [ -177.9859, 68.3505 ], [ -177.988, 68.3484 ], [ -177.9932, 68.3474 ], [ -177.9932, 68.3443 ], [ -177.9859, 68.3391 ], [ -177.9849, 68.3359 ], [ -177.9797, 68.3349 ], [ -177.9807, 68.3318 ], [ -177.9729, 68.3292 ], [ -177.9651, 68.3287 ], [ -177.9661, 68.3234 ], [ -177.9557, 68.3193 ], [ -177.925, 68.3187 ], [ -177.9234, 68.3172 ], [ -177.9286, 68.3161 ], [ -177.9297, 68.313 ], [ -177.9349, 68.312 ], [ -177.938, 68.3089 ], [ -177.9458, 68.3063 ], [ -177.9542, 68.3063 ], [ -177.9745, 68.2995 ], [ -177.9755, 68.2963 ], [ -177.9807, 68.2953 ], [ -177.9839, 68.2859 ], [ -177.9891, 68.2849 ], [ -177.9891, 68.2818 ], [ -177.9859, 68.2797 ], [ -178.0063, 68.2729 ], [ -178.0146, 68.2729 ], [ -178.025, 68.2688 ], [ -178.0328, 68.2682 ], [ -178.0359, 68.263 ], [ -178.0412, 68.262 ], [ -178.038, 68.2599 ], [ -178.038, 68.2547 ], [ -178.0495, 68.2516 ], [ -178.0516, 68.2495 ], [ -178.05, 68.2479 ], [ -178.0437, 68.2479 ], [ -178.0396, 68.2458 ], [ -178.0271, 68.2458 ], [ -178.0229, 68.2438 ], [ -178.0167, 68.2458 ], [ -177.9917, 68.2458 ], [ -177.9812, 68.25 ], [ -177.975, 68.25 ], [ -177.9672, 68.2526 ], [ -177.9625, 68.2562 ], [ -177.9583, 68.2542 ], [ -177.95, 68.2542 ], [ -177.9484, 68.2526 ], [ -177.9563, 68.25 ], [ -177.9661, 68.2495 ], [ -177.9682, 68.2422 ], [ -177.9479, 68.2417 ], [ -177.9339, 68.2464 ], [ -177.9328, 68.2495 ], [ -177.9214, 68.2505 ], [ -177.9307, 68.2547 ], [ -177.9307, 68.2578 ], [ -177.9193, 68.2589 ], [ -177.9208, 68.2604 ], [ -177.9286, 68.2609 ], [ -177.9271, 68.2625 ], [ -177.9187, 68.2625 ], [ -177.9083, 68.2667 ], [ -177.9021, 68.2667 ], [ -177.8979, 68.2688 ], [ -177.8896, 68.2688 ], [ -177.8729, 68.275 ], [ -177.8338, 68.2776 ], [ -177.8432, 68.2818 ], [ -177.838, 68.2839 ], [ -177.8432, 68.287 ], [ -177.8354, 68.2896 ], [ -177.8208, 68.2896 ], [ -177.8167, 68.2917 ], [ -177.8083, 68.2917 ], [ -177.7812, 68.3 ], [ -177.7667, 68.3 ], [ -177.7625, 68.3042 ], [ -177.7479, 68.3042 ], [ -177.7333, 68.3083 ], [ -177.725, 68.3083 ], [ -177.7208, 68.3125 ], [ -177.7021, 68.3125 ], [ -177.6979, 68.3104 ], [ -177.6854, 68.3104 ], [ -177.6812, 68.3083 ], [ -177.6687, 68.3125 ], [ -177.65, 68.3125 ], [ -177.6354, 68.3083 ], [ -177.625, 68.3083 ], [ -177.6234, 68.3068 ], [ -177.6286, 68.3057 ], [ -177.6286, 68.3026 ], [ -177.6255, 68.3016 ], [ -177.6245, 68.2984 ], [ -177.6193, 68.2963 ], [ -177.6266, 68.2922 ], [ -177.6172, 68.2911 ], [ -177.6182, 68.2818 ], [ -177.6151, 68.2807 ], [ -177.6141, 68.2755 ], [ -177.6104, 68.2729 ], [ -177.613, 68.2672 ], [ -177.6208, 68.2646 ], [ -177.65, 68.2646 ], [ -177.6542, 68.2625 ], [ -177.6854, 68.2604 ], [ -177.6958, 68.2562 ], [ -177.7104, 68.2562 ], [ -177.7146, 68.2521 ], [ -177.7229, 68.2521 ], [ -177.7271, 68.25 ], [ -177.7354, 68.25 ], [ -177.7432, 68.2474 ], [ -177.7443, 68.2422 ], [ -177.7495, 68.2401 ], [ -177.7464, 68.2391 ], [ -177.7453, 68.2338 ], [ -177.7437, 68.2333 ], [ -177.7396, 68.2354 ], [ -177.7312, 68.2354 ], [ -177.7234, 68.238 ], [ -177.7234, 68.2412 ], [ -177.7266, 68.2422 ], [ -177.725, 68.2438 ], [ -177.7125, 68.2438 ], [ -177.7083, 68.2458 ], [ -177.7, 68.2458 ], [ -177.6958, 68.2438 ], [ -177.6854, 68.2479 ], [ -177.6818, 68.2474 ], [ -177.6807, 68.2443 ], [ -177.675, 68.2417 ], [ -177.6651, 68.2422 ], [ -177.6703, 68.2453 ], [ -177.6604, 68.2458 ], [ -177.6562, 68.2479 ], [ -177.65, 68.2458 ], [ -177.6396, 68.2458 ], [ -177.6354, 68.2438 ], [ -177.6271, 68.2438 ], [ -177.6229, 68.2458 ], [ -177.6104, 68.2458 ], [ -177.6062, 68.2438 ], [ -177.5984, 68.2432 ], [ -177.5984, 68.2401 ], [ -177.6, 68.2396 ], [ -177.6146, 68.2396 ], [ -177.6187, 68.2375 ], [ -177.6271, 68.2375 ], [ -177.6297, 68.2338 ], [ -177.6562, 68.225 ], [ -177.6703, 68.2245 ], [ -177.6667, 68.2208 ], [ -177.6672, 68.2193 ], [ -177.6724, 68.2182 ], [ -177.6693, 68.2161 ], [ -177.6693, 68.213 ], [ -177.6745, 68.212 ], [ -177.6792, 68.2083 ], [ -177.687, 68.2078 ], [ -177.6839, 68.2057 ], [ -177.6839, 68.2026 ], [ -177.6891, 68.2016 ], [ -177.6911, 68.1984 ], [ -177.6875, 68.1979 ], [ -177.6812, 68.2 ], [ -177.6797, 68.1995 ], [ -177.6807, 68.1943 ], [ -177.6776, 68.1932 ], [ -177.6766, 68.1901 ], [ -177.6625, 68.1896 ], [ -177.6583, 68.1875 ], [ -177.6547, 68.188 ], [ -177.6536, 68.1911 ], [ -177.6396, 68.1958 ], [ -177.6297, 68.1964 ], [ -177.6286, 68.1995 ], [ -177.6234, 68.2005 ], [ -177.6224, 68.2037 ], [ -177.6109, 68.2047 ], [ -177.6083, 68.2083 ], [ -177.6, 68.2083 ], [ -177.5958, 68.2104 ], [ -177.5875, 68.2083 ], [ -177.5797, 68.2109 ], [ -177.5786, 68.2141 ], [ -177.5672, 68.2172 ], [ -177.5672, 68.2203 ], [ -177.5703, 68.2224 ], [ -177.5589, 68.2234 ], [ -177.5641, 68.2266 ], [ -177.5526, 68.2276 ], [ -177.5557, 68.2297 ], [ -177.5542, 68.2312 ], [ -177.5401, 68.2307 ], [ -177.5453, 68.2286 ], [ -177.5474, 68.2266 ], [ -177.5458, 68.225 ], [ -177.5333, 68.2292 ], [ -177.5255, 68.2286 ], [ -177.5307, 68.2266 ], [ -177.5255, 68.2234 ], [ -177.5307, 68.2214 ], [ -177.5229, 68.2208 ], [ -177.5213, 68.2193 ], [ -177.5328, 68.2182 ], [ -177.5312, 68.2167 ], [ -177.5125, 68.2167 ], [ -177.5083, 68.2208 ], [ -177.4943, 68.2182 ], [ -177.5021, 68.2146 ], [ -177.512, 68.2141 ], [ -177.5146, 68.2104 ], [ -177.5245, 68.2099 ], [ -177.5271, 68.2063 ], [ -177.537, 68.2057 ], [ -177.538, 68.2026 ], [ -177.5458, 68.2 ], [ -177.5557, 68.1995 ], [ -177.5568, 68.1964 ], [ -177.562, 68.1953 ], [ -177.5604, 68.1937 ], [ -177.5542, 68.1937 ], [ -177.5417, 68.1979 ], [ -177.5401, 68.1974 ], [ -177.5412, 68.1943 ], [ -177.5359, 68.1932 ], [ -177.537, 68.1901 ], [ -177.5354, 68.1896 ], [ -177.5255, 68.1922 ], [ -177.5224, 68.1974 ], [ -177.5083, 68.2021 ], [ -177.4984, 68.2026 ], [ -177.4974, 68.2057 ], [ -177.4859, 68.2089 ], [ -177.4833, 68.2125 ], [ -177.475, 68.2125 ], [ -177.4583, 68.2188 ], [ -177.4438, 68.2188 ], [ -177.4396, 68.2208 ], [ -177.4167, 68.2208 ], [ -177.4089, 68.2234 ], [ -177.4057, 68.2286 ], [ -177.4005, 68.2297 ], [ -177.3995, 68.2328 ], [ -177.3896, 68.2333 ], [ -177.3854, 68.2354 ], [ -177.3771, 68.2354 ], [ -177.3729, 68.2333 ], [ -177.3667, 68.2396 ], [ -177.3542, 68.2396 ], [ -177.35, 68.2375 ], [ -177.3437, 68.2375 ], [ -177.3422, 68.2391 ], [ -177.3453, 68.2412 ], [ -177.3417, 68.2417 ], [ -177.3271, 68.2375 ], [ -177.3208, 68.2375 ], [ -177.3141, 68.2338 ], [ -177.3062, 68.2333 ], [ -177.2958, 68.2292 ], [ -177.2792, 68.2271 ], [ -177.2521, 68.2146 ], [ -177.2276, 68.2099 ], [ -177.2266, 68.2068 ], [ -177.2172, 68.2057 ], [ -177.213, 68.2037 ], [ -177.212, 68.2005 ], [ -177.2026, 68.1995 ], [ -177.1828, 68.1901 ], [ -177.175, 68.1896 ], [ -177.1667, 68.1854 ], [ -177.1589, 68.1849 ], [ -177.1589, 68.1818 ], [ -177.1625, 68.1813 ], [ -177.1667, 68.1833 ], [ -177.1729, 68.1792 ], [ -177.1896, 68.1792 ], [ -177.1922, 68.1828 ], [ -177.1974, 68.1828 ], [ -177.1995, 68.1734 ], [ -177.1875, 68.1687 ], [ -177.1771, 68.1687 ], [ -177.1755, 68.1672 ], [ -177.1807, 68.1661 ], [ -177.1818, 68.1609 ], [ -177.1839, 68.1589 ], [ -177.1891, 68.1578 ], [ -177.1891, 68.1547 ], [ -177.1859, 68.1526 ], [ -177.1974, 68.1495 ], [ -177.1984, 68.1464 ], [ -177.2036, 68.1453 ], [ -177.2005, 68.1432 ], [ -177.2005, 68.1401 ], [ -177.2057, 68.1391 ], [ -177.2078, 68.1359 ], [ -177.1958, 68.1312 ], [ -177.188, 68.1307 ], [ -177.1839, 68.1286 ], [ -177.1828, 68.1255 ], [ -177.1734, 68.1245 ], [ -177.1708, 68.1208 ], [ -177.1542, 68.1208 ], [ -177.15, 68.1188 ], [ -177.1359, 68.1182 ], [ -177.1318, 68.1151 ], [ -177.137, 68.1141 ], [ -177.137, 68.1109 ], [ -177.1338, 68.1099 ], [ -177.1313, 68.1062 ], [ -177.125, 68.1062 ], [ -177.1208, 68.1042 ], [ -177.1104, 68.1042 ], [ -177.1078, 68.1005 ], [ -177.1, 68.1 ], [ -177.0964, 68.1036 ], [ -177.1042, 68.1042 ], [ -177.112, 68.1068 ], [ -177.1161, 68.1088 ], [ -177.1172, 68.112 ], [ -177.1203, 68.1141 ], [ -177.1125, 68.1167 ], [ -177.0937, 68.1167 ], [ -177.0896, 68.1188 ], [ -177.075, 68.1188 ], [ -177.0646, 68.1229 ], [ -177.0568, 68.1234 ], [ -177.0599, 68.1255 ], [ -177.0583, 68.1271 ], [ -177.0443, 68.1255 ], [ -177.0474, 68.1276 ], [ -177.0458, 68.1292 ], [ -177.0167, 68.1312 ], [ -177.0063, 68.1271 ], [ -176.9875, 68.1333 ], [ -176.9833, 68.1312 ], [ -176.9563, 68.1271 ], [ -176.9479, 68.1229 ], [ -176.9417, 68.1229 ], [ -176.9339, 68.1203 ], [ -176.9229, 68.1146 ], [ -176.9062, 68.1125 ], [ -176.8917, 68.1062 ], [ -176.8839, 68.1057 ], [ -176.8797, 68.1026 ], [ -176.8849, 68.1016 ], [ -176.8859, 68.0964 ], [ -176.8932, 68.0922 ], [ -176.8854, 68.0917 ], [ -176.8813, 68.0896 ], [ -176.8672, 68.0891 ], [ -176.8682, 68.0859 ], [ -176.863, 68.0849 ], [ -176.8641, 68.0818 ], [ -176.8562, 68.0813 ], [ -176.8521, 68.0792 ], [ -176.8437, 68.0792 ], [ -176.8396, 68.0813 ], [ -176.8188, 68.075 ], [ -176.8083, 68.075 ], [ -176.8042, 68.0729 ], [ -176.7979, 68.0729 ], [ -176.7964, 68.0714 ], [ -176.8016, 68.0703 ], [ -176.8016, 68.0672 ], [ -176.7937, 68.0667 ], [ -176.7854, 68.0625 ], [ -176.7776, 68.062 ], [ -176.775, 68.0583 ], [ -176.7583, 68.0563 ], [ -176.7458, 68.05 ], [ -176.738, 68.0495 ], [ -176.737, 68.0463 ], [ -176.7276, 68.0432 ], [ -176.7297, 68.0359 ], [ -176.7318, 68.0339 ], [ -176.737, 68.0328 ], [ -176.737, 68.0297 ], [ -176.7333, 68.0271 ], [ -176.7359, 68.0214 ], [ -176.7563, 68.0146 ], [ -176.7646, 68.0146 ], [ -176.7786, 68.0099 ], [ -176.7797, 68.0068 ], [ -176.7849, 68.0057 ], [ -176.788, 68.0005 ], [ -176.7932, 67.9995 ], [ -176.7849, 67.9943 ], [ -176.7771, 67.9917 ], [ -176.7693, 67.9912 ], [ -176.7609, 67.987 ], [ -176.7609, 67.9838 ], [ -176.8042, 67.9833 ], [ -176.8083, 67.9812 ], [ -176.8208, 67.9812 ], [ -176.825, 67.9833 ], [ -176.875, 67.9833 ], [ -176.8792, 67.9875 ], [ -176.8917, 67.9875 ], [ -176.8958, 67.9854 ], [ -176.9083, 67.9854 ], [ -176.925, 67.9792 ], [ -176.9396, 67.9792 ], [ -176.9411, 67.9776 ], [ -176.9333, 67.975 ], [ -176.9229, 67.975 ], [ -176.9187, 67.9729 ], [ -176.9125, 67.9729 ], [ -176.9083, 67.9688 ], [ -176.8958, 67.9688 ], [ -176.8917, 67.9667 ], [ -176.875, 67.9667 ], [ -176.8687, 67.9688 ], [ -176.8646, 67.9667 ], [ -176.8521, 67.9667 ], [ -176.8479, 67.9646 ], [ -176.8375, 67.9688 ], [ -176.8188, 67.9688 ], [ -176.8146, 67.9708 ], [ -176.8021, 67.9708 ], [ -176.7917, 67.9667 ], [ -176.7839, 67.9661 ], [ -176.7849, 67.963 ], [ -176.7833, 67.9625 ], [ -176.7651, 67.9599 ], [ -176.7661, 67.9547 ], [ -176.7646, 67.9542 ], [ -176.7609, 67.9547 ], [ -176.7583, 67.9583 ], [ -176.7458, 67.9583 ], [ -176.7417, 67.9563 ], [ -176.7354, 67.9563 ], [ -176.725, 67.9521 ], [ -176.7146, 67.9521 ], [ -176.6984, 67.9453 ], [ -176.6995, 67.9401 ], [ -176.6901, 67.937 ], [ -176.6911, 67.9339 ], [ -176.6859, 67.9328 ], [ -176.6875, 67.9292 ], [ -176.6839, 67.9266 ], [ -176.6833, 67.9229 ], [ -176.6854, 67.9187 ], [ -176.6833, 67.9167 ], [ -176.6771, 67.9167 ], [ -176.6672, 67.9213 ], [ -176.6672, 67.9266 ], [ -176.6745, 67.9297 ], [ -176.6745, 67.9328 ], [ -176.6729, 67.9333 ], [ -176.6542, 67.9333 ], [ -176.65, 67.9313 ], [ -176.6438, 67.9313 ], [ -176.6422, 67.9307 ], [ -176.6438, 67.9271 ], [ -176.6417, 67.925 ], [ -176.6229, 67.925 ], [ -176.6214, 67.9255 ], [ -176.6203, 67.9307 ], [ -176.6062, 67.9313 ], [ -176.5984, 67.9287 ], [ -176.5995, 67.9255 ], [ -176.5937, 67.9229 ], [ -176.5875, 67.9229 ], [ -176.5729, 67.9187 ], [ -176.5542, 67.9187 ], [ -176.5401, 67.912 ], [ -176.5396, 67.9083 ], [ -176.5458, 67.8938 ], [ -176.5412, 67.8901 ], [ -176.5271, 67.8896 ], [ -176.5229, 67.8875 ], [ -176.4958, 67.8875 ], [ -176.4854, 67.8833 ], [ -176.4667, 67.8854 ], [ -176.4625, 67.8875 ], [ -176.45, 67.8875 ], [ -176.4458, 67.8896 ], [ -176.4271, 67.8896 ], [ -176.4229, 67.8917 ], [ -176.4146, 67.8917 ], [ -176.4104, 67.8938 ], [ -176.3979, 67.8938 ], [ -176.3938, 67.8917 ], [ -176.3542, 67.8917 ], [ -176.35, 67.8938 ], [ -176.3313, 67.8938 ], [ -176.3271, 67.8958 ], [ -176.3188, 67.8958 ], [ -176.3146, 67.8979 ], [ -176.3083, 67.8979 ], [ -176.3042, 67.8958 ], [ -176.2854, 67.8958 ], [ -176.2812, 67.8979 ], [ -176.275, 67.8979 ], [ -176.2688, 67.8958 ], [ -176.2583, 67.9 ], [ -176.2417, 67.8979 ], [ -176.2375, 67.8958 ], [ -176.2271, 67.8958 ], [ -176.2125, 67.8917 ], [ -176.1938, 67.8917 ], [ -176.1859, 67.8943 ], [ -176.1807, 67.9036 ], [ -176.1729, 67.9042 ], [ -176.1687, 67.9021 ], [ -176.1609, 67.9016 ], [ -176.1542, 67.8979 ], [ -176.1417, 67.8979 ], [ -176.1307, 67.8922 ], [ -176.1146, 67.8875 ], [ -176.0813, 67.8833 ], [ -176.0797, 67.8828 ], [ -176.0807, 67.8797 ], [ -176.0792, 67.8792 ], [ -176.0667, 67.8792 ], [ -176.0583, 67.875 ], [ -176.0521, 67.875 ], [ -176.0375, 67.8708 ], [ -176.025, 67.8708 ], [ -176.0167, 67.8667 ], [ -176.0104, 67.8667 ], [ -176.0063, 67.8646 ], [ -175.9901, 67.862 ], [ -175.9953, 67.8599 ], [ -175.9979, 67.8562 ], [ -176.0333, 67.8562 ], [ -176.0437, 67.8521 ], [ -176.0521, 67.8521 ], [ -176.0724, 67.8453 ], [ -176.0755, 67.838 ], [ -176.0807, 67.837 ], [ -176.0838, 67.8339 ], [ -176.0953, 67.8307 ], [ -176.0984, 67.8255 ], [ -176.1099, 67.8224 ], [ -176.1146, 67.8104 ], [ -176.1125, 67.8083 ], [ -176.1047, 67.8078 ], [ -176.1068, 67.8047 ], [ -176.1208, 67.8 ], [ -176.1286, 67.7995 ], [ -176.1255, 67.7963 ], [ -176.1307, 67.7953 ], [ -176.1271, 67.7917 ], [ -176.1307, 67.7839 ], [ -176.1229, 67.7833 ], [ -176.1208, 67.7813 ], [ -176.1271, 67.7667 ], [ -176.1266, 67.763 ], [ -176.1234, 67.762 ], [ -176.1245, 67.7589 ], [ -176.1062, 67.7562 ], [ -176.0875, 67.7479 ], [ -176.0693, 67.7453 ], [ -176.0703, 67.7401 ], [ -176.0609, 67.7391 ], [ -176.0641, 67.7318 ], [ -176.0589, 67.7307 ], [ -176.0667, 67.7271 ], [ -176.0729, 67.7292 ], [ -176.0854, 67.7292 ], [ -176.0896, 67.7312 ], [ -176.0958, 67.7292 ], [ -176.0974, 67.7297 ], [ -176.0964, 67.7328 ], [ -176.0979, 67.7333 ], [ -176.1167, 67.7333 ], [ -176.1208, 67.7312 ], [ -176.1458, 67.7312 ], [ -176.15, 67.7292 ], [ -176.2125, 67.7292 ], [ -176.2167, 67.7271 ], [ -176.2563, 67.7271 ], [ -176.2604, 67.725 ], [ -176.2771, 67.7271 ], [ -176.2812, 67.7292 ], [ -176.2896, 67.7292 ], [ -176.2937, 67.7271 ], [ -176.2979, 67.7292 ], [ -176.3104, 67.7292 ], [ -176.3146, 67.725 ], [ -176.3104, 67.7208 ], [ -176.313, 67.7151 ], [ -176.3245, 67.712 ], [ -176.3213, 67.7089 ], [ -176.3266, 67.7078 ], [ -176.3276, 67.7047 ], [ -176.3604, 67.7021 ], [ -176.3682, 67.6995 ], [ -176.3714, 67.6943 ], [ -176.3792, 67.6917 ], [ -176.4021, 67.6917 ], [ -176.4062, 67.6937 ], [ -176.4125, 67.6917 ], [ -176.4167, 67.6937 ], [ -176.4354, 67.6937 ], [ -176.437, 67.6922 ], [ -176.4318, 67.6911 ], [ -176.4328, 67.688 ], [ -176.425, 67.6875 ], [ -176.4234, 67.6859 ], [ -176.45, 67.6771 ], [ -176.4625, 67.6771 ], [ -176.4667, 67.6813 ], [ -176.4729, 67.6813 ], [ -176.4771, 67.6792 ], [ -176.4875, 67.6813 ], [ -176.4901, 67.6849 ], [ -176.5088, 67.6932 ], [ -176.5271, 67.6958 ], [ -176.5312, 67.6979 ], [ -176.5417, 67.6979 ], [ -176.5458, 67.7 ], [ -176.5792, 67.7 ], [ -176.5833, 67.7021 ], [ -176.5958, 67.7021 ], [ -176.6, 67.7042 ], [ -176.6078, 67.7047 ], [ -176.6068, 67.7078 ], [ -176.612, 67.7089 ], [ -176.6089, 67.7161 ], [ -176.6208, 67.7208 ], [ -176.6286, 67.7214 ], [ -176.6266, 67.7245 ], [ -176.6151, 67.7286 ], [ -176.637, 67.7276 ], [ -176.6349, 67.7307 ], [ -176.6255, 67.7318 ], [ -176.6234, 67.7338 ], [ -176.625, 67.7354 ], [ -176.6479, 67.7354 ], [ -176.6521, 67.7375 ], [ -176.6583, 67.7375 ], [ -176.6599, 67.7359 ], [ -176.6526, 67.7349 ], [ -176.6516, 67.7318 ], [ -176.6422, 67.7307 ], [ -176.6438, 67.7292 ], [ -176.6521, 67.7292 ], [ -176.6562, 67.7271 ], [ -176.6604, 67.7292 ], [ -176.6667, 67.7271 ], [ -176.6729, 67.7271 ], [ -176.6792, 67.7292 ], [ -176.6854, 67.7271 ], [ -176.6896, 67.7292 ], [ -176.7, 67.7292 ], [ -176.7042, 67.7312 ], [ -176.7333, 67.7312 ], [ -176.7375, 67.7333 ], [ -176.7682, 67.7307 ], [ -176.7667, 67.7292 ], [ -176.75, 67.7292 ], [ -176.7422, 67.7266 ], [ -176.7432, 67.7234 ], [ -176.7417, 67.7229 ], [ -176.6854, 67.7229 ], [ -176.6771, 67.7188 ], [ -176.6693, 67.7203 ], [ -176.6667, 67.7167 ], [ -176.6604, 67.7188 ], [ -176.6562, 67.7167 ], [ -176.65, 67.7167 ], [ -176.6474, 67.713 ], [ -176.6338, 67.7078 ], [ -176.6391, 67.7047 ], [ -176.6255, 67.7037 ], [ -176.6255, 67.7005 ], [ -176.6307, 67.6995 ], [ -176.6292, 67.6979 ], [ -176.5958, 67.6979 ], [ -176.5875, 67.6937 ], [ -176.5646, 67.6937 ], [ -176.5604, 67.6958 ], [ -176.5417, 67.6958 ], [ -176.5333, 67.6917 ], [ -176.5229, 67.6917 ], [ -176.5203, 67.688 ], [ -176.5109, 67.687 ], [ -176.512, 67.6818 ], [ -176.5026, 67.6787 ], [ -176.5036, 67.6734 ], [ -176.4911, 67.6672 ], [ -176.4818, 67.6661 ], [ -176.4792, 67.6625 ], [ -176.4438, 67.6646 ], [ -176.4359, 67.6672 ], [ -176.4349, 67.6703 ], [ -176.4208, 67.675 ], [ -176.3687, 67.675 ], [ -176.3646, 67.6771 ], [ -176.3458, 67.6771 ], [ -176.3354, 67.6813 ], [ -176.3229, 67.6813 ], [ -176.3182, 67.6849 ], [ -176.3042, 67.6896 ], [ -176.2854, 67.6917 ], [ -176.2839, 67.6901 ], [ -176.2875, 67.6813 ], [ -176.2839, 67.6776 ], [ -176.2891, 67.6755 ], [ -176.2672, 67.6661 ], [ -176.2661, 67.663 ], [ -176.2521, 67.6563 ], [ -176.2458, 67.6563 ], [ -176.2375, 67.6521 ], [ -176.2297, 67.6516 ], [ -176.2255, 67.6495 ], [ -176.2229, 67.6458 ], [ -176.2125, 67.6458 ], [ -176.2083, 67.6417 ], [ -176.1943, 67.6412 ], [ -176.1859, 67.637 ], [ -176.1911, 67.6339 ], [ -176.1792, 67.6333 ], [ -176.1687, 67.6292 ], [ -176.1625, 67.6312 ], [ -176.1583, 67.6292 ], [ -176.1083, 67.6292 ], [ -176.1042, 67.6271 ], [ -176.1, 67.6292 ], [ -176.0917, 67.6292 ], [ -176.0875, 67.6271 ], [ -176.0604, 67.6292 ], [ -176.0542, 67.6271 ], [ -176.05, 67.6292 ], [ -176.0042, 67.6292 ], [ -176.0, 67.6312 ], [ -175.9917, 67.6312 ], [ -175.9875, 67.6333 ], [ -175.9583, 67.6354 ], [ -175.9542, 67.6375 ], [ -175.9479, 67.6354 ], [ -175.9417, 67.6375 ], [ -175.9312, 67.6375 ], [ -175.9271, 67.6354 ], [ -175.9083, 67.6354 ], [ -175.8958, 67.6312 ], [ -175.888, 67.6307 ], [ -175.888, 67.6276 ], [ -175.8901, 67.6255 ], [ -175.8953, 67.6245 ], [ -175.8958, 67.6229 ], [ -175.888, 67.6151 ], [ -175.8901, 67.6109 ], [ -175.8953, 67.6099 ], [ -175.8984, 67.6047 ], [ -175.9099, 67.6036 ], [ -175.9109, 67.5984 ], [ -175.9162, 67.5974 ], [ -175.913, 67.5943 ], [ -175.9151, 67.5922 ], [ -175.9203, 67.5911 ], [ -175.9245, 67.5797 ], [ -175.9167, 67.5792 ], [ -175.9125, 67.5771 ], [ -175.9021, 67.5771 ], [ -175.8995, 67.5734 ], [ -175.8958, 67.5729 ], [ -175.8938, 67.575 ], [ -175.8974, 67.5776 ], [ -175.8974, 67.5807 ], [ -175.8813, 67.5813 ], [ -175.8771, 67.5833 ], [ -175.85, 67.5813 ], [ -175.8354, 67.5771 ], [ -175.8125, 67.5771 ], [ -175.7958, 67.5833 ], [ -175.7896, 67.5813 ], [ -175.7688, 67.5813 ], [ -175.7672, 67.5807 ], [ -175.7672, 67.5776 ], [ -175.7724, 67.5755 ], [ -175.7693, 67.5745 ], [ -175.7667, 67.5708 ], [ -175.7625, 67.5708 ], [ -175.7583, 67.5687 ], [ -175.75, 67.5708 ], [ -175.7484, 67.5703 ], [ -175.7495, 67.5672 ], [ -175.7479, 67.5667 ], [ -175.7208, 67.5646 ], [ -175.7167, 67.5625 ], [ -175.7088, 67.562 ], [ -175.7099, 67.5589 ], [ -175.7083, 67.5583 ], [ -175.6958, 67.5583 ], [ -175.6917, 67.5563 ], [ -175.6812, 67.5563 ], [ -175.6771, 67.5542 ], [ -175.6693, 67.5537 ], [ -175.6693, 67.5505 ], [ -175.6714, 67.5484 ], [ -175.6766, 67.5474 ], [ -175.6807, 67.538 ], [ -175.6755, 67.537 ], [ -175.6766, 67.5318 ], [ -175.6708, 67.5292 ], [ -175.663, 67.5286 ], [ -175.6641, 67.5255 ], [ -175.6609, 67.5245 ], [ -175.6599, 67.5214 ], [ -175.6542, 67.5188 ], [ -175.625, 67.5188 ], [ -175.6208, 67.5208 ], [ -175.6146, 67.5208 ], [ -175.6062, 67.5167 ], [ -175.6, 67.5188 ], [ -175.5854, 67.5146 ], [ -175.5625, 67.5146 ], [ -175.5568, 67.512 ], [ -175.5557, 67.5089 ], [ -175.5458, 67.5062 ], [ -175.5375, 67.5062 ], [ -175.5333, 67.5083 ], [ -175.5167, 67.5083 ], [ -175.5151, 67.5068 ], [ -175.5203, 67.5047 ], [ -175.5125, 67.5042 ], [ -175.5016, 67.4984 ], [ -175.4937, 67.4979 ], [ -175.4896, 67.4958 ], [ -175.4729, 67.4958 ], [ -175.4625, 67.5 ], [ -175.4542, 67.5 ], [ -175.4438, 67.5042 ], [ -175.4375, 67.5021 ], [ -175.4297, 67.5026 ], [ -175.4354, 67.5062 ], [ -175.4479, 67.5062 ], [ -175.4521, 67.5083 ], [ -175.4563, 67.5062 ], [ -175.487, 67.5047 ], [ -175.488, 67.5078 ], [ -175.4911, 67.5099 ], [ -175.4812, 67.5104 ], [ -175.4787, 67.5141 ], [ -175.4734, 67.5151 ], [ -175.475, 67.5167 ], [ -175.4812, 67.5167 ], [ -175.487, 67.5193 ], [ -175.4818, 67.5214 ], [ -175.4833, 67.5229 ], [ -175.4911, 67.5224 ], [ -175.4943, 67.5151 ], [ -175.4979, 67.5125 ], [ -175.5099, 67.5172 ], [ -175.5078, 67.5203 ], [ -175.4937, 67.525 ], [ -175.4854, 67.525 ], [ -175.475, 67.5292 ], [ -175.4687, 67.5292 ], [ -175.4583, 67.525 ], [ -175.4563, 67.5271 ], [ -175.4599, 67.5297 ], [ -175.4599, 67.5328 ], [ -175.4438, 67.5333 ], [ -175.4422, 67.5339 ], [ -175.4401, 67.5391 ], [ -175.4625, 67.5458 ], [ -175.4687, 67.5437 ], [ -175.4729, 67.5458 ], [ -175.4958, 67.5479 ], [ -175.5099, 67.5432 ], [ -175.512, 67.538 ], [ -175.5068, 67.5359 ], [ -175.5109, 67.5297 ], [ -175.5354, 67.5292 ], [ -175.5432, 67.5266 ], [ -175.5464, 67.5234 ], [ -175.5516, 67.5224 ], [ -175.5526, 67.5193 ], [ -175.5604, 67.5167 ], [ -175.5708, 67.5208 ], [ -175.5771, 67.5208 ], [ -175.5813, 67.5229 ], [ -175.5891, 67.5234 ], [ -175.5958, 67.5271 ], [ -175.6021, 67.5271 ], [ -175.6104, 67.5313 ], [ -175.6167, 67.5313 ], [ -175.6208, 67.5292 ], [ -175.6349, 67.5297 ], [ -175.6328, 67.5328 ], [ -175.6276, 67.5339 ], [ -175.6245, 67.5411 ], [ -175.613, 67.5422 ], [ -175.612, 67.5453 ], [ -175.6005, 67.5484 ], [ -175.5984, 67.5516 ], [ -175.6037, 67.5526 ], [ -175.6026, 67.5557 ], [ -175.612, 67.5568 ], [ -175.6109, 67.5599 ], [ -175.6161, 67.5609 ], [ -175.6271, 67.5667 ], [ -175.6812, 67.5667 ], [ -175.6854, 67.5687 ], [ -175.6979, 67.5687 ], [ -175.7021, 67.5708 ], [ -175.7083, 67.5708 ], [ -175.7141, 67.5734 ], [ -175.712, 67.5766 ], [ -175.688, 67.5839 ], [ -175.687, 67.587 ], [ -175.6755, 67.588 ], [ -175.6724, 67.5932 ], [ -175.6672, 67.5943 ], [ -175.663, 67.6036 ], [ -175.6682, 67.6047 ], [ -175.6724, 67.6078 ], [ -175.6672, 67.6088 ], [ -175.663, 67.6162 ], [ -175.6682, 67.6172 ], [ -175.6792, 67.6229 ], [ -175.6854, 67.6229 ], [ -175.6896, 67.625 ], [ -175.7063, 67.625 ], [ -175.7104, 67.6271 ], [ -175.7182, 67.6276 ], [ -175.7172, 67.6307 ], [ -175.7224, 67.6328 ], [ -175.7167, 67.6333 ], [ -175.7151, 67.6349 ], [ -175.7245, 67.638 ], [ -175.7234, 67.6412 ], [ -175.7287, 67.6422 ], [ -175.7276, 67.6453 ], [ -175.7292, 67.6458 ], [ -175.7563, 67.65 ], [ -175.7604, 67.6521 ], [ -175.7937, 67.6521 ], [ -175.7979, 67.6542 ], [ -175.8099, 67.6547 ], [ -175.8078, 67.662 ], [ -175.8026, 67.663 ], [ -175.8042, 67.6646 ], [ -175.8167, 67.6646 ], [ -175.837, 67.6578 ], [ -175.8338, 67.6547 ], [ -175.8417, 67.6521 ], [ -175.8495, 67.6516 ], [ -175.8526, 67.6484 ], [ -175.8604, 67.6458 ], [ -175.8729, 67.6458 ], [ -175.8807, 67.6432 ], [ -175.8838, 67.6401 ], [ -175.8979, 67.6375 ], [ -175.9, 67.6396 ], [ -175.8974, 67.6453 ], [ -175.8922, 67.6464 ], [ -175.8901, 67.6484 ], [ -175.8932, 67.6516 ], [ -175.888, 67.6526 ], [ -175.887, 67.6557 ], [ -175.8818, 67.6568 ], [ -175.8849, 67.6599 ], [ -175.8797, 67.6609 ], [ -175.8828, 67.6641 ], [ -175.8776, 67.6651 ], [ -175.8813, 67.6687 ], [ -175.8786, 67.6766 ], [ -175.8734, 67.6776 ], [ -175.8682, 67.6849 ], [ -175.863, 67.6859 ], [ -175.8547, 67.7068 ], [ -175.8589, 67.7099 ], [ -175.8682, 67.713 ], [ -175.8672, 67.7161 ], [ -175.8766, 67.7193 ], [ -175.8714, 67.7214 ], [ -175.8693, 67.7245 ], [ -175.8771, 67.725 ], [ -175.8813, 67.7271 ], [ -175.9, 67.7271 ], [ -175.9042, 67.7229 ], [ -175.9104, 67.7229 ], [ -175.9146, 67.725 ], [ -175.9271, 67.725 ], [ -175.9312, 67.7271 ], [ -175.9375, 67.725 ], [ -175.9417, 67.7271 ], [ -175.9937, 67.725 ], [ -175.9979, 67.7271 ], [ -176.0042, 67.725 ], [ -176.0099, 67.7276 ], [ -176.0099, 67.7307 ], [ -175.9771, 67.7333 ], [ -175.9729, 67.7354 ], [ -175.9646, 67.7354 ], [ -175.9604, 67.7375 ], [ -175.9187, 67.7396 ], [ -175.9083, 67.7438 ], [ -175.9021, 67.7438 ], [ -175.8943, 67.7464 ], [ -175.8974, 67.7484 ], [ -175.8953, 67.7516 ], [ -175.8875, 67.7521 ], [ -175.8813, 67.7562 ], [ -175.8729, 67.7562 ], [ -175.8526, 67.763 ], [ -175.8495, 67.7682 ], [ -175.8443, 67.7693 ], [ -175.8432, 67.7745 ], [ -175.8396, 67.7771 ], [ -175.8333, 67.775 ], [ -175.8104, 67.775 ], [ -175.8062, 67.7771 ], [ -175.8, 67.775 ], [ -175.7958, 67.7771 ], [ -175.7833, 67.7771 ], [ -175.7792, 67.7792 ], [ -175.7568, 67.7818 ], [ -175.7536, 67.7911 ], [ -175.75, 67.7917 ], [ -175.7417, 67.7875 ], [ -175.7339, 67.787 ], [ -175.7328, 67.7839 ], [ -175.725, 67.7833 ], [ -175.7167, 67.7792 ], [ -175.7104, 67.7792 ], [ -175.7005, 67.7745 ], [ -175.7005, 67.7714 ], [ -175.7083, 67.7688 ], [ -175.7182, 67.7693 ], [ -175.7172, 67.7724 ], [ -175.7208, 67.7729 ], [ -175.7234, 67.7693 ], [ -175.7349, 67.7662 ], [ -175.7318, 67.763 ], [ -175.737, 67.762 ], [ -175.7391, 67.7589 ], [ -175.7312, 67.7562 ], [ -175.6938, 67.7562 ], [ -175.6818, 67.7516 ], [ -175.6828, 67.7484 ], [ -175.6786, 67.7464 ], [ -175.6646, 67.7458 ], [ -175.6599, 67.7495 ], [ -175.6547, 67.7505 ], [ -175.6547, 67.7536 ], [ -175.6708, 67.7583 ], [ -175.6812, 67.7583 ], [ -175.6833, 67.7604 ], [ -175.6807, 67.7682 ], [ -175.6667, 67.7667 ], [ -175.6479, 67.7583 ], [ -175.6375, 67.7583 ], [ -175.6229, 67.7521 ], [ -175.6187, 67.7521 ], [ -175.5937, 67.7438 ], [ -175.5875, 67.7438 ], [ -175.5859, 67.7432 ], [ -175.587, 67.7401 ], [ -175.5854, 67.7396 ], [ -175.563, 67.737 ], [ -175.5625, 67.7333 ], [ -175.5646, 67.7312 ], [ -175.5729, 67.7312 ], [ -175.5792, 67.7292 ], [ -175.5875, 67.7333 ], [ -175.5937, 67.7333 ], [ -175.5974, 67.7307 ], [ -175.5958, 67.7292 ], [ -175.5776, 67.7266 ], [ -175.575, 67.7229 ], [ -175.5521, 67.725 ], [ -175.5484, 67.7276 ], [ -175.5474, 67.7307 ], [ -175.5437, 67.7312 ], [ -175.5354, 67.7271 ], [ -175.513, 67.7245 ], [ -175.5182, 67.7214 ], [ -175.5151, 67.7203 ], [ -175.5141, 67.7172 ], [ -175.5083, 67.7146 ], [ -175.4958, 67.7146 ], [ -175.4917, 67.7125 ], [ -175.475, 67.7125 ], [ -175.4563, 67.7063 ], [ -175.4458, 67.7063 ], [ -175.4312, 67.7 ], [ -175.4271, 67.7 ], [ -175.4167, 67.6958 ], [ -175.4125, 67.6958 ], [ -175.3875, 67.6875 ], [ -175.3833, 67.6875 ], [ -175.3687, 67.6813 ], [ -175.3583, 67.6813 ], [ -175.3453, 67.6755 ], [ -175.3396, 67.675 ], [ -175.3208, 67.6687 ], [ -175.3125, 67.6687 ], [ -175.3083, 67.6708 ], [ -175.3042, 67.6687 ], [ -175.2875, 67.6687 ], [ -175.2797, 67.6661 ], [ -175.2807, 67.663 ], [ -175.2766, 67.6609 ], [ -175.2646, 67.6563 ], [ -175.2568, 67.6557 ], [ -175.2588, 67.6505 ], [ -175.2604, 67.65 ], [ -175.2688, 67.65 ], [ -175.2729, 67.6479 ], [ -175.2979, 67.6479 ], [ -175.3057, 67.6453 ], [ -175.3068, 67.6422 ], [ -175.312, 67.6412 ], [ -175.3146, 67.6375 ], [ -175.3109, 67.6349 ], [ -175.312, 67.6297 ], [ -175.3083, 67.6271 ], [ -175.3099, 67.6234 ], [ -175.3005, 67.6224 ], [ -175.3016, 67.6172 ], [ -175.2979, 67.6146 ], [ -175.2995, 67.6088 ], [ -175.2922, 67.6057 ], [ -175.2912, 67.6026 ], [ -175.2776, 67.5974 ], [ -175.2786, 67.5943 ], [ -175.2713, 67.5911 ], [ -175.2703, 67.588 ], [ -175.2568, 67.587 ], [ -175.2568, 67.5839 ], [ -175.262, 67.5818 ], [ -175.25, 67.5813 ], [ -175.2443, 67.5787 ], [ -175.2432, 67.5755 ], [ -175.2396, 67.5729 ], [ -175.2422, 67.5672 ], [ -175.2443, 67.5651 ], [ -175.2521, 67.5646 ], [ -175.2536, 67.563 ], [ -175.2464, 67.5599 ], [ -175.2453, 67.5568 ], [ -175.2422, 67.5557 ], [ -175.2432, 67.5526 ], [ -175.2354, 67.5521 ], [ -175.2328, 67.5484 ], [ -175.2193, 67.5453 ], [ -175.2203, 67.5422 ], [ -175.2172, 67.5401 ], [ -175.2224, 67.5391 ], [ -175.2224, 67.5359 ], [ -175.2146, 67.5354 ], [ -175.2083, 67.5375 ], [ -175.2, 67.5333 ], [ -175.1938, 67.5333 ], [ -175.1922, 67.5328 ], [ -175.1932, 67.5297 ], [ -175.1891, 67.5276 ], [ -175.1797, 67.5266 ], [ -175.1662, 67.5193 ], [ -175.1609, 67.5182 ], [ -175.1646, 67.5146 ], [ -175.1687, 67.5167 ], [ -175.1812, 67.5167 ], [ -175.1875, 67.5146 ], [ -175.1891, 67.5151 ], [ -175.188, 67.5182 ], [ -175.1974, 67.5193 ], [ -175.1958, 67.5208 ], [ -175.188, 67.5214 ], [ -175.1922, 67.5245 ], [ -175.2104, 67.525 ], [ -175.2141, 67.5224 ], [ -175.2167, 67.5167 ], [ -175.2146, 67.5146 ], [ -175.2068, 67.5141 ], [ -175.212, 67.512 ], [ -175.2141, 67.5089 ], [ -175.1938, 67.5 ], [ -175.1859, 67.4995 ], [ -175.1922, 67.4859 ], [ -175.1974, 67.4849 ], [ -175.1995, 67.4828 ], [ -175.1943, 67.4807 ], [ -175.1958, 67.4771 ], [ -175.1922, 67.4745 ], [ -175.1932, 67.4714 ], [ -175.1901, 67.4703 ], [ -175.1891, 67.4672 ], [ -175.1854, 67.4646 ], [ -175.188, 67.4589 ], [ -175.1932, 67.4578 ], [ -175.1953, 67.4547 ], [ -175.1922, 67.4537 ], [ -175.1911, 67.4484 ], [ -175.188, 67.4474 ], [ -175.1891, 67.4443 ], [ -175.1859, 67.4432 ], [ -175.1849, 67.4401 ], [ -175.1776, 67.437 ], [ -175.1766, 67.4339 ], [ -175.1734, 67.4318 ], [ -175.1771, 67.4292 ], [ -175.1833, 67.4292 ], [ -175.1875, 67.4271 ], [ -175.2021, 67.4271 ], [ -175.2063, 67.425 ], [ -175.2125, 67.425 ], [ -175.2167, 67.4271 ], [ -175.2245, 67.4266 ], [ -175.2276, 67.4193 ], [ -175.2328, 67.4182 ], [ -175.2391, 67.4078 ], [ -175.2359, 67.4057 ], [ -175.2354, 67.4021 ], [ -175.2401, 67.3943 ], [ -175.2453, 67.3932 ], [ -175.2479, 67.3875 ], [ -175.2443, 67.3839 ], [ -175.2484, 67.3776 ], [ -175.2563, 67.3771 ], [ -175.2667, 67.3729 ], [ -175.2766, 67.3724 ], [ -175.2776, 67.3693 ], [ -175.2912, 67.3641 ], [ -175.2943, 67.3588 ], [ -175.2995, 67.3578 ], [ -175.3005, 67.3547 ], [ -175.3057, 67.3536 ], [ -175.3089, 67.3484 ], [ -175.3203, 67.3453 ], [ -175.3234, 67.3422 ], [ -175.3286, 67.3411 ], [ -175.3297, 67.338 ], [ -175.3411, 67.337 ], [ -175.338, 67.3339 ], [ -175.3432, 67.3328 ], [ -175.3417, 67.3313 ], [ -175.3338, 67.3318 ], [ -175.3307, 67.3349 ], [ -175.3229, 67.3375 ], [ -175.3062, 67.3375 ], [ -175.3047, 67.3359 ], [ -175.3161, 67.3328 ], [ -175.3172, 67.3297 ], [ -175.325, 67.3271 ], [ -175.3333, 67.3271 ], [ -175.3474, 67.3224 ], [ -175.3495, 67.3203 ], [ -175.3547, 67.3089 ], [ -175.3599, 67.3078 ], [ -175.3604, 67.3063 ], [ -175.3599, 67.3047 ], [ -175.3484, 67.2995 ], [ -175.3495, 67.2963 ], [ -175.3443, 67.2953 ], [ -175.3453, 67.2922 ], [ -175.3313, 67.2917 ], [ -175.3297, 67.2932 ], [ -175.3328, 67.2953 ], [ -175.325, 67.2958 ], [ -175.3214, 67.2984 ], [ -175.325, 67.3 ], [ -175.3292, 67.2979 ], [ -175.3354, 67.2979 ], [ -175.337, 67.2984 ], [ -175.3359, 67.3016 ], [ -175.3411, 67.3026 ], [ -175.3359, 67.313 ], [ -175.3391, 67.3161 ], [ -175.3338, 67.3172 ], [ -175.3391, 67.3203 ], [ -175.3229, 67.3229 ], [ -175.3167, 67.3271 ], [ -175.3, 67.325 ], [ -175.2953, 67.3307 ], [ -175.2875, 67.3333 ], [ -175.275, 67.3333 ], [ -175.2708, 67.3313 ], [ -175.2667, 67.3333 ], [ -175.2568, 67.3339 ], [ -175.2599, 67.3359 ], [ -175.2609, 67.3391 ], [ -175.2641, 67.3401 ], [ -175.2625, 67.3417 ], [ -175.2563, 67.3396 ], [ -175.2521, 67.3417 ], [ -175.2437, 67.3417 ], [ -175.2396, 67.3396 ], [ -175.2187, 67.3396 ], [ -175.2146, 67.3375 ], [ -175.2083, 67.3375 ], [ -175.2021, 67.3396 ], [ -175.2005, 67.3391 ], [ -175.2016, 67.3359 ], [ -175.2, 67.3354 ], [ -175.1812, 67.3375 ], [ -175.1755, 67.3339 ], [ -175.1807, 67.3328 ], [ -175.1828, 67.3307 ], [ -175.1828, 67.3276 ], [ -175.1583, 67.3271 ], [ -175.1536, 67.3328 ], [ -175.1422, 67.3339 ], [ -175.1516, 67.338 ], [ -175.15, 67.3396 ], [ -175.1417, 67.3396 ], [ -175.1375, 67.3417 ], [ -175.1292, 67.3417 ], [ -175.125, 67.3438 ], [ -175.1083, 67.3438 ], [ -175.1042, 67.3458 ], [ -175.0958, 67.3458 ], [ -175.0917, 67.3479 ], [ -175.0792, 67.3479 ], [ -175.075, 67.35 ], [ -175.0651, 67.3505 ], [ -175.0625, 67.3542 ], [ -175.0542, 67.3542 ], [ -175.0464, 67.3568 ], [ -175.0412, 67.3662 ], [ -175.0276, 67.3714 ], [ -175.0245, 67.3766 ], [ -175.0193, 67.3776 ], [ -175.0161, 67.3828 ], [ -175.0109, 67.3839 ], [ -175.0109, 67.387 ], [ -175.0141, 67.388 ], [ -175.0141, 67.3912 ], [ -175.0026, 67.3922 ], [ -175.0057, 67.3953 ], [ -174.9943, 67.3984 ], [ -174.9911, 67.4016 ], [ -174.9859, 67.4026 ], [ -174.9849, 67.4057 ], [ -174.9714, 67.4109 ], [ -174.9687, 67.4167 ], [ -174.9724, 67.4203 ], [ -174.9672, 67.4213 ], [ -174.9651, 67.4234 ], [ -174.9625, 67.4292 ], [ -174.9667, 67.4333 ], [ -174.9641, 67.4391 ], [ -174.9625, 67.4396 ], [ -174.9588, 67.4391 ], [ -174.9599, 67.4359 ], [ -174.9521, 67.4354 ], [ -174.9438, 67.4313 ], [ -174.9375, 67.4313 ], [ -174.9292, 67.4271 ], [ -174.9151, 67.4266 ], [ -174.9182, 67.4193 ], [ -174.9104, 67.4187 ], [ -174.9089, 67.4172 ], [ -174.9141, 67.4161 ], [ -174.9162, 67.4141 ], [ -174.9214, 67.4026 ], [ -174.9266, 67.4016 ], [ -174.9234, 67.3995 ], [ -174.9234, 67.3964 ], [ -174.9292, 67.3958 ], [ -174.9307, 67.3943 ], [ -174.9214, 67.3932 ], [ -174.9245, 67.3839 ], [ -174.9109, 67.3786 ], [ -174.912, 67.3734 ], [ -174.9042, 67.3729 ], [ -174.9016, 67.3766 ], [ -174.8917, 67.3771 ], [ -174.8875, 67.3792 ], [ -174.8813, 67.3771 ], [ -174.875, 67.3792 ], [ -174.8708, 67.3771 ], [ -174.8568, 67.3807 ], [ -174.8526, 67.3786 ], [ -174.8516, 67.3755 ], [ -174.8443, 67.3724 ], [ -174.8453, 67.3672 ], [ -174.8417, 67.3646 ], [ -174.8422, 67.363 ], [ -174.8537, 67.3588 ], [ -174.8297, 67.3578 ], [ -174.8307, 67.3526 ], [ -174.8208, 67.3479 ], [ -174.8083, 67.3479 ], [ -174.8042, 67.3458 ], [ -174.8, 67.3479 ], [ -174.7859, 67.3474 ], [ -174.7818, 67.3443 ], [ -174.787, 67.3422 ], [ -174.7776, 67.3411 ], [ -174.7792, 67.3375 ], [ -174.7755, 67.3349 ], [ -174.7766, 67.3318 ], [ -174.763, 67.3266 ], [ -174.7641, 67.3214 ], [ -174.7568, 67.3161 ], [ -174.7578, 67.313 ], [ -174.7484, 67.312 ], [ -174.7521, 67.3083 ], [ -174.7917, 67.3063 ], [ -174.7958, 67.3042 ], [ -174.8104, 67.3042 ], [ -174.8208, 67.3 ], [ -174.8271, 67.3 ], [ -174.8318, 67.2963 ], [ -174.837, 67.2953 ], [ -174.838, 67.2922 ], [ -174.8432, 67.2911 ], [ -174.8453, 67.2891 ], [ -174.8474, 67.2839 ], [ -174.8443, 67.2818 ], [ -174.8521, 67.2813 ], [ -174.8599, 67.2786 ], [ -174.863, 67.2734 ], [ -174.8703, 67.2724 ], [ -174.8703, 67.2693 ], [ -174.863, 67.2641 ], [ -174.8641, 67.2589 ], [ -174.8568, 67.2557 ], [ -174.8562, 67.2542 ], [ -174.8568, 67.2526 ], [ -174.8641, 67.2516 ], [ -174.8651, 67.2484 ], [ -174.8703, 67.2474 ], [ -174.8734, 67.2443 ], [ -174.8786, 67.2432 ], [ -174.8786, 67.2401 ], [ -174.8714, 67.237 ], [ -174.8714, 67.2338 ], [ -174.8734, 67.2318 ], [ -174.8849, 67.2286 ], [ -174.8875, 67.2229 ], [ -174.8838, 67.2203 ], [ -174.8849, 67.2172 ], [ -174.8818, 67.2161 ], [ -174.887, 67.2141 ], [ -174.8891, 67.2099 ], [ -174.8854, 67.2063 ], [ -174.887, 67.2026 ], [ -174.8792, 67.2021 ], [ -174.8771, 67.2 ], [ -174.8797, 67.1964 ], [ -174.8849, 67.1953 ], [ -174.8911, 67.1828 ], [ -174.8875, 67.1792 ], [ -174.8891, 67.1734 ], [ -174.8797, 67.1724 ], [ -174.8818, 67.1693 ], [ -174.887, 67.1682 ], [ -174.8911, 67.1599 ], [ -174.888, 67.1578 ], [ -174.887, 67.1547 ], [ -174.8838, 67.1536 ], [ -174.8891, 67.1516 ], [ -174.8859, 67.1495 ], [ -174.887, 67.1464 ], [ -174.8838, 67.1443 ], [ -174.8891, 67.1432 ], [ -174.8932, 67.1339 ], [ -174.888, 67.1328 ], [ -174.8838, 67.1297 ], [ -174.8859, 67.1255 ], [ -174.8911, 67.1245 ], [ -174.8932, 67.1224 ], [ -174.8932, 67.1193 ], [ -174.8901, 67.1172 ], [ -174.8922, 67.1151 ], [ -174.8974, 67.1141 ], [ -174.8943, 67.1109 ], [ -174.8995, 67.1099 ], [ -174.9026, 67.1026 ], [ -174.9078, 67.1016 ], [ -174.9099, 67.0995 ], [ -174.9099, 67.0964 ], [ -174.9068, 67.0943 ], [ -174.9146, 67.0792 ], [ -174.9141, 67.0776 ], [ -174.9068, 67.0745 ], [ -174.912, 67.0714 ], [ -174.9062, 67.0687 ], [ -174.8984, 67.0682 ], [ -174.8995, 67.0651 ], [ -174.8963, 67.0641 ], [ -174.9016, 67.062 ], [ -174.9021, 67.0604 ], [ -174.8984, 67.0578 ], [ -174.8995, 67.0526 ], [ -174.8958, 67.05 ], [ -174.8984, 67.0463 ], [ -174.9036, 67.0453 ], [ -174.9057, 67.0411 ], [ -174.9021, 67.0375 ], [ -174.9036, 67.0339 ], [ -174.9005, 67.0328 ], [ -174.8995, 67.0297 ], [ -174.8875, 67.0292 ], [ -174.8776, 67.0245 ], [ -174.8786, 67.0193 ], [ -174.8708, 67.0188 ], [ -174.8609, 67.0141 ], [ -174.863, 67.0109 ], [ -174.8682, 67.0099 ], [ -174.8729, 67.0 ], [ -174.8693, 66.9974 ], [ -174.8682, 66.9943 ], [ -174.8651, 66.9932 ], [ -174.8661, 66.9901 ], [ -174.8589, 66.987 ], [ -174.8599, 66.9818 ], [ -174.8458, 66.975 ], [ -174.8276, 66.9724 ], [ -174.8297, 66.9693 ], [ -174.8349, 66.9682 ], [ -174.8318, 66.9661 ], [ -174.8307, 66.963 ], [ -174.825, 66.9604 ], [ -174.8193, 66.9599 ], [ -174.8182, 66.9568 ], [ -174.8089, 66.9557 ], [ -174.788, 66.9453 ], [ -174.7891, 66.9422 ], [ -174.7854, 66.9396 ], [ -174.787, 66.9359 ], [ -174.7729, 66.9354 ], [ -174.7713, 66.9349 ], [ -174.7724, 66.9318 ], [ -174.7646, 66.9313 ], [ -174.7547, 66.9266 ], [ -174.7578, 66.9193 ], [ -174.7437, 66.9187 ], [ -174.7312, 66.9229 ], [ -174.7229, 66.9187 ], [ -174.7109, 66.9182 ], [ -174.7125, 66.9167 ], [ -174.725, 66.9167 ], [ -174.7354, 66.9125 ], [ -174.7479, 66.9125 ], [ -174.7526, 66.9089 ], [ -174.7641, 66.9057 ], [ -174.7661, 66.9026 ], [ -174.763, 66.9015 ], [ -174.7641, 66.8964 ], [ -174.7588, 66.8953 ], [ -174.7599, 66.8922 ], [ -174.7568, 66.8912 ], [ -174.762, 66.8891 ], [ -174.7588, 66.887 ], [ -174.7578, 66.8839 ], [ -174.7505, 66.8828 ], [ -174.7495, 66.8797 ], [ -174.7339, 66.8724 ], [ -174.7375, 66.8646 ], [ -174.7339, 66.8609 ], [ -174.7375, 66.8542 ], [ -174.7339, 66.8516 ], [ -174.7328, 66.8484 ], [ -174.7297, 66.8474 ], [ -174.7307, 66.8443 ], [ -174.7276, 66.8432 ], [ -174.7328, 66.8401 ], [ -174.7255, 66.8349 ], [ -174.7266, 66.8318 ], [ -174.7193, 66.8266 ], [ -174.7203, 66.8234 ], [ -174.7172, 66.8224 ], [ -174.7182, 66.8172 ], [ -174.7109, 66.812 ], [ -174.713, 66.8089 ], [ -174.7182, 66.8078 ], [ -174.7151, 66.8047 ], [ -174.7203, 66.8026 ], [ -174.713, 66.7995 ], [ -174.7146, 66.7937 ], [ -174.7109, 66.7911 ], [ -174.712, 66.7859 ], [ -174.7088, 66.7849 ], [ -174.7099, 66.7818 ], [ -174.7047, 66.7807 ], [ -174.7057, 66.7776 ], [ -174.7021, 66.775 ], [ -174.7036, 66.7714 ], [ -174.7005, 66.7703 ], [ -174.7057, 66.7682 ], [ -174.7021, 66.7646 ], [ -174.7047, 66.7609 ], [ -174.7187, 66.7604 ], [ -174.7229, 66.7625 ], [ -174.7271, 66.7604 ], [ -174.737, 66.7599 ], [ -174.737, 66.7568 ], [ -174.7271, 66.7521 ], [ -174.7208, 66.7521 ], [ -174.7167, 66.7542 ], [ -174.7042, 66.7542 ], [ -174.7026, 66.7526 ], [ -174.7078, 66.7516 ], [ -174.7047, 66.7484 ], [ -174.7125, 66.7479 ], [ -174.7167, 66.75 ], [ -174.7292, 66.75 ], [ -174.7333, 66.7521 ], [ -174.7646, 66.7542 ], [ -174.7672, 66.7505 ], [ -174.7724, 66.7495 ], [ -174.7771, 66.7438 ], [ -174.7958, 66.7438 ], [ -174.8, 66.7417 ], [ -174.8083, 66.7417 ], [ -174.813, 66.7359 ], [ -174.8182, 66.7349 ], [ -174.8193, 66.7318 ], [ -174.8245, 66.7307 ], [ -174.8276, 66.7255 ], [ -174.8328, 66.7245 ], [ -174.8359, 66.7193 ], [ -174.8411, 66.7182 ], [ -174.8484, 66.7068 ], [ -174.8562, 66.7063 ], [ -174.8604, 66.7042 ], [ -174.8667, 66.7042 ], [ -174.8708, 66.7063 ], [ -174.8896, 66.7042 ], [ -174.8974, 66.7016 ], [ -174.8984, 66.6984 ], [ -174.9036, 66.6974 ], [ -174.9068, 66.6943 ], [ -174.9162, 66.6932 ], [ -174.913, 66.6911 ], [ -174.9141, 66.6859 ], [ -174.9109, 66.6839 ], [ -174.9208, 66.6833 ], [ -174.925, 66.6813 ], [ -174.9292, 66.6833 ], [ -174.9391, 66.6828 ], [ -174.9375, 66.6813 ], [ -174.9193, 66.6787 ], [ -174.9151, 66.6766 ], [ -174.9172, 66.6734 ], [ -174.9312, 66.6729 ], [ -174.9354, 66.6687 ], [ -174.9479, 66.6687 ], [ -174.9521, 66.6708 ], [ -174.9687, 66.6708 ], [ -174.9729, 66.6729 ], [ -174.9995, 66.6641 ], [ -175.0016, 66.6609 ], [ -174.9979, 66.6604 ], [ -174.9812, 66.6667 ], [ -174.9667, 66.6646 ], [ -174.9609, 66.662 ], [ -174.9599, 66.6589 ], [ -174.9479, 66.6583 ], [ -174.938, 66.6536 ], [ -174.937, 66.6505 ], [ -174.9339, 66.6495 ], [ -174.9349, 66.6443 ], [ -174.9229, 66.6458 ], [ -174.9146, 66.6417 ], [ -174.9068, 66.6412 ], [ -174.8958, 66.6354 ], [ -174.8838, 66.6349 ], [ -174.8849, 66.6318 ], [ -174.8818, 66.6307 ], [ -174.8807, 66.6276 ], [ -174.8708, 66.6229 ], [ -174.8651, 66.6224 ], [ -174.8641, 66.6193 ], [ -174.8609, 66.6182 ], [ -174.8661, 66.6162 ], [ -174.8625, 66.6125 ], [ -174.8651, 66.6068 ], [ -174.875, 66.6062 ], [ -174.8797, 66.6026 ], [ -174.8911, 66.5995 ], [ -174.8943, 66.5922 ], [ -174.9057, 66.5911 ], [ -174.9068, 66.588 ], [ -174.9146, 66.5854 ], [ -174.9208, 66.5875 ], [ -174.9245, 66.5849 ], [ -174.9187, 66.5813 ], [ -174.9125, 66.5813 ], [ -174.9047, 66.5839 ], [ -174.9016, 66.587 ], [ -174.8938, 66.5896 ], [ -174.8875, 66.5896 ], [ -174.8833, 66.5875 ], [ -174.8792, 66.5896 ], [ -174.8396, 66.5896 ], [ -174.8354, 66.5917 ], [ -174.8188, 66.5896 ], [ -174.8125, 66.5917 ], [ -174.8083, 66.5896 ], [ -174.7937, 66.5896 ], [ -174.7896, 66.5875 ], [ -174.7771, 66.5875 ], [ -174.7729, 66.5896 ], [ -174.7667, 66.5875 ], [ -174.7563, 66.5917 ], [ -174.7396, 66.5917 ], [ -174.7349, 66.5953 ], [ -174.7297, 66.5964 ], [ -174.7271, 66.6 ], [ -174.7208, 66.5979 ], [ -174.7, 66.5979 ], [ -174.6958, 66.5958 ], [ -174.6854, 66.5958 ], [ -174.6812, 66.5938 ], [ -174.6734, 66.5932 ], [ -174.6745, 66.588 ], [ -174.6672, 66.587 ], [ -174.6562, 66.5813 ], [ -174.6313, 66.5792 ], [ -174.6167, 66.575 ], [ -174.6062, 66.575 ], [ -174.5995, 66.5714 ], [ -174.5922, 66.5703 ], [ -174.5745, 66.5609 ], [ -174.5651, 66.5599 ], [ -174.5557, 66.5547 ], [ -174.5484, 66.5537 ], [ -174.5375, 66.5479 ], [ -174.5271, 66.5479 ], [ -174.5125, 66.5437 ], [ -174.4875, 66.5417 ], [ -174.4833, 66.5396 ], [ -174.4729, 66.5396 ], [ -174.4687, 66.5375 ], [ -174.4583, 66.5375 ], [ -174.4401, 66.5286 ], [ -174.4417, 66.525 ], [ -174.438, 66.5214 ], [ -174.4401, 66.5172 ], [ -174.4453, 66.5162 ], [ -174.4422, 66.5141 ], [ -174.4453, 66.5068 ], [ -174.438, 66.5036 ], [ -174.4391, 66.4984 ], [ -174.4312, 66.4979 ], [ -174.4255, 66.4953 ], [ -174.4266, 66.4922 ], [ -174.4193, 66.487 ], [ -174.4203, 66.4838 ], [ -174.4172, 66.4828 ], [ -174.4182, 66.4797 ], [ -174.4125, 66.4771 ], [ -174.4062, 66.4771 ], [ -174.4047, 66.4755 ], [ -174.4083, 66.4729 ], [ -174.4125, 66.475 ], [ -174.4187, 66.475 ], [ -174.4229, 66.4729 ], [ -174.4417, 66.4729 ], [ -174.4458, 66.4708 ], [ -174.4521, 66.4708 ], [ -174.4536, 66.4693 ], [ -174.4479, 66.4688 ], [ -174.4464, 66.4672 ], [ -174.4542, 66.4667 ], [ -174.462, 66.4641 ], [ -174.4672, 66.4568 ], [ -174.4724, 66.4557 ], [ -174.4708, 66.4542 ], [ -174.463, 66.4537 ], [ -174.4661, 66.4464 ], [ -174.463, 66.4453 ], [ -174.462, 66.4422 ], [ -174.4588, 66.4401 ], [ -174.4641, 66.4391 ], [ -174.4661, 66.437 ], [ -174.463, 66.4349 ], [ -174.4651, 66.4318 ], [ -174.4703, 66.4307 ], [ -174.4672, 66.4287 ], [ -174.4682, 66.4234 ], [ -174.4651, 66.4224 ], [ -174.4651, 66.4193 ], [ -174.4766, 66.4182 ], [ -174.4729, 66.4146 ], [ -174.4745, 66.4109 ], [ -174.4708, 66.4083 ], [ -174.4724, 66.4047 ], [ -174.4687, 66.4021 ], [ -174.4693, 66.4005 ], [ -174.4745, 66.3995 ], [ -174.4714, 66.3964 ], [ -174.4766, 66.3943 ], [ -174.4745, 66.3901 ], [ -174.4714, 66.3891 ], [ -174.4724, 66.3859 ], [ -174.4651, 66.3828 ], [ -174.4703, 66.3807 ], [ -174.4672, 66.3776 ], [ -174.4724, 66.3766 ], [ -174.4729, 66.375 ], [ -174.4724, 66.3734 ], [ -174.4667, 66.3708 ], [ -174.4547, 66.3703 ], [ -174.4505, 66.3682 ], [ -174.4516, 66.3651 ], [ -174.4422, 66.3641 ], [ -174.4432, 66.3609 ], [ -174.4339, 66.3599 ], [ -174.4266, 66.3547 ], [ -174.4083, 66.3562 ], [ -174.4026, 66.3536 ], [ -174.4016, 66.3505 ], [ -174.3943, 66.3495 ], [ -174.3938, 66.3479 ], [ -174.3958, 66.3458 ], [ -174.4057, 66.3453 ], [ -174.4083, 66.3417 ], [ -174.4208, 66.3417 ], [ -174.425, 66.3396 ], [ -174.4333, 66.3396 ], [ -174.4375, 66.3375 ], [ -174.4542, 66.3375 ], [ -174.462, 66.3349 ], [ -174.4703, 66.3297 ], [ -174.4625, 66.3292 ], [ -174.4568, 66.3266 ], [ -174.462, 66.3234 ], [ -174.4505, 66.3182 ], [ -174.4516, 66.3151 ], [ -174.4484, 66.3141 ], [ -174.4557, 66.2984 ], [ -174.4521, 66.2979 ], [ -174.4479, 66.3 ], [ -174.4333, 66.2958 ], [ -174.4214, 66.2953 ], [ -174.413, 66.2911 ], [ -174.4167, 66.2854 ], [ -174.4146, 66.2833 ], [ -174.4104, 66.2854 ], [ -174.3938, 66.2833 ], [ -174.3797, 66.288 ], [ -174.3828, 66.2901 ], [ -174.3818, 66.2932 ], [ -174.3875, 66.2937 ], [ -174.3932, 66.2963 ], [ -174.3917, 66.2979 ], [ -174.3854, 66.2979 ], [ -174.3786, 66.2943 ], [ -174.3714, 66.2932 ], [ -174.3766, 66.2901 ], [ -174.3667, 66.2875 ], [ -174.3604, 66.2896 ], [ -174.3542, 66.2896 ], [ -174.35, 66.2875 ], [ -174.3234, 66.2859 ], [ -174.3276, 66.2891 ], [ -174.3349, 66.2901 ], [ -174.3391, 66.2932 ], [ -174.325, 66.2979 ], [ -174.3208, 66.2958 ], [ -174.313, 66.2963 ], [ -174.3057, 66.3057 ], [ -174.3005, 66.3068 ], [ -174.2974, 66.3141 ], [ -174.2922, 66.3151 ], [ -174.2953, 66.3182 ], [ -174.2901, 66.3193 ], [ -174.3016, 66.3234 ], [ -174.3, 66.3292 ], [ -174.3036, 66.3318 ], [ -174.3005, 66.3359 ], [ -174.3042, 66.3396 ], [ -174.3026, 66.3432 ], [ -174.3099, 66.3484 ], [ -174.3047, 66.3505 ], [ -174.3016, 66.3557 ], [ -174.2901, 66.3588 ], [ -174.2932, 66.362 ], [ -174.2833, 66.3625 ], [ -174.2807, 66.3662 ], [ -174.2708, 66.3667 ], [ -174.263, 66.3693 ], [ -174.2604, 66.3729 ], [ -174.2641, 66.3766 ], [ -174.2588, 66.3776 ], [ -174.2557, 66.3828 ], [ -174.2505, 66.3839 ], [ -174.2474, 66.3891 ], [ -174.2396, 66.3917 ], [ -174.2312, 66.3917 ], [ -174.2271, 66.3938 ], [ -174.2208, 66.3917 ], [ -174.2063, 66.3958 ], [ -174.1938, 66.3958 ], [ -174.1875, 66.4 ], [ -174.1687, 66.4021 ], [ -174.1651, 66.4047 ], [ -174.163, 66.4089 ], [ -174.1662, 66.412 ], [ -174.1583, 66.4146 ], [ -174.1521, 66.4146 ], [ -174.1484, 66.4172 ], [ -174.1443, 66.4255 ], [ -174.1443, 66.4287 ], [ -174.1474, 66.4297 ], [ -174.1463, 66.4328 ], [ -174.1495, 66.4339 ], [ -174.1484, 66.437 ], [ -174.1641, 66.4443 ], [ -174.163, 66.4516 ], [ -174.1729, 66.4542 ], [ -174.1792, 66.4521 ], [ -174.1974, 66.4526 ], [ -174.2016, 66.4547 ], [ -174.2005, 66.4578 ], [ -174.2078, 66.463 ], [ -174.2057, 66.4661 ], [ -174.2005, 66.4661 ], [ -174.1958, 66.4604 ], [ -174.1812, 66.4583 ], [ -174.1771, 66.4563 ], [ -174.1667, 66.4563 ], [ -174.1625, 66.4542 ], [ -174.1583, 66.4563 ], [ -174.1292, 66.4583 ], [ -174.1167, 66.4625 ], [ -174.1104, 66.4604 ], [ -174.1, 66.4646 ], [ -174.0958, 66.4625 ], [ -174.0896, 66.4625 ], [ -174.0854, 66.4646 ], [ -174.0729, 66.4646 ], [ -174.0688, 66.4667 ], [ -174.0562, 66.4667 ], [ -174.0521, 66.4688 ], [ -174.0292, 66.4688 ], [ -174.0229, 66.4708 ], [ -174.0213, 66.4703 ], [ -174.0224, 66.4672 ], [ -174.0208, 66.4667 ], [ -174.0146, 66.4688 ], [ -174.0104, 66.4667 ], [ -174.0042, 66.4667 ], [ -173.9958, 66.4625 ], [ -173.9797, 66.4599 ], [ -173.9651, 66.4516 ], [ -173.9641, 66.4484 ], [ -173.9609, 66.4474 ], [ -173.9724, 66.4432 ], [ -173.9693, 66.4401 ], [ -173.9714, 66.4359 ], [ -173.9766, 66.4349 ], [ -173.9734, 66.4328 ], [ -173.9745, 66.4297 ], [ -173.9672, 66.4287 ], [ -173.9672, 66.4255 ], [ -173.9849, 66.4203 ], [ -173.987, 66.4172 ], [ -173.9839, 66.4161 ], [ -173.9849, 66.4109 ], [ -173.9818, 66.4099 ], [ -173.9828, 66.4047 ], [ -173.9755, 66.4016 ], [ -173.9787, 66.3922 ], [ -173.9547, 66.3828 ], [ -173.9557, 66.3797 ], [ -173.9484, 66.3766 ], [ -173.9474, 66.3734 ], [ -173.9417, 66.3729 ], [ -173.9318, 66.3682 ], [ -173.9318, 66.3651 ], [ -173.9354, 66.3604 ], [ -173.9318, 66.3568 ], [ -173.937, 66.3557 ], [ -173.9339, 66.3536 ], [ -173.937, 66.3464 ], [ -173.9333, 66.3438 ], [ -173.9354, 66.3417 ], [ -173.9479, 66.3417 ], [ -173.9521, 66.3396 ], [ -173.9583, 66.3417 ], [ -173.9651, 66.3359 ], [ -173.9703, 66.3349 ], [ -173.9714, 66.3318 ], [ -173.9979, 66.3229 ], [ -174.0063, 66.3229 ], [ -174.0141, 66.3203 ], [ -174.0151, 66.3172 ], [ -174.0203, 66.3161 ], [ -174.0224, 66.313 ], [ -174.0193, 66.3109 ], [ -174.0292, 66.2917 ], [ -174.0276, 66.288 ], [ -174.0328, 66.287 ], [ -174.0292, 66.2833 ], [ -174.0307, 66.2797 ], [ -174.0234, 66.2745 ], [ -174.0297, 66.263 ], [ -174.0432, 66.2578 ], [ -174.0458, 66.2542 ], [ -174.0542, 66.2542 ], [ -174.0583, 66.2521 ], [ -174.0708, 66.2521 ], [ -174.0729, 66.25 ], [ -174.0724, 66.2484 ], [ -174.0667, 66.2458 ], [ -174.0604, 66.25 ], [ -174.038, 66.2495 ], [ -174.0349, 66.2422 ], [ -174.0276, 66.2391 ], [ -174.0307, 66.2318 ], [ -174.025, 66.2292 ], [ -174.0188, 66.2292 ], [ -174.0151, 66.2318 ], [ -174.0224, 66.2359 ], [ -174.0224, 66.2391 ], [ -174.0208, 66.2396 ], [ -174.0146, 66.2396 ], [ -174.0104, 66.2375 ], [ -173.9979, 66.2375 ], [ -173.9937, 66.2396 ], [ -173.9859, 66.2391 ], [ -173.987, 66.2359 ], [ -173.9839, 66.2338 ], [ -173.9917, 66.2312 ], [ -173.9995, 66.2307 ], [ -174.0026, 66.2276 ], [ -174.0078, 66.2266 ], [ -174.0125, 66.2229 ], [ -174.0167, 66.225 ], [ -174.0203, 66.2245 ], [ -174.0213, 66.2214 ], [ -174.0266, 66.2193 ], [ -174.0042, 66.2188 ], [ -174.0026, 66.2182 ], [ -174.0036, 66.2151 ], [ -173.9979, 66.2125 ], [ -173.9672, 66.2078 ], [ -173.9682, 66.2047 ], [ -173.9651, 66.2037 ], [ -173.9641, 66.2005 ], [ -173.9583, 66.1979 ], [ -173.9521, 66.1979 ], [ -173.9505, 66.1995 ], [ -173.9578, 66.2026 ], [ -173.9583, 66.2042 ], [ -173.9578, 66.2057 ], [ -173.9526, 66.2078 ], [ -173.9687, 66.2104 ], [ -173.9729, 66.2125 ], [ -173.9812, 66.2125 ], [ -173.987, 66.2151 ], [ -173.9849, 66.2224 ], [ -173.9734, 66.2255 ], [ -173.9766, 66.2276 ], [ -173.9755, 66.2307 ], [ -173.9787, 66.2318 ], [ -173.9766, 66.2349 ], [ -173.9687, 66.2354 ], [ -173.9422, 66.2443 ], [ -173.9453, 66.2474 ], [ -173.9432, 66.2495 ], [ -173.938, 66.2505 ], [ -173.9349, 66.2578 ], [ -173.9276, 66.2589 ], [ -173.9266, 66.262 ], [ -173.9214, 66.263 ], [ -173.9245, 66.2662 ], [ -173.9193, 66.2672 ], [ -173.9141, 66.2745 ], [ -173.9047, 66.2755 ], [ -173.8875, 66.2833 ], [ -173.8792, 66.2833 ], [ -173.8646, 66.2875 ], [ -173.8521, 66.2875 ], [ -173.8479, 66.2896 ], [ -173.8354, 66.2896 ], [ -173.8276, 66.2922 ], [ -173.8245, 66.2953 ], [ -173.813, 66.2984 ], [ -173.812, 66.3016 ], [ -173.8021, 66.3021 ], [ -173.7943, 66.3047 ], [ -173.7958, 66.3063 ], [ -173.8036, 66.3068 ], [ -173.8026, 66.3099 ], [ -173.8068, 66.312 ], [ -173.8167, 66.3146 ], [ -173.8271, 66.3146 ], [ -173.8354, 66.3187 ], [ -173.8625, 66.3187 ], [ -173.8687, 66.3167 ], [ -173.8745, 66.3193 ], [ -173.8734, 66.3224 ], [ -173.8807, 66.3234 ], [ -173.8932, 66.3297 ], [ -173.8932, 66.3328 ], [ -173.8854, 66.3354 ], [ -173.8755, 66.3359 ], [ -173.8745, 66.3391 ], [ -173.8708, 66.3417 ], [ -173.8646, 66.3417 ], [ -173.8604, 66.3396 ], [ -173.8542, 66.3417 ], [ -173.8313, 66.3354 ], [ -173.8271, 66.3375 ], [ -173.8208, 66.3354 ], [ -173.8104, 66.3396 ], [ -173.8021, 66.3396 ], [ -173.7979, 66.3417 ], [ -173.7854, 66.3417 ], [ -173.7583, 66.35 ], [ -173.7547, 66.3495 ], [ -173.7557, 66.3464 ], [ -173.7521, 66.3458 ], [ -173.7443, 66.3484 ], [ -173.7474, 66.3505 ], [ -173.7458, 66.3521 ], [ -173.7276, 66.3505 ], [ -173.7318, 66.3536 ], [ -173.7375, 66.3542 ], [ -173.7391, 66.3557 ], [ -173.7172, 66.3588 ], [ -173.7161, 66.362 ], [ -173.7109, 66.363 ], [ -173.7088, 66.3651 ], [ -173.7042, 66.3729 ], [ -173.7047, 66.3745 ], [ -173.712, 66.3776 ], [ -173.7068, 66.3797 ], [ -173.7047, 66.3828 ], [ -173.712, 66.3859 ], [ -173.7109, 66.3912 ], [ -173.7141, 66.3922 ], [ -173.7125, 66.3958 ], [ -173.7161, 66.3995 ], [ -173.7109, 66.4005 ], [ -173.7016, 66.4161 ], [ -173.6963, 66.4172 ], [ -173.6911, 66.4266 ], [ -173.6859, 66.4287 ], [ -173.6911, 66.4318 ], [ -173.6901, 66.4391 ], [ -173.6974, 66.4401 ], [ -173.7042, 66.4437 ], [ -173.712, 66.4443 ], [ -173.7088, 66.4484 ], [ -173.7088, 66.4516 ], [ -173.7172, 66.4557 ], [ -173.7417, 66.4604 ], [ -173.7458, 66.4646 ], [ -173.7563, 66.4646 ], [ -173.7688, 66.4688 ], [ -173.7854, 66.4688 ], [ -173.7896, 66.4708 ], [ -173.8016, 66.4693 ], [ -173.8042, 66.475 ], [ -173.8016, 66.4807 ], [ -173.7812, 66.4833 ], [ -173.7776, 66.487 ], [ -173.7833, 66.4896 ], [ -173.812, 66.4901 ], [ -173.8224, 66.4964 ], [ -173.8213, 66.5016 ], [ -173.8245, 66.5026 ], [ -173.8255, 66.5057 ], [ -173.8313, 66.5083 ], [ -173.837, 66.5089 ], [ -173.8318, 66.5109 ], [ -173.8333, 66.5125 ], [ -173.8542, 66.5125 ], [ -173.8687, 66.5167 ], [ -173.8729, 66.5146 ], [ -173.8792, 66.5146 ], [ -173.9021, 66.5208 ], [ -173.9146, 66.5208 ], [ -173.9187, 66.5188 ], [ -173.9604, 66.5188 ], [ -173.9703, 66.5234 ], [ -173.9714, 66.5266 ], [ -173.9833, 66.5271 ], [ -173.9958, 66.5313 ], [ -174.0688, 66.5313 ], [ -174.0713, 66.5349 ], [ -174.0813, 66.5375 ], [ -174.0995, 66.538 ], [ -174.0984, 66.5411 ], [ -174.1057, 66.5463 ], [ -174.1047, 66.5516 ], [ -174.1078, 66.5526 ], [ -174.1047, 66.5568 ], [ -174.1062, 66.5583 ], [ -174.1307, 66.5578 ], [ -174.1338, 66.5526 ], [ -174.1391, 66.5516 ], [ -174.1422, 66.5484 ], [ -174.1536, 66.5453 ], [ -174.1589, 66.5359 ], [ -174.1667, 66.5333 ], [ -174.1724, 66.5391 ], [ -174.1682, 66.5474 ], [ -174.1662, 66.5495 ], [ -174.1609, 66.5505 ], [ -174.1589, 66.5557 ], [ -174.1646, 66.5583 ], [ -174.175, 66.5583 ], [ -174.1792, 66.5604 ], [ -174.1854, 66.5583 ], [ -174.1917, 66.5583 ], [ -174.1958, 66.5563 ], [ -174.2021, 66.5563 ], [ -174.2078, 66.5589 ], [ -174.2083, 66.5604 ], [ -174.2078, 66.562 ], [ -174.2026, 66.5641 ], [ -174.2099, 66.5651 ], [ -174.2141, 66.5672 ], [ -174.2151, 66.5703 ], [ -174.2208, 66.5729 ], [ -174.2266, 66.5734 ], [ -174.225, 66.575 ], [ -174.2172, 66.5755 ], [ -174.212, 66.5807 ], [ -174.2005, 66.5839 ], [ -174.1953, 66.5953 ], [ -174.1938, 66.5958 ], [ -174.1859, 66.5932 ], [ -174.188, 66.5901 ], [ -174.1932, 66.588 ], [ -174.1839, 66.587 ], [ -174.1849, 66.5839 ], [ -174.1724, 66.5776 ], [ -174.163, 66.5766 ], [ -174.1682, 66.5734 ], [ -174.1583, 66.5708 ], [ -174.1521, 66.5729 ], [ -174.1396, 66.5729 ], [ -174.1354, 66.5771 ], [ -174.1167, 66.5771 ], [ -174.1125, 66.5792 ], [ -174.1062, 66.5771 ], [ -174.1047, 66.5787 ], [ -174.1078, 66.5807 ], [ -174.0984, 66.5818 ], [ -174.0953, 66.5849 ], [ -174.0875, 66.5875 ], [ -174.0792, 66.5875 ], [ -174.0713, 66.5901 ], [ -174.0703, 66.5932 ], [ -174.0651, 66.5943 ], [ -174.0682, 66.5974 ], [ -174.063, 66.5984 ], [ -174.0609, 66.6005 ], [ -174.0583, 66.6062 ], [ -174.062, 66.6088 ], [ -174.0599, 66.6141 ], [ -174.0526, 66.6151 ], [ -174.0521, 66.6167 ], [ -174.0542, 66.6188 ], [ -174.0667, 66.6146 ], [ -174.0771, 66.6146 ], [ -174.0786, 66.6162 ], [ -174.0708, 66.6188 ], [ -174.0521, 66.6208 ], [ -174.0484, 66.6234 ], [ -174.0474, 66.6266 ], [ -174.0333, 66.6271 ], [ -174.0286, 66.6307 ], [ -173.9984, 66.6401 ], [ -173.9953, 66.6432 ], [ -173.9875, 66.6458 ], [ -173.9812, 66.6458 ], [ -173.975, 66.65 ], [ -173.9672, 66.6505 ], [ -173.9625, 66.6542 ], [ -173.9542, 66.6542 ], [ -173.9401, 66.6589 ], [ -173.9375, 66.6625 ], [ -173.9146, 66.6625 ], [ -173.9042, 66.6667 ], [ -173.8917, 66.6667 ], [ -173.8901, 66.6661 ], [ -173.8911, 66.663 ], [ -173.8854, 66.6604 ], [ -173.8609, 66.6609 ], [ -173.8625, 66.6625 ], [ -173.8792, 66.6625 ], [ -173.8807, 66.663 ], [ -173.8797, 66.6661 ], [ -173.887, 66.6693 ], [ -173.8833, 66.6729 ], [ -173.8693, 66.6734 ], [ -173.8734, 66.6766 ], [ -173.8771, 66.6771 ], [ -173.8813, 66.675 ], [ -173.8979, 66.675 ], [ -173.8995, 66.6755 ], [ -173.8984, 66.6807 ], [ -173.9162, 66.6818 ], [ -173.9203, 66.6839 ], [ -173.9193, 66.6891 ], [ -173.9266, 66.6922 ], [ -173.9255, 66.6974 ], [ -173.9286, 66.6984 ], [ -173.9297, 66.7016 ], [ -173.9328, 66.7037 ], [ -173.9276, 66.7047 ], [ -173.9255, 66.7078 ], [ -173.9349, 66.7089 ], [ -173.9276, 66.7224 ], [ -173.9349, 66.7255 ], [ -173.9359, 66.7286 ], [ -173.9391, 66.7297 ], [ -173.9339, 66.7401 ], [ -173.937, 66.7422 ], [ -173.937, 66.7453 ], [ -173.9318, 66.7464 ], [ -173.9349, 66.7495 ], [ -173.9297, 66.7578 ], [ -173.937, 66.7609 ], [ -173.9359, 66.7662 ], [ -173.9396, 66.7688 ], [ -173.9349, 66.7766 ], [ -173.9297, 66.7776 ], [ -173.9297, 66.7807 ], [ -173.937, 66.7818 ], [ -173.9453, 66.7859 ], [ -173.9463, 66.7891 ], [ -173.9495, 66.7901 ], [ -173.9422, 66.8037 ], [ -173.9495, 66.8089 ], [ -173.9484, 66.8141 ], [ -173.9557, 66.8172 ], [ -173.9547, 66.8203 ], [ -173.9583, 66.8229 ], [ -173.9568, 66.8287 ], [ -173.9599, 66.8297 ], [ -173.9588, 66.8328 ], [ -173.9625, 66.8354 ], [ -173.9604, 66.8396 ], [ -173.9641, 66.8422 ], [ -173.963, 66.8474 ], [ -173.9661, 66.8484 ], [ -173.9651, 66.8516 ], [ -173.9724, 66.8568 ], [ -173.9714, 66.8599 ], [ -173.9787, 66.863 ], [ -173.9797, 66.8662 ], [ -173.9953, 66.8734 ], [ -173.9943, 66.8766 ], [ -173.9974, 66.8776 ], [ -173.9984, 66.8807 ], [ -174.0099, 66.8859 ], [ -174.0088, 66.8891 ], [ -174.0161, 66.8922 ], [ -174.0151, 66.8974 ], [ -174.0224, 66.9005 ], [ -174.0224, 66.9036 ], [ -174.0203, 66.9057 ], [ -174.0151, 66.9068 ], [ -174.0188, 66.9104 ], [ -174.0161, 66.9161 ], [ -174.0088, 66.9172 ], [ -174.0125, 66.9208 ], [ -174.0104, 66.925 ], [ -174.0109, 66.9266 ], [ -174.0182, 66.9297 ], [ -174.0151, 66.9359 ], [ -174.0188, 66.9396 ], [ -174.0172, 66.9432 ], [ -174.0245, 66.9484 ], [ -174.0234, 66.9516 ], [ -174.0307, 66.9547 ], [ -174.0297, 66.9599 ], [ -174.037, 66.9651 ], [ -174.0359, 66.9682 ], [ -174.0396, 66.9708 ], [ -174.0375, 66.9729 ], [ -174.0292, 66.9729 ], [ -174.0213, 66.9755 ], [ -174.0188, 66.9792 ], [ -174.0042, 66.9792 ], [ -174.0026, 66.9807 ], [ -174.0271, 66.9812 ], [ -174.0312, 66.9792 ], [ -174.0437, 66.9792 ], [ -174.0479, 66.9771 ], [ -174.0562, 66.9771 ], [ -174.0604, 66.975 ], [ -174.0667, 66.975 ], [ -174.0708, 66.9771 ], [ -174.0938, 66.9771 ], [ -174.1, 66.975 ], [ -174.1099, 66.9776 ], [ -174.1193, 66.9828 ], [ -174.1271, 66.9833 ], [ -174.1313, 66.9854 ], [ -174.1479, 66.9854 ], [ -174.1521, 66.9833 ], [ -174.1583, 66.9854 ], [ -174.1687, 66.9854 ], [ -174.1729, 66.9833 ], [ -174.1854, 66.9833 ], [ -174.1953, 66.9807 ], [ -174.1979, 66.975 ], [ -174.1943, 66.9714 ], [ -174.1979, 66.9688 ], [ -174.2083, 66.9688 ], [ -174.2141, 66.9714 ], [ -174.2109, 66.9807 ], [ -174.2167, 66.9833 ], [ -174.2203, 66.9828 ], [ -174.2214, 66.9797 ], [ -174.2328, 66.9766 ], [ -174.2359, 66.9734 ], [ -174.2411, 66.9734 ], [ -174.2453, 66.9755 ], [ -174.2432, 66.9786 ], [ -174.2333, 66.9792 ], [ -174.2255, 66.9818 ], [ -174.2245, 66.9849 ], [ -174.213, 66.988 ], [ -174.2078, 66.9953 ], [ -174.1984, 66.9964 ], [ -174.2016, 66.9995 ], [ -174.1963, 67.0005 ], [ -174.1922, 67.0047 ], [ -174.1896, 67.0104 ], [ -174.1932, 67.013 ], [ -174.1922, 67.0162 ], [ -174.1974, 67.0172 ], [ -174.1958, 67.0188 ], [ -174.1901, 67.0193 ], [ -174.1932, 67.0214 ], [ -174.1943, 67.0245 ], [ -174.2016, 67.0276 ], [ -174.2026, 67.0307 ], [ -174.2083, 67.0313 ], [ -174.2141, 67.0339 ], [ -174.2026, 67.038 ], [ -174.2016, 67.0411 ], [ -174.1943, 67.0422 ], [ -174.1943, 67.0453 ], [ -174.1974, 67.0463 ], [ -174.1963, 67.0495 ], [ -174.2167, 67.0563 ], [ -174.2229, 67.0542 ], [ -174.2354, 67.0542 ], [ -174.2396, 67.0521 ], [ -174.2521, 67.0521 ], [ -174.2563, 67.05 ], [ -174.2854, 67.05 ], [ -174.2896, 67.0479 ], [ -174.3021, 67.0479 ], [ -174.3062, 67.0458 ], [ -174.3188, 67.0458 ], [ -174.3229, 67.0437 ], [ -174.3375, 67.0437 ], [ -174.3521, 67.0396 ], [ -174.3604, 67.0396 ], [ -174.3708, 67.0354 ], [ -174.3833, 67.0354 ], [ -174.3875, 67.0333 ], [ -174.3958, 67.0333 ], [ -174.4, 67.0313 ], [ -174.4396, 67.0292 ], [ -174.4438, 67.0271 ], [ -174.4521, 67.0271 ], [ -174.4563, 67.025 ], [ -174.4896, 67.025 ], [ -174.4911, 67.0266 ], [ -174.4891, 67.0286 ], [ -174.4563, 67.0396 ], [ -174.4479, 67.0396 ], [ -174.4375, 67.0437 ], [ -174.425, 67.0437 ], [ -174.4208, 67.0458 ], [ -174.4146, 67.0458 ], [ -174.4083, 67.0479 ], [ -174.4068, 67.0474 ], [ -174.4078, 67.0443 ], [ -174.3938, 67.0417 ], [ -174.3859, 67.0453 ], [ -174.3938, 67.0458 ], [ -174.4036, 67.0495 ], [ -174.3922, 67.0526 ], [ -174.3896, 67.0563 ], [ -174.3813, 67.0563 ], [ -174.3708, 67.0604 ], [ -174.3646, 67.0583 ], [ -174.3542, 67.0625 ], [ -174.3458, 67.0625 ], [ -174.3417, 67.0646 ], [ -174.2812, 67.0646 ], [ -174.2771, 67.0667 ], [ -174.2458, 67.0625 ], [ -174.2443, 67.0641 ], [ -174.25, 67.0646 ], [ -174.2516, 67.0661 ], [ -174.2437, 67.0667 ], [ -174.2375, 67.0646 ], [ -174.2167, 67.0708 ], [ -174.2042, 67.0708 ], [ -174.2, 67.0729 ], [ -174.1792, 67.075 ], [ -174.1646, 67.0792 ], [ -174.15, 67.0792 ], [ -174.1458, 67.0813 ], [ -174.0813, 67.0813 ], [ -174.0771, 67.0792 ], [ -174.0708, 67.0813 ], [ -174.0271, 67.0813 ], [ -174.0229, 67.0792 ], [ -173.9333, 67.0792 ], [ -173.9292, 67.0771 ], [ -173.875, 67.0771 ], [ -173.8708, 67.075 ], [ -173.8333, 67.075 ], [ -173.8292, 67.0729 ], [ -173.8167, 67.0729 ], [ -173.8125, 67.075 ], [ -173.8, 67.075 ], [ -173.7958, 67.0729 ], [ -173.7917, 67.075 ], [ -173.7854, 67.0729 ], [ -173.7521, 67.0729 ], [ -173.7479, 67.075 ], [ -173.7417, 67.075 ], [ -173.7375, 67.0729 ], [ -173.7333, 67.075 ], [ -173.7146, 67.075 ], [ -173.7104, 67.0771 ], [ -173.7021, 67.0771 ], [ -173.6979, 67.0792 ], [ -173.6854, 67.0792 ], [ -173.675, 67.0833 ], [ -173.6625, 67.0833 ], [ -173.6547, 67.0859 ], [ -173.6578, 67.0891 ], [ -173.6505, 67.0901 ], [ -173.6484, 67.0953 ], [ -173.6516, 67.0974 ], [ -173.6463, 67.0984 ], [ -173.65, 67.1021 ], [ -173.6474, 67.1078 ], [ -173.6438, 67.1104 ], [ -173.6313, 67.1104 ], [ -173.6271, 67.1083 ], [ -173.6167, 67.1083 ], [ -173.6026, 67.1016 ], [ -173.6078, 67.0995 ], [ -173.6062, 67.0979 ], [ -173.6, 67.0979 ], [ -173.5958, 67.1 ], [ -173.5792, 67.1 ], [ -173.575, 67.0979 ], [ -173.5646, 67.0979 ], [ -173.5604, 67.0958 ], [ -173.5542, 67.0958 ], [ -173.55, 67.0938 ], [ -173.5292, 67.0938 ], [ -173.525, 67.0917 ], [ -173.5146, 67.0917 ], [ -173.5104, 67.0896 ], [ -173.4979, 67.0896 ], [ -173.4937, 67.0875 ], [ -173.4792, 67.0875 ], [ -173.475, 67.0854 ], [ -173.4646, 67.0854 ], [ -173.45, 67.0813 ], [ -173.4292, 67.0813 ], [ -173.425, 67.0792 ], [ -173.3979, 67.0792 ], [ -173.3938, 67.0771 ], [ -173.3833, 67.0771 ], [ -173.3687, 67.0729 ], [ -173.3521, 67.0729 ], [ -173.3479, 67.0708 ], [ -173.3417, 67.0729 ], [ -173.3375, 67.0708 ], [ -173.3229, 67.0708 ], [ -173.3188, 67.0687 ], [ -173.2708, 67.0667 ], [ -173.2667, 67.0646 ], [ -173.2563, 67.0646 ], [ -173.2521, 67.0625 ], [ -173.225, 67.0625 ], [ -173.2208, 67.0604 ], [ -173.2042, 67.0604 ], [ -173.2, 67.0583 ], [ -173.1854, 67.0583 ], [ -173.1812, 67.0563 ], [ -173.1583, 67.0563 ], [ -173.1542, 67.0542 ], [ -173.1396, 67.0542 ], [ -173.1354, 67.0521 ], [ -173.1229, 67.0521 ], [ -173.1187, 67.05 ], [ -173.1125, 67.0521 ], [ -173.1083, 67.05 ], [ -173.0979, 67.05 ], [ -173.0937, 67.0479 ], [ -173.0667, 67.0479 ], [ -173.0599, 67.0443 ], [ -173.0526, 67.0432 ], [ -173.0641, 67.0391 ], [ -173.0609, 67.0359 ], [ -173.0661, 67.0339 ], [ -173.0604, 67.0313 ], [ -173.0547, 67.0307 ], [ -173.0536, 67.0276 ], [ -173.0505, 67.0266 ], [ -173.0516, 67.0234 ], [ -173.0484, 67.0224 ], [ -173.0484, 67.0193 ], [ -173.0646, 67.0146 ], [ -173.0672, 67.0182 ], [ -173.0745, 67.0214 ], [ -173.0714, 67.0255 ], [ -173.0714, 67.0286 ], [ -173.0771, 67.0313 ], [ -173.0849, 67.0307 ], [ -173.0896, 67.0271 ], [ -173.0937, 67.0292 ], [ -173.1, 67.0292 ], [ -173.1016, 67.0307 ], [ -173.0964, 67.0328 ], [ -173.1062, 67.0375 ], [ -173.1141, 67.037 ], [ -173.1109, 67.0339 ], [ -173.1187, 67.0313 ], [ -173.1229, 67.0333 ], [ -173.1307, 67.0328 ], [ -173.1328, 67.0297 ], [ -173.125, 67.0292 ], [ -173.1234, 67.0276 ], [ -173.1307, 67.0266 ], [ -173.1318, 67.0234 ], [ -173.1354, 67.0229 ], [ -173.1438, 67.0271 ], [ -173.1557, 67.0276 ], [ -173.1651, 67.0328 ], [ -173.1724, 67.0339 ], [ -173.175, 67.0375 ], [ -173.1812, 67.0354 ], [ -173.1854, 67.0375 ], [ -173.2021, 67.0375 ], [ -173.2063, 67.0396 ], [ -173.2104, 67.0375 ], [ -173.2167, 67.0375 ], [ -173.2208, 67.0396 ], [ -173.2271, 67.0375 ], [ -173.2333, 67.0375 ], [ -173.2354, 67.0396 ], [ -173.2339, 67.0432 ], [ -173.2432, 67.0443 ], [ -173.2375, 67.05 ], [ -173.2271, 67.05 ], [ -173.2208, 67.0479 ], [ -173.213, 67.0505 ], [ -173.2146, 67.0521 ], [ -173.2312, 67.0521 ], [ -173.2396, 67.0563 ], [ -173.25, 67.0563 ], [ -173.2646, 67.0604 ], [ -173.2708, 67.0583 ], [ -173.2792, 67.0625 ], [ -173.3062, 67.0625 ], [ -173.3203, 67.0568 ], [ -173.3146, 67.0563 ], [ -173.3078, 67.0526 ], [ -173.3, 67.0521 ], [ -173.2958, 67.05 ], [ -173.2833, 67.05 ], [ -173.2818, 67.0516 ], [ -173.2849, 67.0537 ], [ -173.2729, 67.0542 ], [ -173.2672, 67.0516 ], [ -173.2661, 67.0484 ], [ -173.263, 67.0474 ], [ -173.2641, 67.0443 ], [ -173.2568, 67.0391 ], [ -173.2599, 67.0328 ], [ -173.2568, 67.0297 ], [ -173.2682, 67.0286 ], [ -173.2708, 67.0229 ], [ -173.2661, 67.0193 ], [ -173.2563, 67.0167 ], [ -173.2333, 67.0167 ], [ -173.2292, 67.0146 ], [ -173.2188, 67.0146 ], [ -173.2146, 67.0125 ], [ -173.1833, 67.0125 ], [ -173.1792, 67.0104 ], [ -173.1687, 67.0104 ], [ -173.1589, 67.0057 ], [ -173.1578, 67.0026 ], [ -173.1505, 67.0016 ], [ -173.1516, 66.9964 ], [ -173.1484, 66.9943 ], [ -173.1505, 66.9901 ], [ -173.1557, 66.9891 ], [ -173.1604, 66.9854 ], [ -173.1771, 66.9854 ], [ -173.1812, 66.9875 ], [ -173.1932, 66.988 ], [ -173.1943, 66.9912 ], [ -173.2016, 66.9943 ], [ -173.1995, 66.9974 ], [ -173.1943, 66.9984 ], [ -173.1958, 67.0 ], [ -173.2083, 67.0 ], [ -173.2354, 66.9917 ], [ -173.2437, 66.9917 ], [ -173.2479, 66.9938 ], [ -173.275, 66.9917 ], [ -173.2792, 66.9896 ], [ -173.2891, 66.9891 ], [ -173.2922, 66.9838 ], [ -173.2974, 66.9828 ], [ -173.3005, 66.9776 ], [ -173.3078, 66.9766 ], [ -173.3109, 66.9714 ], [ -173.3161, 66.9703 ], [ -173.3188, 66.9667 ], [ -173.3292, 66.9708 ], [ -173.3349, 66.9714 ], [ -173.3432, 66.9755 ], [ -173.3422, 66.9807 ], [ -173.3479, 66.9833 ], [ -173.3604, 66.9833 ], [ -173.3682, 66.9807 ], [ -173.3729, 66.9771 ], [ -173.3917, 66.9771 ], [ -173.3958, 66.975 ], [ -173.4036, 66.9745 ], [ -173.4005, 66.9724 ], [ -173.4016, 66.9693 ], [ -173.3922, 66.9682 ], [ -173.3771, 66.9604 ], [ -173.3667, 66.9604 ], [ -173.3625, 66.9583 ], [ -173.3396, 66.9583 ], [ -173.3354, 66.9604 ], [ -173.3292, 66.9604 ], [ -173.3245, 66.9641 ], [ -173.3208, 66.9646 ], [ -173.3193, 66.9641 ], [ -173.3203, 66.9609 ], [ -173.3089, 66.9557 ], [ -173.3109, 66.9505 ], [ -173.3125, 66.95 ], [ -173.3188, 66.9521 ], [ -173.325, 66.95 ], [ -173.3292, 66.9521 ], [ -173.3396, 66.9521 ], [ -173.3437, 66.9542 ], [ -173.3479, 66.9521 ], [ -173.3625, 66.9521 ], [ -173.3703, 66.9495 ], [ -173.3734, 66.9422 ], [ -173.3755, 66.9401 ], [ -173.3807, 66.9391 ], [ -173.3849, 66.9318 ], [ -173.3708, 66.9313 ], [ -173.3521, 66.9396 ], [ -173.3354, 66.9375 ], [ -173.3229, 66.9417 ], [ -173.3172, 66.9391 ], [ -173.3203, 66.9349 ], [ -173.3203, 66.9318 ], [ -173.3078, 66.9255 ], [ -173.3021, 66.925 ], [ -173.2964, 66.9224 ], [ -173.3016, 66.9203 ], [ -173.3036, 66.9151 ], [ -173.2917, 66.9146 ], [ -173.2901, 66.913 ], [ -173.2979, 66.9125 ], [ -173.3083, 66.9083 ], [ -173.3141, 66.9078 ], [ -173.3167, 66.9021 ], [ -173.313, 66.8984 ], [ -173.3182, 66.8964 ], [ -173.3109, 66.8953 ], [ -173.3099, 66.8922 ], [ -173.3057, 66.8901 ], [ -173.2984, 66.8891 ], [ -173.2995, 66.8839 ], [ -173.2917, 66.8833 ], [ -173.2776, 66.8786 ], [ -173.2776, 66.8755 ], [ -173.2875, 66.8729 ], [ -173.3, 66.8729 ], [ -173.3026, 66.8693 ], [ -173.3141, 66.8662 ], [ -173.3188, 66.8604 ], [ -173.3151, 66.8578 ], [ -173.3161, 66.8526 ], [ -173.3089, 66.8516 ], [ -173.3078, 66.8484 ], [ -173.3005, 66.8443 ], [ -173.3042, 66.8417 ], [ -173.3182, 66.8411 ], [ -173.3213, 66.8359 ], [ -173.3266, 66.8339 ], [ -173.3193, 66.8307 ], [ -173.3203, 66.8255 ], [ -173.3161, 66.8234 ], [ -173.3104, 66.8229 ], [ -173.3021, 66.8187 ], [ -173.2917, 66.8187 ], [ -173.2771, 66.8146 ], [ -173.2724, 66.8182 ], [ -173.2646, 66.8208 ], [ -173.2604, 66.8187 ], [ -173.2542, 66.8208 ], [ -173.2479, 66.8208 ], [ -173.2396, 66.8167 ], [ -173.2359, 66.8172 ], [ -173.2391, 66.8193 ], [ -173.2375, 66.8208 ], [ -173.2104, 66.8208 ], [ -173.2063, 66.8229 ], [ -173.2, 66.8229 ], [ -173.1958, 66.8208 ], [ -173.1896, 66.8229 ], [ -173.1771, 66.8229 ], [ -173.1646, 66.8187 ], [ -173.1583, 66.8208 ], [ -173.1417, 66.8208 ], [ -173.1333, 66.8271 ], [ -173.1208, 66.8271 ], [ -173.1182, 66.8307 ], [ -173.1021, 66.8354 ], [ -173.0958, 66.8333 ], [ -173.0943, 66.8349 ], [ -173.0974, 66.837 ], [ -173.0896, 66.8375 ], [ -173.0818, 66.8401 ], [ -173.0849, 66.8422 ], [ -173.0838, 66.8453 ], [ -173.087, 66.8464 ], [ -173.0859, 66.8495 ], [ -173.0891, 66.8505 ], [ -173.0776, 66.8547 ], [ -173.0807, 66.8568 ], [ -173.0818, 66.8599 ], [ -173.0917, 66.8625 ], [ -173.1042, 66.8583 ], [ -173.1146, 66.8583 ], [ -173.1187, 66.8604 ], [ -173.1354, 66.8604 ], [ -173.1396, 66.8625 ], [ -173.15, 66.8625 ], [ -173.1609, 66.8682 ], [ -173.1687, 66.8688 ], [ -173.1745, 66.8714 ], [ -173.1729, 66.8729 ], [ -173.1672, 66.8734 ], [ -173.1641, 66.8786 ], [ -173.1589, 66.8797 ], [ -173.1578, 66.8828 ], [ -173.1479, 66.8833 ], [ -173.1432, 66.887 ], [ -173.138, 66.888 ], [ -173.1359, 66.8932 ], [ -173.1391, 66.8943 ], [ -173.1401, 66.8974 ], [ -173.1568, 66.9057 ], [ -173.1667, 66.9083 ], [ -173.1771, 66.9083 ], [ -173.1812, 66.9104 ], [ -173.1875, 66.9104 ], [ -173.1917, 66.9083 ], [ -173.2063, 66.9083 ], [ -173.2099, 66.9036 ], [ -173.2063, 66.9 ], [ -173.2083, 66.8979 ], [ -173.2161, 66.8984 ], [ -173.2203, 66.9005 ], [ -173.2214, 66.9036 ], [ -173.2245, 66.9057 ], [ -173.2224, 66.9078 ], [ -173.2083, 66.9125 ], [ -173.2, 66.9125 ], [ -173.1833, 66.9187 ], [ -173.175, 66.9187 ], [ -173.1609, 66.9234 ], [ -173.1646, 66.9271 ], [ -173.1609, 66.9339 ], [ -173.1641, 66.937 ], [ -173.1583, 66.9479 ], [ -173.162, 66.9505 ], [ -173.1609, 66.9537 ], [ -173.1641, 66.9557 ], [ -173.1568, 66.9599 ], [ -173.1646, 66.9604 ], [ -173.1703, 66.963 ], [ -173.1662, 66.9703 ], [ -173.1521, 66.975 ], [ -173.1438, 66.975 ], [ -173.1292, 66.9792 ], [ -173.1, 66.9792 ], [ -173.0833, 66.9854 ], [ -173.0776, 66.9859 ], [ -173.0766, 66.9891 ], [ -173.0729, 66.9917 ], [ -173.063, 66.9859 ], [ -173.0682, 66.9849 ], [ -173.0651, 66.9818 ], [ -173.0766, 66.9786 ], [ -173.0807, 66.9724 ], [ -173.0771, 66.9688 ], [ -173.0786, 66.9651 ], [ -173.0714, 66.962 ], [ -173.0714, 66.9589 ], [ -173.0745, 66.9547 ], [ -173.0672, 66.9495 ], [ -173.0682, 66.9443 ], [ -173.0625, 66.9417 ], [ -173.0505, 66.9411 ], [ -173.0516, 66.9359 ], [ -173.0484, 66.9349 ], [ -173.0495, 66.9318 ], [ -173.0464, 66.9307 ], [ -173.0474, 66.9276 ], [ -173.0443, 66.9266 ], [ -173.0417, 66.9229 ], [ -173.0271, 66.9208 ], [ -173.0229, 66.9187 ], [ -173.0167, 66.9187 ], [ -173.0104, 66.9146 ], [ -172.9979, 66.9146 ], [ -172.9937, 66.9125 ], [ -172.9833, 66.9125 ], [ -172.9792, 66.9104 ], [ -172.9646, 66.9104 ], [ -172.9604, 66.9083 ], [ -172.9542, 66.9083 ], [ -172.95, 66.9104 ], [ -172.9438, 66.9083 ], [ -172.8687, 66.9083 ], [ -172.8562, 66.9042 ], [ -172.8437, 66.9042 ], [ -172.8396, 66.9063 ], [ -172.8354, 66.9042 ], [ -172.8276, 66.9036 ], [ -172.8292, 66.9 ], [ -172.8271, 66.8979 ], [ -172.8167, 66.8979 ], [ -172.8125, 66.8958 ], [ -172.7812, 66.8958 ], [ -172.7688, 66.8896 ], [ -172.7312, 66.8896 ], [ -172.7187, 66.8854 ], [ -172.7, 66.8854 ], [ -172.6953, 66.8912 ], [ -172.6875, 66.8938 ], [ -172.6833, 66.8917 ], [ -172.6771, 66.8938 ], [ -172.6708, 66.8938 ], [ -172.6625, 66.8896 ], [ -172.6458, 66.8896 ], [ -172.6417, 66.8917 ], [ -172.6354, 66.8917 ], [ -172.6292, 66.8896 ], [ -172.625, 66.8917 ], [ -172.6109, 66.8922 ], [ -172.6068, 66.8995 ], [ -172.6167, 66.9042 ], [ -172.6245, 66.9047 ], [ -172.6224, 66.9099 ], [ -172.6109, 66.9109 ], [ -172.6141, 66.913 ], [ -172.613, 66.9161 ], [ -172.6203, 66.9193 ], [ -172.6203, 66.9224 ], [ -172.6187, 66.9229 ], [ -172.5854, 66.9229 ], [ -172.5828, 66.9193 ], [ -172.5713, 66.9141 ], [ -172.5724, 66.9109 ], [ -172.5646, 66.9104 ], [ -172.563, 66.9099 ], [ -172.5641, 66.9068 ], [ -172.5479, 66.9042 ], [ -172.5437, 66.9021 ], [ -172.5063, 66.9021 ], [ -172.5021, 66.9042 ], [ -172.4771, 66.9042 ], [ -172.4542, 66.9104 ], [ -172.4479, 66.9104 ], [ -172.4375, 66.9146 ], [ -172.425, 66.9146 ], [ -172.4167, 66.9104 ], [ -172.4104, 66.9104 ], [ -172.4089, 66.912 ], [ -172.4162, 66.913 ], [ -172.4203, 66.9161 ], [ -172.4062, 66.9167 ], [ -172.3958, 66.9208 ], [ -172.3875, 66.9208 ], [ -172.3849, 66.9245 ], [ -172.3797, 66.9255 ], [ -172.3766, 66.9287 ], [ -172.3687, 66.9313 ], [ -172.3604, 66.9313 ], [ -172.3583, 66.9333 ], [ -172.3604, 66.9354 ], [ -172.3661, 66.9359 ], [ -172.3651, 66.9391 ], [ -172.3724, 66.9422 ], [ -172.375, 66.9458 ], [ -172.3792, 66.9437 ], [ -172.3854, 66.9437 ], [ -172.3896, 66.9458 ], [ -172.4021, 66.9458 ], [ -172.4229, 66.9542 ], [ -172.4307, 66.9547 ], [ -172.4422, 66.962 ], [ -172.4516, 66.963 ], [ -172.4625, 66.9688 ], [ -172.4745, 66.9693 ], [ -172.4734, 66.9724 ], [ -172.475, 66.9729 ], [ -172.4812, 66.9729 ], [ -172.4937, 66.9771 ], [ -172.5, 66.9771 ], [ -172.5083, 66.9812 ], [ -172.5333, 66.9833 ], [ -172.5375, 66.9854 ], [ -172.5479, 66.9854 ], [ -172.5521, 66.9875 ], [ -172.5583, 66.9875 ], [ -172.5625, 66.9854 ], [ -172.5688, 66.9875 ], [ -172.6, 66.9875 ], [ -172.6042, 66.9896 ], [ -172.625, 66.9896 ], [ -172.6292, 66.9917 ], [ -172.6667, 66.9938 ], [ -172.6708, 66.9958 ], [ -172.7229, 66.9979 ], [ -172.7271, 67.0 ], [ -172.7312, 66.9979 ], [ -172.7375, 67.0 ], [ -172.7437, 67.0 ], [ -172.7479, 66.9979 ], [ -172.7667, 66.9979 ], [ -172.7708, 66.9958 ], [ -172.7854, 67.0 ], [ -172.8104, 67.0021 ], [ -172.825, 67.0062 ], [ -172.8396, 67.0062 ], [ -172.8479, 67.0104 ], [ -172.8583, 67.0104 ], [ -172.8667, 67.0146 ], [ -172.8771, 67.0146 ], [ -172.8813, 67.0167 ], [ -172.8875, 67.0167 ], [ -172.8917, 67.0188 ], [ -172.9021, 67.0188 ], [ -172.9062, 67.0208 ], [ -172.9604, 67.0208 ], [ -172.9667, 67.0167 ], [ -172.9792, 67.0167 ], [ -172.9896, 67.0125 ], [ -172.9958, 67.0146 ], [ -173.0021, 67.0125 ], [ -173.0104, 67.0167 ], [ -173.0208, 67.0167 ], [ -173.0266, 67.0193 ], [ -173.0255, 67.0224 ], [ -173.0286, 67.0245 ], [ -173.0234, 67.0255 ], [ -173.0213, 67.0307 ], [ -173.0375, 67.0333 ], [ -173.0396, 67.0354 ], [ -173.037, 67.0391 ], [ -173.0318, 67.0401 ], [ -173.0292, 67.0437 ], [ -173.0229, 67.0417 ], [ -173.0167, 67.0437 ], [ -173.0104, 67.0437 ], [ -173.0063, 67.0417 ], [ -172.9792, 67.0417 ], [ -172.975, 67.0396 ], [ -172.9646, 67.0396 ], [ -172.9604, 67.0375 ], [ -172.9458, 67.0375 ], [ -172.9417, 67.0333 ], [ -172.9375, 67.0354 ], [ -172.9083, 67.0354 ], [ -172.9068, 67.0339 ], [ -172.912, 67.0328 ], [ -172.9141, 67.0297 ], [ -172.9062, 67.0292 ], [ -172.9021, 67.0271 ], [ -172.8839, 67.0276 ], [ -172.8896, 67.0313 ], [ -172.9016, 67.0318 ], [ -172.9, 67.0333 ], [ -172.8771, 67.0333 ], [ -172.8729, 67.0313 ], [ -172.8609, 67.0307 ], [ -172.8625, 67.0292 ], [ -172.8708, 67.0292 ], [ -172.8724, 67.0276 ], [ -172.8625, 67.0229 ], [ -172.8521, 67.0229 ], [ -172.8479, 67.0208 ], [ -172.8313, 67.0208 ], [ -172.8208, 67.025 ], [ -172.8125, 67.025 ], [ -172.8089, 67.0276 ], [ -172.8104, 67.0292 ], [ -172.8349, 67.0297 ], [ -172.8333, 67.0313 ], [ -172.8146, 67.0313 ], [ -172.8104, 67.0333 ], [ -172.8042, 67.0333 ], [ -172.7984, 67.0307 ], [ -172.7995, 67.0255 ], [ -172.7917, 67.025 ], [ -172.7792, 67.0208 ], [ -172.7688, 67.0208 ], [ -172.7646, 67.0188 ], [ -172.7583, 67.0188 ], [ -172.7458, 67.0125 ], [ -172.725, 67.0125 ], [ -172.7208, 67.0104 ], [ -172.7104, 67.0104 ], [ -172.7063, 67.0083 ], [ -172.7, 67.0083 ], [ -172.6901, 67.0057 ], [ -172.6833, 67.0021 ], [ -172.6729, 67.0021 ], [ -172.6583, 66.9979 ], [ -172.6271, 66.9979 ], [ -172.6125, 66.9938 ], [ -172.5854, 66.9938 ], [ -172.5813, 66.9917 ], [ -172.5542, 66.9917 ], [ -172.5479, 66.9938 ], [ -172.5437, 66.9917 ], [ -172.5188, 66.9896 ], [ -172.5146, 66.9875 ], [ -172.4792, 66.9854 ], [ -172.475, 66.9833 ], [ -172.4583, 66.9833 ], [ -172.4542, 66.9812 ], [ -172.4463, 66.9849 ], [ -172.4521, 66.9854 ], [ -172.4588, 66.9891 ], [ -172.4667, 66.9896 ], [ -172.4708, 66.9917 ], [ -172.4958, 66.9938 ], [ -172.5, 66.9958 ], [ -172.5104, 66.9958 ], [ -172.5203, 67.0016 ], [ -172.5063, 67.0021 ], [ -172.5021, 67.0 ], [ -172.4917, 67.0 ], [ -172.4875, 66.9979 ], [ -172.4771, 66.9979 ], [ -172.4729, 66.9958 ], [ -172.4458, 66.9958 ], [ -172.4333, 66.9917 ], [ -172.4229, 66.9917 ], [ -172.4187, 66.9896 ], [ -172.3901, 66.987 ], [ -172.4062, 66.9812 ], [ -172.4125, 66.9812 ], [ -172.4167, 66.9833 ], [ -172.4266, 66.9807 ], [ -172.425, 66.9792 ], [ -172.4083, 66.9792 ], [ -172.3938, 66.975 ], [ -172.3833, 66.975 ], [ -172.3792, 66.9729 ], [ -172.3583, 66.9729 ], [ -172.3542, 66.9708 ], [ -172.3479, 66.9729 ], [ -172.3396, 66.9688 ], [ -172.3229, 66.9688 ], [ -172.3104, 66.9646 ], [ -172.275, 66.9625 ], [ -172.2708, 66.9604 ], [ -172.2604, 66.9604 ], [ -172.2563, 66.9583 ], [ -172.2229, 66.9583 ], [ -172.2187, 66.9563 ], [ -172.1979, 66.9563 ], [ -172.1938, 66.9542 ], [ -172.1771, 66.9542 ], [ -172.1729, 66.9521 ], [ -172.1687, 66.9542 ], [ -172.1521, 66.9542 ], [ -172.1479, 66.9521 ], [ -172.113, 66.9526 ], [ -172.1146, 66.9542 ], [ -172.125, 66.9542 ], [ -172.1292, 66.9563 ], [ -172.1411, 66.9568 ], [ -172.1396, 66.9583 ], [ -172.1083, 66.9563 ], [ -172.1042, 66.9542 ], [ -172.0875, 66.9542 ], [ -172.0833, 66.9521 ], [ -172.0625, 66.9521 ], [ -172.0609, 66.9505 ], [ -172.0724, 66.9495 ], [ -172.0693, 66.9474 ], [ -172.0682, 66.9443 ], [ -172.0625, 66.9417 ], [ -172.0583, 66.9437 ], [ -172.0437, 66.9437 ], [ -172.0396, 66.9458 ], [ -172.0229, 66.9458 ], [ -172.0125, 66.95 ], [ -172.0063, 66.9479 ], [ -171.9875, 66.95 ], [ -171.9807, 66.9578 ], [ -171.9729, 66.9583 ], [ -171.9687, 66.9604 ], [ -171.9438, 66.9604 ], [ -171.9297, 66.9651 ], [ -171.9255, 66.9724 ], [ -171.9333, 66.9729 ], [ -171.9349, 66.9745 ], [ -171.925, 66.9771 ], [ -171.9167, 66.9729 ], [ -171.9104, 66.9729 ], [ -171.8963, 66.9682 ], [ -171.8995, 66.9609 ], [ -171.8875, 66.9604 ], [ -171.8792, 66.9563 ], [ -171.8687, 66.9563 ], [ -171.863, 66.9537 ], [ -171.8646, 66.9521 ], [ -171.8917, 66.9521 ], [ -171.8979, 66.9542 ], [ -171.9021, 66.9521 ], [ -171.9099, 66.9516 ], [ -171.912, 66.9495 ], [ -171.9104, 66.9479 ], [ -171.8896, 66.9479 ], [ -171.8854, 66.9458 ], [ -171.8687, 66.9479 ], [ -171.8646, 66.9458 ], [ -171.8583, 66.9458 ], [ -171.8568, 66.9464 ], [ -171.8578, 66.9495 ], [ -171.85, 66.9521 ], [ -171.8396, 66.9521 ], [ -171.8354, 66.95 ], [ -171.825, 66.95 ], [ -171.8104, 66.9458 ], [ -171.7943, 66.9505 ], [ -171.7917, 66.9542 ], [ -171.7833, 66.9542 ], [ -171.7792, 66.9521 ], [ -171.7583, 66.9521 ], [ -171.7542, 66.95 ], [ -171.7437, 66.95 ], [ -171.7396, 66.9479 ], [ -171.725, 66.9479 ], [ -171.7208, 66.9458 ], [ -171.7146, 66.9479 ], [ -171.7104, 66.9437 ], [ -171.7, 66.9437 ], [ -171.6875, 66.9396 ], [ -171.6771, 66.9396 ], [ -171.6729, 66.9375 ], [ -171.6667, 66.9375 ], [ -171.6557, 66.9318 ], [ -171.65, 66.9313 ], [ -171.6443, 66.9287 ], [ -171.6411, 66.9213 ], [ -171.638, 66.9203 ], [ -171.6391, 66.9172 ], [ -171.6318, 66.9141 ], [ -171.6328, 66.9109 ], [ -171.6292, 66.9083 ], [ -171.6297, 66.9068 ], [ -171.6375, 66.9042 ], [ -171.6458, 66.9042 ], [ -171.6682, 66.8974 ], [ -171.6734, 66.888 ], [ -171.6875, 66.8875 ], [ -171.6922, 66.8839 ], [ -171.7036, 66.8807 ], [ -171.7016, 66.8755 ], [ -171.7, 66.875 ], [ -171.6953, 66.8786 ], [ -171.6901, 66.8786 ], [ -171.6911, 66.8755 ], [ -171.6839, 66.8724 ], [ -171.6849, 66.8693 ], [ -171.6776, 66.8662 ], [ -171.6797, 66.863 ], [ -171.6849, 66.862 ], [ -171.6776, 66.8578 ], [ -171.6786, 66.8547 ], [ -171.6578, 66.8443 ], [ -171.6484, 66.8432 ], [ -171.6495, 66.8401 ], [ -171.6422, 66.8391 ], [ -171.6354, 66.8354 ], [ -171.6276, 66.8349 ], [ -171.6286, 66.8318 ], [ -171.6193, 66.8307 ], [ -171.6161, 66.8255 ], [ -171.6089, 66.8245 ], [ -171.5937, 66.8167 ], [ -171.5859, 66.8161 ], [ -171.587, 66.813 ], [ -171.5797, 66.812 ], [ -171.5729, 66.8083 ], [ -171.5625, 66.8083 ], [ -171.5542, 66.8042 ], [ -171.5479, 66.8042 ], [ -171.5297, 66.7995 ], [ -171.5229, 66.7958 ], [ -171.5083, 66.7937 ], [ -171.5, 66.7896 ], [ -171.4922, 66.7891 ], [ -171.4937, 66.7875 ], [ -171.4995, 66.787 ], [ -171.4979, 66.7854 ], [ -171.4792, 66.7875 ], [ -171.4708, 66.7833 ], [ -171.4667, 66.7833 ], [ -171.4625, 66.7813 ], [ -171.4521, 66.7813 ], [ -171.4453, 66.7776 ], [ -171.4354, 66.775 ], [ -171.425, 66.775 ], [ -171.4125, 66.7688 ], [ -171.3854, 66.7688 ], [ -171.3813, 66.7667 ], [ -171.3667, 66.7667 ], [ -171.3625, 66.7646 ], [ -171.3521, 66.7646 ], [ -171.3422, 66.7599 ], [ -171.3432, 66.7568 ], [ -171.3401, 66.7557 ], [ -171.3411, 66.7526 ], [ -171.3338, 66.7474 ], [ -171.338, 66.7422 ], [ -171.3458, 66.7396 ], [ -171.3646, 66.7375 ], [ -171.375, 66.7333 ], [ -171.3807, 66.7328 ], [ -171.3776, 66.7307 ], [ -171.3776, 66.7276 ], [ -171.3818, 66.7234 ], [ -171.387, 66.7224 ], [ -171.3838, 66.7193 ], [ -171.3891, 66.7172 ], [ -171.3859, 66.7161 ], [ -171.387, 66.713 ], [ -171.3838, 66.712 ], [ -171.3849, 66.7089 ], [ -171.3818, 66.7078 ], [ -171.3807, 66.7047 ], [ -171.3734, 66.7016 ], [ -171.375, 66.7 ], [ -171.3807, 66.6995 ], [ -171.3776, 66.6974 ], [ -171.3745, 66.6922 ], [ -171.3714, 66.6911 ], [ -171.3766, 66.688 ], [ -171.3693, 66.6849 ], [ -171.3703, 66.6818 ], [ -171.363, 66.6787 ], [ -171.3641, 66.6755 ], [ -171.3568, 66.6724 ], [ -171.3557, 66.6693 ], [ -171.3484, 66.6682 ], [ -171.3495, 66.6651 ], [ -171.3417, 66.6646 ], [ -171.3359, 66.662 ], [ -171.337, 66.6589 ], [ -171.3297, 66.6578 ], [ -171.3229, 66.6542 ], [ -171.3068, 66.6516 ], [ -171.2984, 66.6474 ], [ -171.2995, 66.6443 ], [ -171.2979, 66.6438 ], [ -171.2875, 66.6438 ], [ -171.2833, 66.6417 ], [ -171.2755, 66.6412 ], [ -171.2708, 66.6375 ], [ -171.263, 66.637 ], [ -171.2391, 66.6234 ], [ -171.2318, 66.6224 ], [ -171.2208, 66.6167 ], [ -171.213, 66.6162 ], [ -171.2141, 66.6131 ], [ -171.2068, 66.612 ], [ -171.2, 66.6083 ], [ -171.1922, 66.6078 ], [ -171.1932, 66.6047 ], [ -171.1875, 66.6021 ], [ -171.1818, 66.6016 ], [ -171.1786, 66.5964 ], [ -171.1667, 66.5958 ], [ -171.1641, 66.5922 ], [ -171.1484, 66.5891 ], [ -171.1401, 66.5849 ], [ -171.137, 66.5797 ], [ -171.1089, 66.5745 ], [ -171.1099, 66.5714 ], [ -171.1057, 66.5693 ], [ -171.0875, 66.5646 ], [ -171.0797, 66.5641 ], [ -171.0807, 66.5609 ], [ -171.0792, 66.5604 ], [ -171.0729, 66.5604 ], [ -171.0604, 66.5563 ], [ -171.05, 66.5563 ], [ -171.0375, 66.5521 ], [ -171.0312, 66.5521 ], [ -171.0188, 66.5479 ], [ -171.0146, 66.5479 ], [ -171.0063, 66.5437 ], [ -170.9917, 66.5417 ], [ -170.9714, 66.5307 ], [ -170.9724, 66.5276 ], [ -170.9667, 66.525 ], [ -170.9417, 66.5229 ], [ -170.9318, 66.5203 ], [ -170.9167, 66.5125 ], [ -170.9062, 66.5125 ], [ -170.8963, 66.5099 ], [ -170.8797, 66.5016 ], [ -170.8807, 66.4984 ], [ -170.8708, 66.4938 ], [ -170.8651, 66.4932 ], [ -170.8661, 66.4901 ], [ -170.8578, 66.4859 ], [ -170.8505, 66.4849 ], [ -170.8297, 66.4745 ], [ -170.8224, 66.4693 ], [ -170.8188, 66.4688 ], [ -170.8146, 66.4708 ], [ -170.7937, 66.4625 ], [ -170.7859, 66.462 ], [ -170.787, 66.4589 ], [ -170.7812, 66.4563 ], [ -170.7708, 66.4563 ], [ -170.7609, 66.4537 ], [ -170.762, 66.4505 ], [ -170.7563, 66.4479 ], [ -170.7505, 66.4474 ], [ -170.7411, 66.4422 ], [ -170.7068, 66.4349 ], [ -170.7057, 66.4318 ], [ -170.7016, 66.4297 ], [ -170.6958, 66.4292 ], [ -170.687, 66.4234 ], [ -170.6792, 66.4229 ], [ -170.6734, 66.4203 ], [ -170.6745, 66.4172 ], [ -170.6568, 66.4078 ], [ -170.6578, 66.4047 ], [ -170.638, 66.3953 ], [ -170.6349, 66.3922 ], [ -170.6266, 66.388 ], [ -170.6193, 66.387 ], [ -170.6182, 66.3839 ], [ -170.6109, 66.3828 ], [ -170.6125, 66.3792 ], [ -170.6099, 66.3734 ], [ -170.6026, 66.3724 ], [ -170.5943, 66.3682 ], [ -170.5953, 66.3651 ], [ -170.588, 66.3641 ], [ -170.5797, 66.3599 ], [ -170.5813, 66.3562 ], [ -170.5786, 66.3505 ], [ -170.5713, 66.3495 ], [ -170.5646, 66.3458 ], [ -170.5542, 66.3458 ], [ -170.5417, 66.3417 ], [ -170.5375, 66.3417 ], [ -170.5307, 66.338 ], [ -170.5208, 66.3354 ], [ -170.5042, 66.3354 ], [ -170.4917, 66.3313 ], [ -170.4812, 66.3313 ], [ -170.475, 66.3271 ], [ -170.4646, 66.3271 ], [ -170.4578, 66.3234 ], [ -170.4479, 66.3208 ], [ -170.4312, 66.3208 ], [ -170.4271, 66.3187 ], [ -170.4187, 66.3187 ], [ -170.4042, 66.3146 ], [ -170.3922, 66.3141 ], [ -170.3938, 66.3125 ], [ -170.4125, 66.3125 ], [ -170.4229, 66.3083 ], [ -170.4312, 66.3083 ], [ -170.4516, 66.3016 ], [ -170.4578, 66.2953 ], [ -170.4547, 66.2932 ], [ -170.4547, 66.2901 ], [ -170.462, 66.2891 ], [ -170.4588, 66.2859 ], [ -170.4641, 66.2849 ], [ -170.4687, 66.2813 ], [ -170.4766, 66.2818 ], [ -170.4693, 66.2922 ], [ -170.4724, 66.2943 ], [ -170.4714, 66.2974 ], [ -170.4896, 66.2979 ], [ -170.4932, 66.2953 ], [ -170.4901, 66.2932 ], [ -170.4922, 66.2901 ], [ -170.5125, 66.2833 ], [ -170.5208, 66.2833 ], [ -170.5255, 66.2797 ], [ -170.5307, 66.2786 ], [ -170.5266, 66.2755 ], [ -170.5208, 66.275 ], [ -170.5068, 66.2693 ], [ -170.512, 66.2682 ], [ -170.5151, 66.263 ], [ -170.525, 66.2625 ], [ -170.5286, 66.2599 ], [ -170.5286, 66.2568 ], [ -170.5255, 66.2557 ], [ -170.5307, 66.2495 ], [ -170.5276, 66.2474 ], [ -170.5276, 66.2443 ], [ -170.5339, 66.2359 ], [ -170.5417, 66.2333 ], [ -170.5542, 66.2333 ], [ -170.563, 66.2391 ], [ -170.5724, 66.2401 ], [ -170.5813, 66.2458 ], [ -170.5891, 66.2453 ], [ -170.5937, 66.2417 ], [ -170.6104, 66.2417 ], [ -170.6146, 66.2438 ], [ -170.625, 66.2438 ], [ -170.6375, 66.2479 ], [ -170.6411, 66.2453 ], [ -170.6354, 66.2417 ], [ -170.625, 66.2417 ], [ -170.6068, 66.237 ], [ -170.6, 66.2333 ], [ -170.5943, 66.2328 ], [ -170.5943, 66.2297 ], [ -170.5958, 66.2292 ], [ -170.6062, 66.2333 ], [ -170.6245, 66.2328 ], [ -170.6187, 66.2292 ], [ -170.6109, 66.2307 ], [ -170.612, 66.2276 ], [ -170.6089, 66.2266 ], [ -170.6099, 66.2214 ], [ -170.5833, 66.2208 ], [ -170.575, 66.2167 ], [ -170.5688, 66.2167 ], [ -170.562, 66.2245 ], [ -170.55, 66.2229 ], [ -170.5437, 66.225 ], [ -170.5422, 66.2234 ], [ -170.5453, 66.2193 ], [ -170.5354, 66.2146 ], [ -170.5318, 66.2172 ], [ -170.5349, 66.2193 ], [ -170.5328, 66.2224 ], [ -170.5172, 66.2234 ], [ -170.512, 66.2286 ], [ -170.5005, 66.2318 ], [ -170.4953, 66.2391 ], [ -170.4839, 66.2401 ], [ -170.487, 66.2422 ], [ -170.487, 66.2453 ], [ -170.4849, 66.2474 ], [ -170.4797, 66.2484 ], [ -170.4766, 66.2516 ], [ -170.4667, 66.2521 ], [ -170.4588, 66.2547 ], [ -170.4542, 66.2604 ], [ -170.4417, 66.2604 ], [ -170.4255, 66.2651 ], [ -170.4266, 66.2724 ], [ -170.4187, 66.275 ], [ -170.4104, 66.275 ], [ -170.4062, 66.2771 ], [ -170.3938, 66.2771 ], [ -170.3891, 66.2734 ], [ -170.3838, 66.2734 ], [ -170.3813, 66.2771 ], [ -170.387, 66.2818 ], [ -170.387, 66.2849 ], [ -170.3818, 66.2859 ], [ -170.3786, 66.2891 ], [ -170.3708, 66.2917 ], [ -170.3542, 66.2896 ], [ -170.3526, 66.2891 ], [ -170.3537, 66.2859 ], [ -170.3417, 66.2833 ], [ -170.3391, 66.287 ], [ -170.3354, 66.2875 ], [ -170.3313, 66.2854 ], [ -170.3245, 66.2911 ], [ -170.3167, 66.2937 ], [ -170.3125, 66.2917 ], [ -170.2896, 66.2917 ], [ -170.2854, 66.2937 ], [ -170.2776, 66.2943 ], [ -170.2786, 66.2974 ], [ -170.2771, 66.2979 ], [ -170.2729, 66.2958 ], [ -170.2479, 66.2937 ], [ -170.2464, 66.2922 ], [ -170.2495, 66.288 ], [ -170.2437, 66.2854 ], [ -170.213, 66.2828 ], [ -170.2141, 66.2797 ], [ -170.2057, 66.2755 ], [ -170.1984, 66.2745 ], [ -170.1953, 66.2693 ], [ -170.1896, 66.2667 ], [ -170.1839, 66.2662 ], [ -170.1828, 66.263 ], [ -170.1651, 66.2536 ], [ -170.1682, 66.2484 ], [ -170.1609, 66.2453 ], [ -170.162, 66.2422 ], [ -170.1547, 66.2412 ], [ -170.1557, 66.2359 ], [ -170.1484, 66.2328 ], [ -170.1453, 66.2297 ], [ -170.1375, 66.2292 ], [ -170.1313, 66.225 ], [ -170.1234, 66.2245 ], [ -170.1266, 66.2193 ], [ -170.1208, 66.2167 ], [ -170.1151, 66.2161 ], [ -170.1151, 66.213 ], [ -170.1182, 66.2089 ], [ -170.1109, 66.2057 ], [ -170.1151, 66.1984 ], [ -170.1208, 66.1979 ], [ -170.1224, 66.1964 ], [ -170.1193, 66.1953 ], [ -170.1161, 66.1901 ], [ -170.1047, 66.1849 ], [ -170.1057, 66.1818 ], [ -170.1026, 66.1807 ], [ -170.1042, 66.1792 ], [ -170.1125, 66.1792 ], [ -170.1167, 66.1771 ], [ -170.1229, 66.1792 ], [ -170.1307, 66.1766 ], [ -170.1375, 66.1708 ], [ -170.15, 66.1708 ], [ -170.1542, 66.1687 ], [ -170.1708, 66.1687 ], [ -170.1797, 66.1745 ], [ -170.187, 66.1755 ], [ -170.2021, 66.1833 ], [ -170.2083, 66.1813 ], [ -170.2125, 66.1833 ], [ -170.2208, 66.1833 ], [ -170.2271, 66.1854 ], [ -170.2312, 66.1813 ], [ -170.2255, 66.1755 ], [ -170.2349, 66.1766 ], [ -170.237, 66.1713 ], [ -170.2339, 66.1693 ], [ -170.238, 66.163 ], [ -170.2495, 66.162 ], [ -170.2391, 66.1547 ], [ -170.2312, 66.1542 ], [ -170.225, 66.15 ], [ -170.1958, 66.1458 ], [ -170.1896, 66.1417 ], [ -170.1687, 66.1417 ], [ -170.1672, 66.1412 ], [ -170.1682, 66.138 ], [ -170.1667, 66.1375 ], [ -170.1438, 66.1375 ], [ -170.1359, 66.1401 ], [ -170.1328, 66.1453 ], [ -170.1229, 66.1458 ], [ -170.1151, 66.1484 ], [ -170.1208, 66.1542 ], [ -170.1167, 66.1583 ], [ -170.1042, 66.1583 ], [ -170.1, 66.1604 ], [ -170.0833, 66.1604 ], [ -170.0792, 66.1583 ], [ -170.0672, 66.1589 ], [ -170.0688, 66.1604 ], [ -170.0813, 66.1625 ], [ -170.0854, 66.1646 ], [ -170.1062, 66.1646 ], [ -170.112, 66.1672 ], [ -170.1099, 66.1703 ], [ -170.1047, 66.1713 ], [ -170.1016, 66.1745 ], [ -170.0938, 66.1771 ], [ -170.0875, 66.175 ], [ -170.0813, 66.1771 ], [ -170.0724, 66.1713 ], [ -170.0625, 66.1687 ], [ -170.0458, 66.1687 ], [ -170.0417, 66.1667 ], [ -170.0354, 66.1687 ], [ -170.0292, 66.1625 ], [ -170.0167, 66.1604 ], [ -170.0042, 66.1563 ], [ -169.9979, 66.1563 ], [ -169.9937, 66.1542 ], [ -169.9708, 66.1521 ], [ -169.9693, 66.1516 ], [ -169.9703, 66.1484 ], [ -169.9687, 66.1479 ], [ -169.9625, 66.1479 ], [ -169.9583, 66.15 ], [ -169.9354, 66.15 ], [ -169.9229, 66.1458 ], [ -169.8979, 66.1458 ], [ -169.8938, 66.1438 ], [ -169.8729, 66.1438 ], [ -169.8687, 66.1417 ], [ -169.8646, 66.1438 ], [ -169.8464, 66.1432 ], [ -169.85, 66.1396 ], [ -169.8771, 66.1396 ], [ -169.887, 66.1349 ], [ -169.8854, 66.1333 ], [ -169.8813, 66.1354 ], [ -169.8734, 66.1349 ], [ -169.8708, 66.1292 ], [ -169.8724, 66.1255 ], [ -169.8667, 66.1229 ], [ -169.8604, 66.1229 ], [ -169.8526, 66.1255 ], [ -169.8521, 66.1271 ], [ -169.8578, 66.1328 ], [ -169.8359, 66.1339 ], [ -169.8391, 66.1359 ], [ -169.838, 66.1412 ], [ -169.8411, 66.1422 ], [ -169.8391, 66.1453 ], [ -169.8292, 66.1458 ], [ -169.8213, 66.1484 ], [ -169.8182, 66.1516 ], [ -169.8068, 66.1547 ], [ -169.8021, 66.1583 ], [ -169.7854, 66.1583 ], [ -169.7812, 66.1563 ], [ -169.7729, 66.1563 ], [ -169.7688, 66.1542 ], [ -169.7583, 66.1542 ], [ -169.7542, 66.1521 ], [ -169.7437, 66.1521 ], [ -169.7396, 66.15 ], [ -169.7354, 66.15 ], [ -169.7245, 66.1443 ], [ -169.7172, 66.1432 ], [ -169.7182, 66.1401 ], [ -169.7063, 66.1396 ], [ -169.6963, 66.137 ], [ -169.6974, 66.1339 ], [ -169.6917, 66.1312 ], [ -169.6797, 66.1307 ], [ -169.6766, 66.1255 ], [ -169.6734, 66.1245 ], [ -169.6786, 66.1172 ], [ -169.6755, 66.1162 ], [ -169.6766, 66.113 ], [ -169.6651, 66.1078 ], [ -169.6682, 66.1036 ], [ -169.6651, 66.1005 ], [ -169.6682, 66.0964 ], [ -169.6651, 66.0953 ], [ -169.662, 66.088 ], [ -169.6547, 66.087 ], [ -169.6583, 66.0813 ], [ -169.6557, 66.0776 ], [ -169.6526, 66.0766 ], [ -169.6542, 66.075 ], [ -169.6641, 66.0745 ], [ -169.6609, 66.0714 ], [ -169.6662, 66.0703 ], [ -169.6687, 66.0667 ], [ -169.6651, 66.0599 ], [ -169.6651, 66.0568 ], [ -169.6672, 66.0547 ], [ -169.6724, 66.0537 ], [ -169.6714, 66.0505 ], [ -169.6766, 66.0495 ], [ -169.6734, 66.0463 ], [ -169.687, 66.0411 ], [ -169.6943, 66.0318 ], [ -169.7161, 66.0307 ], [ -169.7146, 66.0292 ], [ -169.7068, 66.0286 ], [ -169.7047, 66.0266 ], [ -169.7047, 66.0234 ], [ -169.7099, 66.0182 ], [ -169.7068, 66.0151 ], [ -169.7203, 66.0099 ], [ -169.7172, 66.0068 ], [ -169.7224, 66.0057 ], [ -169.7271, 66.0021 ], [ -169.7333, 66.0042 ], [ -169.7375, 66.0021 ], [ -169.7437, 66.0042 ], [ -169.7479, 66.0021 ], [ -169.7646, 66.0021 ], [ -169.7708, 66.0 ], [ -169.775, 66.0021 ], [ -169.7896, 66.0021 ], [ -169.7937, 66.0042 ], [ -169.8, 66.0021 ], [ -169.8125, 66.0062 ], [ -169.8271, 66.0062 ], [ -169.8318, 66.012 ], [ -169.8453, 66.013 ], [ -169.8495, 66.0151 ], [ -169.8526, 66.0203 ], [ -169.8599, 66.0214 ], [ -169.8589, 66.0245 ], [ -169.862, 66.0255 ], [ -169.8609, 66.0286 ], [ -169.8641, 66.0297 ], [ -169.863, 66.0328 ], [ -169.8724, 66.0339 ], [ -169.8797, 66.0391 ], [ -169.8854, 66.0396 ], [ -169.8938, 66.0437 ], [ -169.9104, 66.0437 ], [ -169.9146, 66.0417 ], [ -169.9521, 66.0417 ], [ -169.9563, 66.0437 ], [ -169.9708, 66.0437 ], [ -169.975, 66.0417 ], [ -169.9937, 66.0417 ], [ -169.9979, 66.0396 ], [ -170.0083, 66.0396 ], [ -170.0099, 66.0401 ], [ -170.0099, 66.0432 ], [ -170.0042, 66.0437 ], [ -170.0026, 66.0453 ], [ -170.0104, 66.0458 ], [ -170.0203, 66.0432 ], [ -170.0224, 66.0391 ], [ -170.0208, 66.0375 ], [ -170.0151, 66.037 ], [ -170.0151, 66.0339 ], [ -170.0167, 66.0333 ], [ -170.0208, 66.0333 ], [ -170.025, 66.0313 ], [ -170.0375, 66.0313 ], [ -170.0417, 66.0292 ], [ -170.0542, 66.0292 ], [ -170.0589, 66.0255 ], [ -170.0667, 66.0229 ], [ -170.0729, 66.0229 ], [ -170.0771, 66.0208 ], [ -170.0896, 66.0208 ], [ -170.1125, 66.0146 ], [ -170.125, 66.0146 ], [ -170.1354, 66.0104 ], [ -170.15, 66.0083 ], [ -170.1687, 66.0021 ], [ -170.1745, 66.0036 ], [ -170.1849, 65.9912 ], [ -170.1818, 65.988 ], [ -170.187, 65.987 ], [ -170.1911, 65.9828 ], [ -170.188, 65.9797 ], [ -170.1974, 65.9786 ], [ -170.2021, 65.975 ], [ -170.2104, 65.975 ], [ -170.2208, 65.9708 ], [ -170.2287, 65.9703 ], [ -170.2349, 65.9641 ], [ -170.2359, 65.9609 ], [ -170.2516, 65.9599 ], [ -170.2568, 65.9547 ], [ -170.262, 65.9537 ], [ -170.2588, 65.9516 ], [ -170.2604, 65.95 ], [ -170.2833, 65.95 ], [ -170.2875, 65.9479 ], [ -170.3062, 65.9479 ], [ -170.3104, 65.9437 ], [ -170.3167, 65.9458 ], [ -170.3229, 65.9458 ], [ -170.3271, 65.9417 ], [ -170.3354, 65.9417 ], [ -170.3432, 65.9391 ], [ -170.3479, 65.9354 ], [ -170.3667, 65.9333 ], [ -170.3708, 65.9313 ], [ -170.3771, 65.9333 ], [ -170.3813, 65.9313 ], [ -170.3979, 65.9313 ], [ -170.4036, 65.9328 ], [ -170.4016, 65.9297 ], [ -170.3984, 65.9287 ], [ -170.3984, 65.9255 ], [ -170.4005, 65.9234 ], [ -170.4057, 65.9224 ], [ -170.4109, 65.9172 ], [ -170.4162, 65.9161 ], [ -170.4193, 65.913 ], [ -170.437, 65.9078 ], [ -170.4484, 65.9005 ], [ -170.4703, 65.8953 ], [ -170.4693, 65.8922 ], [ -170.4745, 65.8912 ], [ -170.4755, 65.888 ], [ -170.4891, 65.8849 ], [ -170.4859, 65.8818 ], [ -170.4911, 65.8807 ], [ -170.5005, 65.8693 ], [ -170.5083, 65.8688 ], [ -170.537, 65.8599 ], [ -170.5401, 65.8547 ], [ -170.5453, 65.8536 ], [ -170.5484, 65.8505 ], [ -170.5536, 65.8495 ], [ -170.5583, 65.8458 ], [ -170.587, 65.8453 ], [ -170.5911, 65.8401 ], [ -170.5854, 65.8375 ], [ -170.5583, 65.8375 ], [ -170.5542, 65.8396 ], [ -170.5417, 65.8396 ], [ -170.5375, 65.8417 ], [ -170.5271, 65.8417 ], [ -170.5208, 65.8396 ], [ -170.5193, 65.8401 ], [ -170.5193, 65.8432 ], [ -170.5292, 65.8438 ], [ -170.5349, 65.8474 ], [ -170.5312, 65.85 ], [ -170.525, 65.85 ], [ -170.5188, 65.8521 ], [ -170.5141, 65.8484 ], [ -170.5047, 65.8474 ], [ -170.5057, 65.8443 ], [ -170.4984, 65.8432 ], [ -170.4901, 65.8391 ], [ -170.4932, 65.8339 ], [ -170.4839, 65.8287 ], [ -170.4849, 65.8234 ], [ -170.4776, 65.8224 ], [ -170.4729, 65.8125 ], [ -170.4745, 65.8089 ], [ -170.4714, 65.8078 ], [ -170.4755, 65.8005 ], [ -170.4828, 65.7995 ], [ -170.4854, 65.7958 ], [ -170.4953, 65.7953 ], [ -170.4984, 65.7922 ], [ -170.5036, 65.7911 ], [ -170.5068, 65.7859 ], [ -170.5182, 65.7828 ], [ -170.5307, 65.7662 ], [ -170.5292, 65.7646 ], [ -170.5172, 65.7641 ], [ -170.5224, 65.7609 ], [ -170.5188, 65.7583 ], [ -170.513, 65.7578 ], [ -170.5245, 65.7536 ], [ -170.5255, 65.7505 ], [ -170.5307, 65.7453 ], [ -170.5286, 65.738 ], [ -170.5255, 65.737 ], [ -170.5266, 65.7338 ], [ -170.5193, 65.7328 ], [ -170.5203, 65.7297 ], [ -170.5188, 65.7292 ], [ -170.5125, 65.7312 ], [ -170.5068, 65.7286 ], [ -170.5078, 65.7255 ], [ -170.5047, 65.7245 ], [ -170.5057, 65.7214 ], [ -170.5026, 65.7203 ], [ -170.5047, 65.7151 ], [ -170.5088, 65.7109 ], [ -170.5141, 65.7099 ], [ -170.5125, 65.7083 ], [ -170.5068, 65.7078 ], [ -170.5078, 65.7047 ], [ -170.5042, 65.7021 ], [ -170.5047, 65.7005 ], [ -170.5104, 65.7 ], [ -170.512, 65.6984 ], [ -170.5026, 65.6974 ], [ -170.5047, 65.6943 ], [ -170.5099, 65.6932 ], [ -170.5141, 65.6891 ], [ -170.5141, 65.6859 ], [ -170.5099, 65.6776 ], [ -170.4984, 65.6766 ], [ -170.4964, 65.6724 ], [ -170.5047, 65.6672 ], [ -170.5099, 65.6661 ], [ -170.5099, 65.663 ], [ -170.5047, 65.6599 ], [ -170.5063, 65.6563 ], [ -170.5047, 65.6505 ], [ -170.525, 65.65 ], [ -170.5328, 65.6474 ], [ -170.537, 65.6412 ], [ -170.5339, 65.638 ], [ -170.5443, 65.6234 ], [ -170.5578, 65.6182 ], [ -170.5609, 65.613 ], [ -170.5745, 65.6099 ], [ -170.5755, 65.6068 ], [ -170.5833, 65.6042 ], [ -170.6021, 65.6042 ], [ -170.6167, 65.6 ], [ -170.6229, 65.6 ], [ -170.6292, 65.6021 ], [ -170.6333, 65.6 ], [ -170.6375, 65.6021 ], [ -170.6562, 65.6021 ], [ -170.663, 65.6057 ], [ -170.6729, 65.6083 ], [ -170.6896, 65.6083 ], [ -170.6938, 65.6062 ], [ -170.7333, 65.6083 ], [ -170.7375, 65.6104 ], [ -170.7604, 65.6125 ], [ -170.7688, 65.6167 ], [ -170.7729, 65.6167 ], [ -170.7771, 65.6188 ], [ -170.7875, 65.6188 ], [ -170.7917, 65.6208 ], [ -170.8417, 65.6208 ], [ -170.8516, 65.6245 ], [ -170.8479, 65.6271 ], [ -170.8401, 65.6276 ], [ -170.8359, 65.6318 ], [ -170.8359, 65.6349 ], [ -170.8464, 65.6412 ], [ -170.8661, 65.6464 ], [ -170.8651, 65.6495 ], [ -170.8667, 65.65 ], [ -170.8813, 65.6521 ], [ -170.8854, 65.6542 ], [ -170.8938, 65.6542 ], [ -170.9021, 65.6583 ], [ -170.9083, 65.6563 ], [ -170.9292, 65.6625 ], [ -170.9396, 65.6625 ], [ -170.9521, 65.6667 ], [ -170.9667, 65.6667 ], [ -170.9875, 65.6729 ], [ -170.9979, 65.6729 ], [ -171.0021, 65.675 ], [ -171.0063, 65.675 ], [ -171.013, 65.6787 ], [ -171.0229, 65.6813 ], [ -171.0375, 65.6813 ], [ -171.0417, 65.6833 ], [ -171.0521, 65.6833 ], [ -171.0536, 65.6839 ], [ -171.0526, 65.687 ], [ -171.0542, 65.6875 ], [ -171.0688, 65.6875 ], [ -171.0729, 65.6896 ], [ -171.0813, 65.6896 ], [ -171.0854, 65.6917 ], [ -171.1062, 65.6917 ], [ -171.1104, 65.6937 ], [ -171.125, 65.6937 ], [ -171.1333, 65.6979 ], [ -171.1417, 65.6979 ], [ -171.1562, 65.7021 ], [ -171.1646, 65.7021 ], [ -171.1662, 65.7037 ], [ -171.163, 65.7099 ], [ -171.1662, 65.7109 ], [ -171.1651, 65.7141 ], [ -171.1724, 65.7172 ], [ -171.1755, 65.7224 ], [ -171.1786, 65.7234 ], [ -171.1776, 65.7266 ], [ -171.1922, 65.7349 ], [ -171.1995, 65.7359 ], [ -171.1979, 65.7396 ], [ -171.2016, 65.7422 ], [ -171.2005, 65.7453 ], [ -171.2099, 65.7505 ], [ -171.213, 65.7557 ], [ -171.2187, 65.7583 ], [ -171.2245, 65.7589 ], [ -171.2255, 65.762 ], [ -171.2328, 65.763 ], [ -171.2443, 65.7703 ], [ -171.2542, 65.7729 ], [ -171.2646, 65.7729 ], [ -171.2688, 65.775 ], [ -171.2807, 65.7755 ], [ -171.2797, 65.7786 ], [ -171.2901, 65.7849 ], [ -171.3021, 65.7854 ], [ -171.3062, 65.7875 ], [ -171.3104, 65.7875 ], [ -171.3172, 65.7911 ], [ -171.3245, 65.7922 ], [ -171.3234, 65.7953 ], [ -171.3328, 65.7963 ], [ -171.3375, 65.8 ], [ -171.3495, 65.8005 ], [ -171.3609, 65.8078 ], [ -171.3703, 65.8089 ], [ -171.3693, 65.812 ], [ -171.3849, 65.8151 ], [ -171.3859, 65.8182 ], [ -171.3932, 65.8193 ], [ -171.4047, 65.8266 ], [ -171.4224, 65.8297 ], [ -171.4214, 65.8328 ], [ -171.4229, 65.8333 ], [ -171.4271, 65.8333 ], [ -171.4396, 65.8375 ], [ -171.4458, 65.8375 ], [ -171.45, 65.8354 ], [ -171.4563, 65.8375 ], [ -171.4646, 65.8313 ], [ -171.4787, 65.8307 ], [ -171.4787, 65.8276 ], [ -171.4729, 65.825 ], [ -171.463, 65.8245 ], [ -171.4588, 65.8224 ], [ -171.4599, 65.8193 ], [ -171.4542, 65.8167 ], [ -171.45, 65.8167 ], [ -171.4458, 65.8146 ], [ -171.4333, 65.8146 ], [ -171.4292, 65.8167 ], [ -171.4214, 65.8161 ], [ -171.4193, 65.8141 ], [ -171.4193, 65.8109 ], [ -171.4234, 65.8068 ], [ -171.4286, 65.8057 ], [ -171.4328, 65.7984 ], [ -171.4297, 65.7974 ], [ -171.4307, 65.7943 ], [ -171.4276, 65.7932 ], [ -171.4224, 65.7839 ], [ -171.4109, 65.7786 ], [ -171.4078, 65.7734 ], [ -171.4047, 65.7724 ], [ -171.4057, 65.7693 ], [ -171.4026, 65.7682 ], [ -171.3995, 65.763 ], [ -171.3963, 65.762 ], [ -171.3995, 65.7568 ], [ -171.3896, 65.7521 ], [ -171.3797, 65.7516 ], [ -171.3786, 65.7484 ], [ -171.3729, 65.7479 ], [ -171.3646, 65.7438 ], [ -171.3562, 65.7438 ], [ -171.3495, 65.7401 ], [ -171.3422, 65.7391 ], [ -171.3313, 65.7333 ], [ -171.325, 65.7354 ], [ -171.3125, 65.7312 ], [ -171.2896, 65.7292 ], [ -171.2745, 65.7214 ], [ -171.2672, 65.7203 ], [ -171.2708, 65.7167 ], [ -171.2792, 65.7208 ], [ -171.2937, 65.7229 ], [ -171.3062, 65.7271 ], [ -171.3208, 65.7271 ], [ -171.3307, 65.7245 ], [ -171.3313, 65.7229 ], [ -171.3276, 65.7203 ], [ -171.3307, 65.7151 ], [ -171.3193, 65.7099 ], [ -171.3161, 65.7068 ], [ -171.3104, 65.7063 ], [ -171.3047, 65.7037 ], [ -171.3062, 65.7021 ], [ -171.3203, 65.7016 ], [ -171.3203, 65.6984 ], [ -171.3182, 65.6964 ], [ -171.3109, 65.6932 ], [ -171.312, 65.6901 ], [ -171.3083, 65.6896 ], [ -171.3042, 65.6917 ], [ -171.2896, 65.6917 ], [ -171.2797, 65.687 ], [ -171.2807, 65.6839 ], [ -171.2724, 65.6797 ], [ -171.2651, 65.6787 ], [ -171.2661, 65.6755 ], [ -171.2401, 65.662 ], [ -171.2432, 65.6568 ], [ -171.2359, 65.6536 ], [ -171.2391, 65.6484 ], [ -171.2349, 65.6464 ], [ -171.2297, 65.6464 ], [ -171.2287, 65.6495 ], [ -171.225, 65.6521 ], [ -171.2208, 65.65 ], [ -171.2146, 65.6521 ], [ -171.2083, 65.6521 ], [ -171.2042, 65.65 ], [ -171.1958, 65.65 ], [ -171.1917, 65.6479 ], [ -171.1812, 65.6479 ], [ -171.1771, 65.6458 ], [ -171.1708, 65.6458 ], [ -171.1667, 65.6479 ], [ -171.1625, 65.6458 ], [ -171.1521, 65.6458 ], [ -171.1479, 65.6438 ], [ -171.1396, 65.6438 ], [ -171.1187, 65.6354 ], [ -171.1042, 65.6354 ], [ -171.0917, 65.6312 ], [ -171.0813, 65.6312 ], [ -171.0713, 65.6286 ], [ -171.0646, 65.625 ], [ -171.0521, 65.6229 ], [ -171.0453, 65.6193 ], [ -171.038, 65.6182 ], [ -171.0391, 65.6151 ], [ -171.0359, 65.6141 ], [ -171.0375, 65.6104 ], [ -171.0354, 65.6062 ], [ -171.037, 65.6026 ], [ -171.0297, 65.6016 ], [ -171.0307, 65.5984 ], [ -171.0276, 65.5974 ], [ -171.0286, 65.5943 ], [ -171.0161, 65.588 ], [ -170.9958, 65.5875 ], [ -170.9917, 65.5854 ], [ -170.9854, 65.5875 ], [ -170.9797, 65.5849 ], [ -170.9818, 65.5818 ], [ -170.987, 65.5807 ], [ -170.9932, 65.5724 ], [ -170.9797, 65.5641 ], [ -170.9807, 65.5609 ], [ -170.9714, 65.5599 ], [ -170.9766, 65.5537 ], [ -170.9734, 65.5516 ], [ -170.9734, 65.5484 ], [ -170.9787, 65.5432 ], [ -170.9755, 65.5411 ], [ -170.9755, 65.538 ], [ -170.9828, 65.537 ], [ -170.9797, 65.5349 ], [ -170.9828, 65.5286 ], [ -170.9797, 65.5255 ], [ -170.9839, 65.5193 ], [ -170.9891, 65.5182 ], [ -170.9911, 65.5162 ], [ -170.988, 65.5141 ], [ -170.9917, 65.5083 ], [ -170.9896, 65.5042 ], [ -170.9911, 65.5005 ], [ -170.988, 65.4984 ], [ -170.9922, 65.4922 ], [ -171.0036, 65.4912 ], [ -171.0005, 65.488 ], [ -171.0057, 65.487 ], [ -171.0109, 65.4818 ], [ -171.0161, 65.4807 ], [ -171.0193, 65.4755 ], [ -171.0307, 65.4724 ], [ -171.0339, 65.4693 ], [ -171.0417, 65.4667 ], [ -171.0625, 65.4667 ], [ -171.0667, 65.4688 ], [ -171.0729, 65.4667 ], [ -171.0797, 65.4703 ], [ -171.087, 65.4714 ], [ -171.0859, 65.4745 ], [ -171.1, 65.4771 ], [ -171.1109, 65.4828 ], [ -171.1208, 65.4854 ], [ -171.1354, 65.4854 ], [ -171.1396, 65.4875 ], [ -171.1438, 65.4854 ], [ -171.15, 65.4854 ], [ -171.1542, 65.4875 ], [ -171.1646, 65.4875 ], [ -171.1724, 65.4922 ], [ -171.1714, 65.4974 ], [ -171.1786, 65.4984 ], [ -171.1776, 65.5016 ], [ -171.1849, 65.5026 ], [ -171.1943, 65.5078 ], [ -171.2167, 65.5125 ], [ -171.225, 65.5167 ], [ -171.2354, 65.5167 ], [ -171.2443, 65.5224 ], [ -171.2542, 65.525 ], [ -171.2688, 65.525 ], [ -171.2729, 65.5271 ], [ -171.2833, 65.5271 ], [ -171.2875, 65.5292 ], [ -171.2979, 65.525 ], [ -171.3042, 65.5271 ], [ -171.3146, 65.5271 ], [ -171.3188, 65.525 ], [ -171.325, 65.5271 ], [ -171.3292, 65.5229 ], [ -171.3354, 65.5229 ], [ -171.3417, 65.525 ], [ -171.3562, 65.5208 ], [ -171.3625, 65.5229 ], [ -171.3667, 65.5208 ], [ -171.4146, 65.5208 ], [ -171.425, 65.5167 ], [ -171.4312, 65.5188 ], [ -171.4458, 65.5146 ], [ -171.4625, 65.5146 ], [ -171.4667, 65.5125 ], [ -171.4729, 65.5125 ], [ -171.4771, 65.5146 ], [ -171.4833, 65.5125 ], [ -171.4875, 65.5146 ], [ -171.4937, 65.5146 ], [ -171.4979, 65.5125 ], [ -171.5021, 65.5146 ], [ -171.5083, 65.5146 ], [ -171.5125, 65.5125 ], [ -171.55, 65.5125 ], [ -171.5542, 65.5146 ], [ -171.5583, 65.5125 ], [ -171.5854, 65.5125 ], [ -171.5896, 65.5104 ], [ -171.5958, 65.5104 ], [ -171.6, 65.5125 ], [ -171.6187, 65.5125 ], [ -171.6229, 65.5146 ], [ -171.6354, 65.5146 ], [ -171.6396, 65.5125 ], [ -171.6474, 65.512 ], [ -171.6505, 65.5089 ], [ -171.6583, 65.5062 ], [ -171.675, 65.5062 ], [ -171.6792, 65.5042 ], [ -171.6979, 65.5021 ], [ -171.7021, 65.5 ], [ -171.7125, 65.5 ], [ -171.7167, 65.5021 ], [ -171.7287, 65.5026 ], [ -171.7172, 65.5068 ], [ -171.7151, 65.5089 ], [ -171.7182, 65.5109 ], [ -171.7172, 65.5141 ], [ -171.7229, 65.5167 ], [ -171.7287, 65.512 ], [ -171.7297, 65.5089 ], [ -171.7333, 65.5083 ], [ -171.7422, 65.5141 ], [ -171.7521, 65.5167 ], [ -171.7667, 65.5167 ], [ -171.7708, 65.5188 ], [ -171.7958, 65.5188 ], [ -171.8, 65.5208 ], [ -171.8104, 65.5188 ], [ -171.8146, 65.5208 ], [ -171.825, 65.5208 ], [ -171.8292, 65.5229 ], [ -171.8333, 65.5208 ], [ -171.8396, 65.5208 ], [ -171.8438, 65.5229 ], [ -171.85, 65.5208 ], [ -171.875, 65.5208 ], [ -171.8792, 65.5229 ], [ -171.9208, 65.5229 ], [ -171.9286, 65.5203 ], [ -171.9333, 65.5167 ], [ -171.9375, 65.5188 ], [ -171.9563, 65.5188 ], [ -171.9807, 65.5318 ], [ -171.9797, 65.5349 ], [ -171.9912, 65.5401 ], [ -171.9943, 65.5432 ], [ -172.0, 65.5458 ], [ -172.0057, 65.5463 ], [ -172.0125, 65.55 ], [ -172.0271, 65.5521 ], [ -172.0396, 65.5563 ], [ -172.0646, 65.5563 ], [ -172.0688, 65.5583 ], [ -172.0729, 65.5563 ], [ -172.0771, 65.5583 ], [ -172.0937, 65.5563 ], [ -172.0995, 65.5589 ], [ -172.0984, 65.562 ], [ -172.1, 65.5625 ], [ -172.1187, 65.5604 ], [ -172.1266, 65.5578 ], [ -172.1182, 65.5526 ], [ -172.1125, 65.5521 ], [ -172.1109, 65.5505 ], [ -172.1245, 65.5453 ], [ -172.1255, 65.5422 ], [ -172.1354, 65.5417 ], [ -172.138, 65.538 ], [ -172.1521, 65.5333 ], [ -172.1583, 65.5333 ], [ -172.1625, 65.5354 ], [ -172.1729, 65.5354 ], [ -172.1828, 65.538 ], [ -172.1776, 65.5401 ], [ -172.1755, 65.5432 ], [ -172.1828, 65.5463 ], [ -172.1818, 65.5495 ], [ -172.1833, 65.55 ], [ -172.1938, 65.55 ], [ -172.1979, 65.5521 ], [ -172.2042, 65.5521 ], [ -172.2083, 65.55 ], [ -172.2208, 65.55 ], [ -172.225, 65.5479 ], [ -172.2458, 65.5479 ], [ -172.25, 65.5458 ], [ -172.2578, 65.5484 ], [ -172.2557, 65.5516 ], [ -172.2443, 65.5526 ], [ -172.2432, 65.5557 ], [ -172.2354, 65.5583 ], [ -172.2255, 65.5589 ], [ -172.2224, 65.5641 ], [ -172.2172, 65.5651 ], [ -172.2141, 65.5703 ], [ -172.2088, 65.5714 ], [ -172.2104, 65.575 ], [ -172.2047, 65.5849 ], [ -172.2161, 65.5901 ], [ -172.2234, 65.5953 ], [ -172.2354, 65.5958 ], [ -172.2422, 65.5995 ], [ -172.2495, 65.6005 ], [ -172.2484, 65.6036 ], [ -172.2557, 65.6068 ], [ -172.2588, 65.6099 ], [ -172.275, 65.6125 ], [ -172.2839, 65.6182 ], [ -172.2917, 65.6188 ], [ -172.3057, 65.6234 ], [ -172.3078, 65.6255 ], [ -172.3078, 65.6286 ], [ -172.3026, 65.6297 ], [ -172.2979, 65.6354 ], [ -172.2901, 65.6339 ], [ -172.2917, 65.6375 ], [ -172.2875, 65.6417 ], [ -172.2797, 65.6412 ], [ -172.2807, 65.638 ], [ -172.2667, 65.6333 ], [ -172.2604, 65.6333 ], [ -172.2526, 65.637 ], [ -172.2583, 65.6396 ], [ -172.2724, 65.6422 ], [ -172.2672, 65.6443 ], [ -172.2661, 65.6474 ], [ -172.2547, 65.6484 ], [ -172.2563, 65.65 ], [ -172.262, 65.6505 ], [ -172.2609, 65.6557 ], [ -172.2818, 65.6641 ], [ -172.2891, 65.6651 ], [ -172.2943, 65.6682 ], [ -172.3016, 65.6693 ], [ -172.3125, 65.675 ], [ -172.3245, 65.6745 ], [ -172.3213, 65.6724 ], [ -172.3245, 65.6651 ], [ -172.3172, 65.6641 ], [ -172.3172, 65.6609 ], [ -172.3193, 65.6589 ], [ -172.3313, 65.6583 ], [ -172.3396, 65.6625 ], [ -172.3516, 65.663 ], [ -172.3542, 65.6667 ], [ -172.3526, 65.6724 ], [ -172.3604, 65.6771 ], [ -172.375, 65.6792 ], [ -172.3792, 65.6813 ], [ -172.3875, 65.6813 ], [ -172.3917, 65.6833 ], [ -172.4125, 65.6833 ], [ -172.425, 65.6875 ], [ -172.4375, 65.6833 ], [ -172.4417, 65.6833 ], [ -172.4458, 65.6854 ], [ -172.4521, 65.6833 ], [ -172.475, 65.6833 ], [ -172.4792, 65.6813 ], [ -172.4896, 65.6813 ], [ -172.4958, 65.6771 ], [ -172.5188, 65.675 ], [ -172.5203, 65.6755 ], [ -172.5193, 65.6787 ], [ -172.5266, 65.6818 ], [ -172.5255, 65.6849 ], [ -172.5313, 65.6875 ], [ -172.5479, 65.6854 ], [ -172.5542, 65.6813 ], [ -172.562, 65.6818 ], [ -172.5547, 65.6859 ], [ -172.5578, 65.688 ], [ -172.5568, 65.6932 ], [ -172.563, 65.6974 ], [ -172.5708, 65.6979 ], [ -172.575, 65.7 ], [ -172.5854, 65.7 ], [ -172.5896, 65.6979 ], [ -172.5958, 65.6979 ], [ -172.6005, 65.6943 ], [ -172.6057, 65.6932 ], [ -172.6089, 65.6901 ], [ -172.6167, 65.6875 ], [ -172.6375, 65.6875 ], [ -172.6417, 65.6896 ], [ -172.6458, 65.6875 ], [ -172.6583, 65.6875 ], [ -172.6625, 65.6896 ], [ -172.6687, 65.6854 ], [ -172.6729, 65.6875 ], [ -172.6792, 65.6854 ], [ -172.6833, 65.6875 ], [ -172.6875, 65.6854 ], [ -172.6974, 65.6849 ], [ -172.6984, 65.6818 ], [ -172.7125, 65.6771 ], [ -172.7187, 65.6792 ], [ -172.7292, 65.675 ], [ -172.7354, 65.675 ], [ -172.7417, 65.6771 ], [ -172.7521, 65.6729 ], [ -172.7625, 65.6729 ], [ -172.7833, 65.6792 ], [ -172.7896, 65.6792 ], [ -172.7979, 65.6833 ], [ -172.8036, 65.6828 ], [ -172.8016, 65.6776 ], [ -172.7833, 65.6729 ], [ -172.7583, 65.6708 ], [ -172.7568, 65.6703 ], [ -172.7578, 65.6672 ], [ -172.7563, 65.6667 ], [ -172.7208, 65.6667 ], [ -172.7167, 65.6687 ], [ -172.6979, 65.6687 ], [ -172.6938, 65.6708 ], [ -172.6875, 65.6708 ], [ -172.6667, 65.6646 ], [ -172.6604, 65.6646 ], [ -172.6562, 65.6667 ], [ -172.6521, 65.6646 ], [ -172.6401, 65.6641 ], [ -172.6318, 65.6599 ], [ -172.637, 65.6578 ], [ -172.6359, 65.6526 ], [ -172.6474, 65.6495 ], [ -172.6526, 65.6422 ], [ -172.6578, 65.6412 ], [ -172.6662, 65.6276 ], [ -172.6604, 65.625 ], [ -172.6547, 65.6245 ], [ -172.6375, 65.6146 ], [ -172.6313, 65.6146 ], [ -172.6187, 65.6104 ], [ -172.6083, 65.6104 ], [ -172.6042, 65.6125 ], [ -172.5984, 65.6099 ], [ -172.5958, 65.6062 ], [ -172.588, 65.6068 ], [ -172.5854, 65.6104 ], [ -172.5792, 65.6083 ], [ -172.575, 65.6104 ], [ -172.5583, 65.6104 ], [ -172.5521, 65.6146 ], [ -172.5396, 65.6146 ], [ -172.5354, 65.6167 ], [ -172.5313, 65.6146 ], [ -172.5208, 65.6146 ], [ -172.5167, 65.6125 ], [ -172.5083, 65.6125 ], [ -172.5, 65.6083 ], [ -172.4937, 65.6083 ], [ -172.4896, 65.6062 ], [ -172.475, 65.6062 ], [ -172.4708, 65.6042 ], [ -172.4583, 65.6021 ], [ -172.45, 65.5979 ], [ -172.4359, 65.5953 ], [ -172.4224, 65.588 ], [ -172.4151, 65.587 ], [ -172.3958, 65.5792 ], [ -172.3917, 65.5792 ], [ -172.3792, 65.575 ], [ -172.3672, 65.5745 ], [ -172.3687, 65.5729 ], [ -172.3786, 65.5724 ], [ -172.3755, 65.5693 ], [ -172.3891, 65.5641 ], [ -172.4036, 65.5411 ], [ -172.4026, 65.537 ] ] ], [ [ [ -174.9125, 66.6771 ], [ -174.9141, 66.6787 ], [ -174.9089, 66.6787 ], [ -174.9089, 66.6776 ], [ -174.9125, 66.6771 ] ] ], [ [ [ -174.5937, 67.0417 ], [ -174.5922, 67.0401 ], [ -174.6057, 67.0401 ], [ -174.6042, 67.0417 ], [ -174.5937, 67.0417 ] ] ], [ [ [ -173.9724, 66.2432 ], [ -173.9693, 66.2432 ], [ -173.9693, 66.2422 ], [ -173.9724, 66.2422 ], [ -173.9724, 66.2432 ] ] ], [ [ [ -173.9604, 66.2438 ], [ -173.962, 66.2453 ], [ -173.9568, 66.2453 ], [ -173.9568, 66.2443 ], [ -173.9604, 66.2438 ] ] ], [ [ [ -173.9568, 66.2662 ], [ -173.9542, 66.2646 ], [ -173.9479, 66.2646 ], [ -173.9463, 66.263 ], [ -173.9516, 66.262 ], [ -173.9547, 66.2568 ], [ -173.9625, 66.2542 ], [ -173.9828, 66.2536 ], [ -173.9797, 66.2505 ], [ -173.9833, 66.2458 ], [ -173.9974, 66.2474 ], [ -173.9974, 66.2443 ], [ -173.9943, 66.2422 ], [ -173.9979, 66.2396 ], [ -174.0099, 66.2401 ], [ -174.0141, 66.2422 ], [ -174.013, 66.2474 ], [ -174.0203, 66.2505 ], [ -174.0182, 66.2536 ], [ -174.013, 66.2547 ], [ -174.012, 66.2578 ], [ -174.0026, 66.2568 ], [ -173.9995, 66.262 ], [ -173.9943, 66.263 ], [ -173.9896, 66.2667 ], [ -173.9833, 66.2667 ], [ -173.9771, 66.2688 ], [ -173.9729, 66.2667 ], [ -173.9568, 66.2662 ] ] ], [ [ [ -173.9479, 66.2667 ], [ -173.9495, 66.2682 ], [ -173.9443, 66.2682 ], [ -173.9443, 66.2672 ], [ -173.9479, 66.2667 ] ] ], [ [ [ -173.9625, 66.2688 ], [ -173.9641, 66.2703 ], [ -173.9588, 66.2703 ], [ -173.9588, 66.2693 ], [ -173.9625, 66.2688 ] ] ], [ [ [ -173.9417, 66.2688 ], [ -173.9432, 66.2703 ], [ -173.938, 66.2703 ], [ -173.938, 66.2693 ], [ -173.9417, 66.2688 ] ] ], [ [ [ -173.9703, 66.2745 ], [ -173.9672, 66.2745 ], [ -173.9672, 66.2734 ], [ -173.9703, 66.2734 ], [ -173.9703, 66.2745 ] ] ], [ [ [ -174.4036, 66.2891 ], [ -174.4005, 66.2891 ], [ -174.4005, 66.288 ], [ -174.4036, 66.288 ], [ -174.4036, 66.2891 ] ] ], [ [ [ -171.8938, 65.4771 ], [ -171.8896, 65.4771 ], [ -171.888, 65.4755 ], [ -171.8958, 65.4729 ], [ -171.9125, 65.4729 ], [ -171.9167, 65.4708 ], [ -171.9229, 65.4729 ], [ -171.9271, 65.4729 ], [ -171.9312, 65.4708 ], [ -171.9375, 65.4729 ], [ -171.9438, 65.4729 ], [ -171.9479, 65.4708 ], [ -171.9687, 65.4708 ], [ -171.9729, 65.4729 ], [ -171.9875, 65.4729 ], [ -171.9917, 65.475 ], [ -172.0208, 65.475 ], [ -172.0266, 65.4786 ], [ -172.0229, 65.4792 ], [ -172.0188, 65.4771 ], [ -171.9896, 65.4771 ], [ -171.9854, 65.475 ], [ -171.9812, 65.4771 ], [ -171.9542, 65.4771 ], [ -171.95, 65.475 ], [ -171.9146, 65.475 ], [ -171.913, 65.4766 ], [ -171.9162, 65.4776 ], [ -171.9187, 65.4812 ], [ -171.9182, 65.4828 ], [ -171.9146, 65.4833 ], [ -171.9083, 65.4771 ], [ -171.8938, 65.4771 ] ] ], [ [ [ -172.2354, 65.4812 ], [ -172.2339, 65.4849 ], [ -172.2391, 65.488 ], [ -172.2391, 65.4912 ], [ -172.237, 65.4932 ], [ -172.2292, 65.4938 ], [ -172.225, 65.4958 ], [ -172.2214, 65.4953 ], [ -172.2182, 65.4901 ], [ -172.2151, 65.4891 ], [ -172.2161, 65.4859 ], [ -172.213, 65.4849 ], [ -172.2245, 65.4807 ], [ -172.2234, 65.4776 ], [ -172.2391, 65.4734 ], [ -172.2354, 65.4812 ] ] ], [ [ [ -172.1078, 65.5537 ], [ -172.1047, 65.5537 ], [ -172.1047, 65.5526 ], [ -172.1078, 65.5526 ], [ -172.1078, 65.5537 ] ] ], [ [ [ -172.0807, 65.5557 ], [ -172.0776, 65.5557 ], [ -172.0776, 65.5547 ], [ -172.0807, 65.5547 ], [ -172.0807, 65.5557 ] ] ], [ [ [ -172.8526, 64.8297 ], [ -172.8604, 64.8292 ], [ -172.862, 64.8307 ], [ -172.8542, 64.8313 ], [ -172.8526, 64.8297 ] ] ], [ [ [ -172.8813, 64.8396 ], [ -172.8776, 64.8391 ], [ -172.8693, 64.8339 ], [ -172.8891, 64.8339 ], [ -172.887, 64.8391 ], [ -172.8813, 64.8396 ] ] ], [ [ [ -172.8953, 64.8391 ], [ -172.8922, 64.8391 ], [ -172.8922, 64.838 ], [ -172.8953, 64.838 ], [ -172.8953, 64.8391 ] ] ], [ [ [ -173.9932, 66.2245 ], [ -173.9901, 66.2245 ], [ -173.9901, 66.2234 ], [ -173.9932, 66.2234 ], [ -173.9932, 66.2245 ] ] ], [ [ [ -171.2891, 65.7536 ], [ -171.2859, 65.7536 ], [ -171.288, 65.7484 ], [ -171.2974, 65.7484 ], [ -171.2974, 65.7516 ], [ -171.2891, 65.7536 ] ] ], [ [ [ -170.2063, 66.1771 ], [ -170.2078, 66.1787 ], [ -170.2026, 66.1787 ], [ -170.2026, 66.1776 ], [ -170.2063, 66.1771 ] ] ], [ [ [ -170.3557, 66.3057 ], [ -170.3526, 66.3057 ], [ -170.3526, 66.3047 ], [ -170.3557, 66.3047 ], [ -170.3557, 66.3057 ] ] ], [ [ [ -170.3578, 66.3089 ], [ -170.3568, 66.3068 ], [ -170.3583, 66.3063 ], [ -170.3661, 66.3068 ], [ -170.3625, 66.3104 ], [ -170.3578, 66.3089 ] ] ], [ [ [ -172.7125, 65.6729 ], [ -172.7109, 65.6713 ], [ -172.7203, 65.6713 ], [ -172.7187, 65.6729 ], [ -172.7125, 65.6729 ] ] ], [ [ [ -172.5688, 65.6792 ], [ -172.5609, 65.6787 ], [ -172.5609, 65.6755 ], [ -172.563, 65.6734 ], [ -172.5661, 65.6734 ], [ -172.5703, 65.6755 ], [ -172.5703, 65.6787 ], [ -172.5688, 65.6792 ] ] ], [ [ [ -172.0286, 66.9495 ], [ -172.0255, 66.9495 ], [ -172.0255, 66.9484 ], [ -172.0286, 66.9484 ], [ -172.0286, 66.9495 ] ] ], [ [ [ 174.3833, 61.8187 ], [ 174.3819, 61.8187 ], [ 174.4, 61.8209 ], [ 174.3958, 61.8187 ], [ 174.3833, 61.8187 ] ] ], [ [ [ 174.7713, 61.9016 ], [ 174.7745, 61.9016 ], [ 174.7745, 61.9005 ], [ 174.7713, 61.9005 ], [ 174.7713, 61.9016 ] ] ], [ [ [ 174.7625, 61.9042 ], [ 174.7609, 61.9057 ], [ 174.763, 61.9078 ], [ 174.7703, 61.9089 ], [ 174.7734, 61.912 ], [ 174.7766, 61.912 ], [ 174.7786, 61.9099 ], [ 174.7766, 61.9068 ], [ 174.7724, 61.9047 ], [ 174.7667, 61.9063 ], [ 174.7625, 61.9042 ] ] ], [ [ [ 174.7266, 61.9161 ], [ 174.7266, 61.9151 ], [ 174.7229, 61.9146 ], [ 174.7229, 61.9167 ], [ 174.7266, 61.9161 ] ] ], [ [ [ 174.8109, 61.9161 ], [ 174.8141, 61.9161 ], [ 174.8141, 61.9151 ], [ 174.8109, 61.9151 ], [ 174.8109, 61.9161 ] ] ], [ [ [ 174.8229, 61.9208 ], [ 174.825, 61.9208 ], [ 174.825, 61.9188 ], [ 174.8172, 61.9193 ], [ 174.8188, 61.9208 ], [ 174.8229, 61.9208 ] ] ], [ [ [ 174.8354, 61.9354 ], [ 174.8396, 61.9375 ], [ 174.8438, 61.9375 ], [ 174.8479, 61.9354 ], [ 174.8479, 61.9333 ], [ 174.8338, 61.9339 ], [ 174.8354, 61.9354 ] ] ], [ [ [ 174.713, 61.937 ], [ 174.7146, 61.9375 ], [ 174.7161, 61.9359 ], [ 174.7146, 61.9354 ], [ 174.713, 61.937 ] ] ], [ [ [ 174.7005, 61.9432 ], [ 174.7021, 61.9437 ], [ 174.7036, 61.9422 ], [ 174.7005, 61.9422 ], [ 174.7005, 61.9432 ] ] ], [ [ [ 175.0922, 61.9682 ], [ 175.0953, 61.9682 ], [ 175.0953, 61.9672 ], [ 175.0938, 61.9667 ], [ 175.0922, 61.9682 ] ] ], [ [ [ 175.325, 62.0875 ], [ 175.3266, 62.0859 ], [ 175.3208, 62.0833 ], [ 175.3172, 62.0859 ], [ 175.3188, 62.0875 ], [ 175.325, 62.0875 ] ] ], [ [ [ 175.2901, 62.0891 ], [ 175.2932, 62.0891 ], [ 175.2932, 62.088 ], [ 175.2901, 62.088 ], [ 175.2901, 62.0891 ] ] ], [ [ [ 176.9125, 62.5104 ], [ 176.9083, 62.5083 ], [ 176.9026, 62.5089 ], [ 176.9042, 62.5104 ], [ 176.9083, 62.5104 ], [ 176.9083, 62.5125 ], [ 176.9182, 62.512 ], [ 176.9167, 62.5104 ], [ 176.9125, 62.5104 ] ] ], [ [ [ 177.2672, 62.5505 ], [ 177.2672, 62.5516 ], [ 177.2708, 62.5521 ], [ 177.2708, 62.55 ], [ 177.2672, 62.5505 ] ] ], [ [ [ 179.287, 63.0005 ], [ 179.2792, 62.9979 ], [ 179.2776, 63.0016 ], [ 179.2854, 63.0021 ], [ 179.287, 63.0005 ] ] ], [ [ [ 179.0797, 63.2776 ], [ 179.0891, 63.2766 ], [ 179.0859, 63.2734 ], [ 179.0911, 63.2714 ], [ 179.0755, 63.2693 ], [ 179.0714, 63.2755 ], [ 179.0745, 63.2786 ], [ 179.0651, 63.2818 ], [ 179.063, 63.2849 ], [ 179.0786, 63.2807 ], [ 179.0797, 63.2776 ] ] ], [ [ [ 179.0547, 63.2922 ], [ 179.0599, 63.2891 ], [ 179.062, 63.2859 ], [ 179.05, 63.2854 ], [ 179.0474, 63.2891 ], [ 179.0359, 63.2932 ], [ 179.0391, 63.2943 ], [ 179.038, 63.2995 ], [ 179.0396, 63.3 ], [ 179.0437, 63.2979 ], [ 179.0516, 63.2974 ], [ 179.0547, 63.2922 ] ] ], [ [ [ 179.0333, 63.3042 ], [ 179.0297, 63.3047 ], [ 179.0297, 63.3057 ], [ 179.0349, 63.3057 ], [ 179.0333, 63.3042 ] ] ], [ [ [ 179.0099, 63.3266 ], [ 179.013, 63.3214 ], [ 179.0245, 63.3172 ], [ 179.0151, 63.3172 ], [ 179.0078, 63.3224 ], [ 179.0026, 63.3234 ], [ 178.9995, 63.3286 ], [ 178.9922, 63.3297 ], [ 178.9896, 63.3333 ], [ 178.9922, 63.3391 ], [ 178.9953, 63.3391 ], [ 178.9984, 63.3339 ], [ 179.0036, 63.3328 ], [ 179.0047, 63.3297 ], [ 179.0099, 63.3266 ] ] ], [ [ [ 178.9859, 63.3411 ], [ 178.9875, 63.3417 ], [ 178.9891, 63.3401 ], [ 178.9859, 63.3401 ], [ 178.9859, 63.3411 ] ] ], [ [ [ 178.9839, 63.3453 ], [ 178.9854, 63.3458 ], [ 178.987, 63.3443 ], [ 178.9854, 63.3438 ], [ 178.9839, 63.3453 ] ] ], [ [ [ 178.8083, 63.4313 ], [ 178.8109, 63.4276 ], [ 178.8161, 63.4266 ], [ 178.8089, 63.4224 ], [ 178.8099, 63.4193 ], [ 178.7875, 63.4188 ], [ 178.7859, 63.4203 ], [ 178.7891, 63.4224 ], [ 178.7813, 63.4229 ], [ 178.7734, 63.4255 ], [ 178.7766, 63.4276 ], [ 178.7755, 63.4328 ], [ 178.7786, 63.4349 ], [ 178.7708, 63.4354 ], [ 178.7693, 63.437 ], [ 178.7724, 63.438 ], [ 178.7713, 63.4411 ], [ 178.7729, 63.4417 ], [ 178.7937, 63.4417 ], [ 178.7964, 63.438 ], [ 178.8078, 63.4349 ], [ 178.8083, 63.4313 ] ] ], [ [ [ 178.7005, 63.3964 ], [ 178.7036, 63.3943 ], [ 178.6922, 63.3943 ], [ 178.6958, 63.3979 ], [ 178.7005, 63.3964 ] ] ], [ [ [ 178.6641, 63.4026 ], [ 178.6589, 63.4026 ], [ 178.6589, 63.4057 ], [ 178.662, 63.4057 ], [ 178.6641, 63.4026 ] ] ], [ [ [ 178.7287, 63.4141 ], [ 178.7328, 63.412 ], [ 178.7359, 63.4068 ], [ 178.7437, 63.4063 ], [ 178.7479, 63.4042 ], [ 178.7667, 63.4042 ], [ 178.7766, 63.4068 ], [ 178.7833, 63.4104 ], [ 178.7896, 63.4104 ], [ 178.7917, 63.4083 ], [ 178.7849, 63.4026 ], [ 178.7776, 63.4016 ], [ 178.7708, 63.3979 ], [ 178.7625, 63.3979 ], [ 178.7583, 63.3958 ], [ 178.7542, 63.3958 ], [ 178.7458, 63.3917 ], [ 178.7417, 63.3937 ], [ 178.7339, 63.3943 ], [ 178.7245, 63.412 ], [ 178.7208, 63.4146 ], [ 178.7151, 63.4151 ], [ 178.7203, 63.4203 ], [ 178.7125, 63.4208 ], [ 178.7109, 63.4224 ], [ 178.7193, 63.4266 ], [ 178.7224, 63.4266 ], [ 178.7255, 63.4193 ], [ 178.7307, 63.4182 ], [ 178.7276, 63.4161 ], [ 178.7287, 63.4141 ] ] ], [ [ [ 178.737, 63.5734 ], [ 178.7297, 63.5724 ], [ 178.7307, 63.5693 ], [ 178.7292, 63.5687 ], [ 178.7188, 63.5687 ], [ 178.7146, 63.5729 ], [ 178.7193, 63.5766 ], [ 178.7292, 63.5792 ], [ 178.7375, 63.5792 ], [ 178.7453, 63.5766 ], [ 178.737, 63.5734 ] ] ], [ [ [ 178.4693, 63.6099 ], [ 178.4708, 63.6104 ], [ 178.4724, 63.6089 ], [ 178.4708, 63.6083 ], [ 178.4693, 63.6099 ] ] ], [ [ [ 178.4734, 63.612 ], [ 178.4766, 63.612 ], [ 178.4766, 63.6109 ], [ 178.4734, 63.6109 ], [ 178.4734, 63.612 ] ] ], [ [ [ 178.712, 63.9172 ], [ 178.7042, 63.9167 ], [ 178.7026, 63.9182 ], [ 178.7104, 63.9188 ], [ 178.712, 63.9172 ] ] ], [ [ [ 178.6859, 63.9578 ], [ 178.6891, 63.9578 ], [ 178.6891, 63.9568 ], [ 178.6875, 63.9562 ], [ 178.6859, 63.9578 ] ] ], [ [ [ 178.6776, 63.9766 ], [ 178.6792, 63.9771 ], [ 178.6807, 63.9755 ], [ 178.6776, 63.9755 ], [ 178.6776, 63.9766 ] ] ], [ [ [ 178.6542, 63.9958 ], [ 178.6526, 63.9974 ], [ 178.6578, 63.9974 ], [ 178.6578, 63.9964 ], [ 178.6542, 63.9958 ] ] ], [ [ [ 178.6557, 64.0005 ], [ 178.6479, 64.0 ], [ 178.6463, 64.0016 ], [ 178.6542, 64.0021 ], [ 178.6557, 64.0005 ] ] ], [ [ [ 178.662, 64.0078 ], [ 178.6589, 64.0068 ], [ 178.6568, 64.0099 ], [ 178.6599, 64.0099 ], [ 178.662, 64.0078 ] ] ], [ [ [ 178.7729, 63.5812 ], [ 178.7688, 63.5812 ], [ 178.7672, 63.5828 ], [ 178.7745, 63.5828 ], [ 178.7729, 63.5812 ] ] ], [ [ [ 178.7818, 63.5901 ], [ 178.7828, 63.588 ], [ 178.7755, 63.5859 ], [ 178.7776, 63.5911 ], [ 178.7818, 63.5901 ] ] ], [ [ [ 179.888, 64.9912 ], [ 179.8911, 64.9912 ], [ 179.8911, 64.9901 ], [ 179.888, 64.9901 ], [ 179.888, 64.9912 ] ] ], [ [ [ 179.8687, 64.9917 ], [ 179.8672, 64.9932 ], [ 179.8703, 64.9932 ], [ 179.8703, 64.9922 ], [ 179.8687, 64.9917 ] ] ], [ [ [ 178.2937, 64.3875 ], [ 178.2901, 64.388 ], [ 178.2901, 64.3891 ], [ 178.2953, 64.3891 ], [ 178.2937, 64.3875 ] ] ], [ [ [ 178.6193, 64.5766 ], [ 178.6, 64.5708 ], [ 178.5813, 64.5687 ], [ 178.5729, 64.5646 ], [ 178.5583, 64.5625 ], [ 178.5542, 64.5604 ], [ 178.5396, 64.5604 ], [ 178.5292, 64.5646 ], [ 178.5213, 64.5651 ], [ 178.5208, 64.5667 ], [ 178.5255, 64.5703 ], [ 178.5328, 64.5714 ], [ 178.5328, 64.5745 ], [ 178.5255, 64.5787 ], [ 178.5479, 64.5792 ], [ 178.5526, 64.5828 ], [ 178.5604, 64.5833 ], [ 178.5688, 64.5875 ], [ 178.5792, 64.5875 ], [ 178.5833, 64.5854 ], [ 178.5938, 64.5854 ], [ 178.5979, 64.5833 ], [ 178.6146, 64.5813 ], [ 178.6354, 64.5875 ], [ 178.65, 64.5875 ], [ 178.6526, 64.5911 ], [ 178.6625, 64.5938 ], [ 178.6662, 64.5911 ], [ 178.6646, 64.5896 ], [ 178.6568, 64.5891 ], [ 178.6521, 64.5854 ], [ 178.6375, 64.5833 ], [ 178.6307, 64.5797 ], [ 178.625, 64.5792 ], [ 178.6193, 64.5766 ] ] ], [ [ [ 178.688, 64.6099 ], [ 178.6911, 64.6099 ], [ 178.6911, 64.6088 ], [ 178.688, 64.6088 ], [ 178.688, 64.6099 ] ] ], [ [ [ 178.6859, 64.6141 ], [ 178.6891, 64.6141 ], [ 178.6891, 64.613 ], [ 178.6859, 64.613 ], [ 178.6859, 64.6141 ] ] ], [ [ [ 178.7826, 64.6271 ], [ 178.7708, 64.625 ], [ 178.7682, 64.6214 ], [ 178.7417, 64.6146 ], [ 178.7312, 64.6146 ], [ 178.7188, 64.6104 ], [ 178.7109, 64.6109 ], [ 178.7208, 64.6167 ], [ 178.7292, 64.6167 ], [ 178.7375, 64.6208 ], [ 178.7495, 64.6214 ], [ 178.7536, 64.6234 ], [ 178.7526, 64.6266 ], [ 178.7583, 64.6292 ], [ 178.7646, 64.6292 ], [ 178.7688, 64.6312 ], [ 178.7896, 64.6312 ], [ 178.7896, 64.6292 ], [ 178.7826, 64.6271 ] ] ], [ [ [ 178.2688, 64.6354 ], [ 178.2792, 64.6354 ], [ 178.2833, 64.6375 ], [ 178.287, 64.637 ], [ 178.2813, 64.6333 ], [ 178.2589, 64.6339 ], [ 178.2604, 64.6354 ], [ 178.2688, 64.6354 ] ] ], [ [ [ 178.6917, 69.4146 ], [ 178.6901, 69.4161 ], [ 178.6953, 69.4161 ], [ 178.6953, 69.4151 ], [ 178.6917, 69.4146 ] ] ], [ [ [ 178.6167, 69.4292 ], [ 178.6151, 69.4307 ], [ 178.6203, 69.4307 ], [ 178.6203, 69.4297 ], [ 178.6167, 69.4292 ] ] ], [ [ [ 178.4646, 64.0167 ], [ 178.4604, 64.0167 ], [ 178.4588, 64.0182 ], [ 178.4661, 64.0182 ], [ 178.4646, 64.0167 ] ] ], [ [ [ 178.637, 64.0307 ], [ 178.6453, 64.0245 ], [ 178.6453, 64.0214 ], [ 178.6396, 64.0188 ], [ 178.6339, 64.0193 ], [ 178.637, 64.0214 ], [ 178.6349, 64.0266 ], [ 178.6271, 64.0313 ], [ 178.6208, 64.0313 ], [ 178.6167, 64.0333 ], [ 178.5896, 64.0333 ], [ 178.588, 64.0349 ], [ 178.5911, 64.0359 ], [ 178.5901, 64.0411 ], [ 178.5943, 64.0432 ], [ 178.6062, 64.0437 ], [ 178.6104, 64.0458 ], [ 178.6146, 64.0458 ], [ 178.6245, 64.0391 ], [ 178.6245, 64.0359 ], [ 178.6214, 64.0339 ], [ 178.6292, 64.0333 ], [ 178.637, 64.0307 ] ] ], [ [ [ 178.6021, 64.0563 ], [ 178.6, 64.0542 ], [ 178.5943, 64.0547 ], [ 178.6, 64.0583 ], [ 178.6021, 64.0563 ] ] ], [ [ [ 178.2937, 64.3833 ], [ 178.2922, 64.3849 ], [ 178.2953, 64.3849 ], [ 178.2953, 64.3839 ], [ 178.2937, 64.3833 ] ] ], [ [ [ 178.2547, 64.4151 ], [ 178.2599, 64.4141 ], [ 178.2609, 64.4109 ], [ 178.2661, 64.4099 ], [ 178.2672, 64.4068 ], [ 178.2786, 64.4036 ], [ 178.2818, 64.3984 ], [ 178.2932, 64.3922 ], [ 178.2875, 64.3896 ], [ 178.2839, 64.3901 ], [ 178.2828, 64.3932 ], [ 178.2776, 64.3943 ], [ 178.2745, 64.3995 ], [ 178.2625, 64.4042 ], [ 178.2479, 64.4021 ], [ 178.2401, 64.4047 ], [ 178.237, 64.4099 ], [ 178.2318, 64.413 ], [ 178.2349, 64.4161 ], [ 178.2297, 64.4172 ], [ 178.2328, 64.4203 ], [ 178.2276, 64.4297 ], [ 178.2276, 64.4328 ], [ 178.2307, 64.4328 ], [ 178.237, 64.4287 ], [ 178.2422, 64.4213 ], [ 178.2516, 64.4182 ], [ 178.2547, 64.4151 ] ] ], [ [ [ 177.6214, 64.4703 ], [ 177.6245, 64.4703 ], [ 177.6245, 64.4693 ], [ 177.6214, 64.4693 ], [ 177.6214, 64.4703 ] ] ], [ [ [ 178.2542, 64.6375 ], [ 178.2557, 64.6359 ], [ 178.2422, 64.6359 ], [ 178.2437, 64.6375 ], [ 178.2542, 64.6375 ] ] ], [ [ [ 176.4724, 65.0495 ], [ 176.4667, 65.0458 ], [ 176.4604, 65.0479 ], [ 176.4563, 65.0458 ], [ 176.4484, 65.0484 ], [ 176.4464, 65.0516 ], [ 176.4542, 65.0542 ], [ 176.4724, 65.0495 ] ] ], [ [ [ 177.4521, 69.5896 ], [ 177.4505, 69.5911 ], [ 177.4557, 69.5911 ], [ 177.4557, 69.5901 ], [ 177.4521, 69.5896 ] ] ], [ [ [ 177.4458, 69.5917 ], [ 177.4443, 69.5932 ], [ 177.4495, 69.5932 ], [ 177.4495, 69.5922 ], [ 177.4458, 69.5917 ] ] ], [ [ [ 170.4833, 68.8063 ], [ 170.4646, 68.7979 ], [ 170.4583, 68.7979 ], [ 170.4547, 68.8005 ], [ 170.462, 68.8047 ], [ 170.463, 68.8078 ], [ 170.4724, 68.8089 ], [ 170.4693, 68.813 ], [ 170.4708, 68.8146 ], [ 170.4792, 68.8146 ], [ 170.4818, 68.8109 ], [ 170.4891, 68.8099 ], [ 170.4833, 68.8063 ] ] ], [ [ [ 170.6938, 68.85 ], [ 170.6964, 68.8464 ], [ 170.7036, 68.8453 ], [ 170.7036, 68.8422 ], [ 170.6859, 68.8464 ], [ 170.6828, 68.8495 ], [ 170.6714, 68.8526 ], [ 170.6693, 68.8557 ], [ 170.6771, 68.8562 ], [ 170.687, 68.8536 ], [ 170.688, 68.8505 ], [ 170.6938, 68.85 ] ] ], [ [ [ 170.8375, 68.975 ], [ 170.8391, 68.9734 ], [ 170.8354, 68.9729 ], [ 170.8354, 68.9708 ], [ 170.8297, 68.9714 ], [ 170.8338, 68.9745 ], [ 170.8375, 68.975 ] ] ], [ [ [ 171.0068, 69.0651 ], [ 171.0068, 69.0661 ], [ 171.0104, 69.0667 ], [ 171.0099, 69.0651 ], [ 171.0068, 69.0651 ] ] ], [ [ [ 177.3708, 69.6 ], [ 177.3745, 69.5984 ], [ 177.3589, 69.5984 ], [ 177.3604, 69.6 ], [ 177.3708, 69.6 ] ] ], [ [ [ 177.3109, 69.6016 ], [ 177.3141, 69.6016 ], [ 177.3141, 69.6005 ], [ 177.3109, 69.6005 ], [ 177.3109, 69.6016 ] ] ], [ [ [ 177.3557, 69.6005 ], [ 177.3292, 69.6 ], [ 177.3271, 69.6021 ], [ 177.3292, 69.6042 ], [ 177.3375, 69.6042 ], [ 177.3417, 69.6021 ], [ 177.3542, 69.6021 ], [ 177.3557, 69.6005 ] ] ], [ [ [ 177.3167, 69.6021 ], [ 177.3151, 69.6036 ], [ 177.3203, 69.6036 ], [ 177.3203, 69.6026 ], [ 177.3167, 69.6021 ] ] ], [ [ [ 164.4479, 69.6083 ], [ 164.4563, 69.6083 ], [ 164.4599, 69.6047 ], [ 164.4458, 69.6042 ], [ 164.4422, 69.6068 ], [ 164.4438, 69.6083 ], [ 164.4479, 69.6083 ] ] ], [ [ [ 168.2557, 69.6495 ], [ 168.2526, 69.6474 ], [ 168.2526, 69.6443 ], [ 168.2547, 69.6422 ], [ 168.2604, 69.6417 ], [ 168.262, 69.6401 ], [ 168.2458, 69.6354 ], [ 168.2375, 69.6354 ], [ 168.2333, 69.6375 ], [ 168.2167, 69.6375 ], [ 168.2141, 69.6412 ], [ 168.2068, 69.6422 ], [ 168.2125, 69.6479 ], [ 168.2307, 69.6484 ], [ 168.2255, 69.6505 ], [ 168.2214, 69.6547 ], [ 168.2266, 69.6568 ], [ 168.2255, 69.6599 ], [ 168.2271, 69.6604 ], [ 168.2412, 69.6599 ], [ 168.2443, 69.6568 ], [ 168.2516, 69.6557 ], [ 168.2557, 69.6495 ] ] ], [ [ [ 168.0917, 69.6646 ], [ 168.088, 69.6652 ], [ 168.088, 69.6661 ], [ 168.0932, 69.6661 ], [ 168.0917, 69.6646 ] ] ], [ [ [ 170.0875, 69.6958 ], [ 170.0854, 69.6937 ], [ 170.0891, 69.687 ], [ 170.0875, 69.6854 ], [ 170.0776, 69.6859 ], [ 170.0828, 69.688 ], [ 170.0813, 69.6896 ], [ 170.0714, 69.6891 ], [ 170.0688, 69.6854 ], [ 170.0562, 69.6854 ], [ 170.0401, 69.6901 ], [ 170.0375, 69.6937 ], [ 170.038, 69.6953 ], [ 170.0505, 69.7016 ], [ 170.0583, 69.7021 ], [ 170.0625, 69.7042 ], [ 170.0771, 69.7042 ], [ 170.087, 69.7016 ], [ 170.0891, 69.6974 ], [ 170.0875, 69.6958 ] ] ], [ [ [ 170.2667, 69.7042 ], [ 170.2813, 69.7042 ], [ 170.2828, 69.7026 ], [ 170.2547, 69.7026 ], [ 170.2563, 69.7042 ], [ 170.2667, 69.7042 ] ] ], [ [ [ 168.0708, 69.725 ], [ 168.0693, 69.7266 ], [ 168.0745, 69.7266 ], [ 168.0745, 69.7255 ], [ 168.0708, 69.725 ] ] ], [ [ [ 170.2229, 69.7625 ], [ 170.2224, 69.7609 ], [ 170.2151, 69.7578 ], [ 170.2161, 69.7526 ], [ 170.2088, 69.7495 ], [ 170.2078, 69.7464 ], [ 170.2005, 69.7432 ], [ 170.1995, 69.7401 ], [ 170.1818, 69.7328 ], [ 170.1828, 69.7297 ], [ 170.175, 69.7271 ], [ 170.1672, 69.7266 ], [ 170.1682, 69.7234 ], [ 170.1547, 69.7182 ], [ 170.1557, 69.7151 ], [ 170.15, 69.7125 ], [ 170.1401, 69.712 ], [ 170.1411, 69.7089 ], [ 170.1333, 69.7083 ], [ 170.125, 69.7042 ], [ 170.1151, 69.7047 ], [ 170.1125, 69.7083 ], [ 170.1162, 69.712 ], [ 170.1089, 69.713 ], [ 170.1037, 69.7203 ], [ 170.0984, 69.7214 ], [ 170.0938, 69.725 ], [ 170.0792, 69.725 ], [ 170.075, 69.7271 ], [ 170.0667, 69.7271 ], [ 170.0625, 69.7292 ], [ 170.0542, 69.7292 ], [ 170.0479, 69.7271 ], [ 170.0437, 69.7292 ], [ 170.0292, 69.7292 ], [ 170.025, 69.7312 ], [ 170.0167, 69.7292 ], [ 170.0151, 69.7307 ], [ 170.0182, 69.7328 ], [ 170.0083, 69.7333 ], [ 170.0036, 69.737 ], [ 169.9984, 69.738 ], [ 170.0109, 69.7453 ], [ 170.0161, 69.7464 ], [ 170.0271, 69.7521 ], [ 170.0333, 69.7521 ], [ 170.0437, 69.7562 ], [ 170.05, 69.7562 ], [ 170.0583, 69.7604 ], [ 170.0646, 69.7604 ], [ 170.0792, 69.7646 ], [ 170.0875, 69.7646 ], [ 170.1021, 69.7688 ], [ 170.1146, 69.7688 ], [ 170.1187, 69.7708 ], [ 170.1375, 69.7708 ], [ 170.1417, 69.7729 ], [ 170.1833, 69.7729 ], [ 170.1875, 69.775 ], [ 170.2328, 69.7745 ], [ 170.2312, 69.7729 ], [ 170.225, 69.7729 ], [ 170.2193, 69.7703 ], [ 170.2266, 69.7662 ], [ 170.2229, 69.7625 ] ] ], [ [ [ 167.8276, 69.7776 ], [ 167.8286, 69.7755 ], [ 167.825, 69.775 ], [ 167.8214, 69.7786 ], [ 167.825, 69.7792 ], [ 167.8276, 69.7776 ] ] ], [ [ [ 176.7708, 69.6646 ], [ 176.7693, 69.6661 ], [ 176.7745, 69.6661 ], [ 176.7745, 69.6651 ], [ 176.7708, 69.6646 ] ] ], [ [ [ 175.9208, 69.8396 ], [ 175.9417, 69.8438 ], [ 175.9438, 69.8375 ], [ 175.9432, 69.8359 ], [ 175.925, 69.8313 ], [ 175.9151, 69.8318 ], [ 175.9151, 69.8349 ], [ 175.9182, 69.8359 ], [ 175.9208, 69.8396 ] ] ], [ [ [ 175.1583, 69.8375 ], [ 175.1568, 69.8391 ], [ 175.162, 69.8391 ], [ 175.162, 69.838 ], [ 175.1583, 69.8375 ] ] ], [ [ [ 175.1458, 69.8393 ], [ 175.1495, 69.8391 ], [ 175.1495, 69.838 ], [ 175.1458, 69.8375 ], [ 175.1458, 69.8393 ] ] ], [ [ [ 167.7521, 69.7833 ], [ 167.7505, 69.7849 ], [ 167.7557, 69.7849 ], [ 167.7557, 69.7839 ], [ 167.7521, 69.7833 ] ] ], [ [ [ 167.8021, 69.7875 ], [ 167.8125, 69.7875 ], [ 167.8141, 69.7859 ], [ 167.7901, 69.7859 ], [ 167.7917, 69.7875 ], [ 167.8021, 69.7875 ] ] ], [ [ [ 168.538, 70.0151 ], [ 168.5417, 70.0146 ], [ 168.5458, 70.0167 ], [ 168.5604, 70.0167 ], [ 168.562, 70.0151 ], [ 168.5568, 70.013 ], [ 168.5688, 70.0125 ], [ 168.5729, 70.0104 ], [ 168.5771, 70.0104 ], [ 168.5797, 70.0068 ], [ 168.587, 70.0057 ], [ 168.5838, 70.0026 ], [ 168.5938, 70.0021 ], [ 168.6, 69.9979 ], [ 168.6083, 69.9979 ], [ 168.6146, 69.9938 ], [ 168.625, 69.9938 ], [ 168.6292, 69.9917 ], [ 168.6354, 69.9938 ], [ 168.6396, 69.9917 ], [ 168.65, 69.9917 ], [ 168.6542, 69.9896 ], [ 168.675, 69.9896 ], [ 168.6792, 69.9875 ], [ 168.6958, 69.9875 ], [ 168.7, 69.9854 ], [ 168.7083, 69.9854 ], [ 168.7125, 69.9833 ], [ 168.7292, 69.9833 ], [ 168.7396, 69.9792 ], [ 168.7563, 69.9792 ], [ 168.7599, 69.9766 ], [ 168.7609, 69.9734 ], [ 168.7708, 69.9708 ], [ 168.7792, 69.9708 ], [ 168.7833, 69.9729 ], [ 168.7958, 69.9729 ], [ 168.8021, 69.9688 ], [ 168.8104, 69.9688 ], [ 168.8146, 69.9708 ], [ 168.8354, 69.9708 ], [ 168.8396, 69.9688 ], [ 168.8542, 69.9688 ], [ 168.8583, 69.9667 ], [ 168.8813, 69.9646 ], [ 168.9, 69.9583 ], [ 168.9063, 69.9604 ], [ 168.9333, 69.9604 ], [ 168.9375, 69.9583 ], [ 168.9521, 69.9583 ], [ 168.9563, 69.9563 ], [ 168.9833, 69.9563 ], [ 168.9875, 69.9542 ], [ 169.0042, 69.9542 ], [ 169.0083, 69.9521 ], [ 169.0182, 69.9516 ], [ 169.0229, 69.9479 ], [ 169.0292, 69.95 ], [ 169.05, 69.95 ], [ 169.0578, 69.9464 ], [ 169.0479, 69.9458 ], [ 169.0437, 69.9479 ], [ 169.0375, 69.9479 ], [ 169.0359, 69.9464 ], [ 169.0412, 69.9453 ], [ 169.0443, 69.9401 ], [ 169.0516, 69.9391 ], [ 169.0484, 69.9359 ], [ 169.0537, 69.9339 ], [ 169.0505, 69.9318 ], [ 169.0578, 69.9307 ], [ 169.0604, 69.9271 ], [ 169.0708, 69.9271 ], [ 169.0786, 69.9245 ], [ 169.0838, 69.9193 ], [ 169.0891, 69.9182 ], [ 169.0943, 69.913 ], [ 169.1141, 69.9078 ], [ 169.1193, 69.9026 ], [ 169.1271, 69.9 ], [ 169.1375, 69.9 ], [ 169.1417, 69.8979 ], [ 169.1563, 69.8979 ], [ 169.1604, 69.8958 ], [ 169.1875, 69.8958 ], [ 169.1917, 69.8979 ], [ 169.2229, 69.8979 ], [ 169.2417, 69.8917 ], [ 169.2521, 69.8917 ], [ 169.2568, 69.8859 ], [ 169.2688, 69.8854 ], [ 169.2792, 69.8812 ], [ 169.3, 69.8812 ], [ 169.3042, 69.8792 ], [ 169.3396, 69.8771 ], [ 169.3458, 69.8729 ], [ 169.3813, 69.8688 ], [ 169.3938, 69.8646 ], [ 169.4021, 69.8646 ], [ 169.4063, 69.8625 ], [ 169.4104, 69.8625 ], [ 169.4208, 69.8583 ], [ 169.4328, 69.8578 ], [ 169.4297, 69.8547 ], [ 169.4432, 69.8516 ], [ 169.438, 69.8484 ], [ 169.4422, 69.8443 ], [ 169.4474, 69.8432 ], [ 169.4516, 69.8391 ], [ 169.4464, 69.837 ], [ 169.4536, 69.8266 ], [ 169.4484, 69.8234 ], [ 169.4557, 69.8224 ], [ 169.4599, 69.8151 ], [ 169.4563, 69.8125 ], [ 169.4578, 69.8068 ], [ 169.4505, 69.8026 ], [ 169.4563, 69.8021 ], [ 169.4578, 69.8005 ], [ 169.4526, 69.7995 ], [ 169.4557, 69.7943 ], [ 169.4505, 69.7932 ], [ 169.437, 69.7859 ], [ 169.4276, 69.7828 ], [ 169.4287, 69.7776 ], [ 169.4208, 69.7771 ], [ 169.4125, 69.7729 ], [ 169.4047, 69.7724 ], [ 169.4068, 69.7693 ], [ 169.412, 69.7682 ], [ 169.4146, 69.7646 ], [ 169.4141, 69.763 ], [ 169.4068, 69.7599 ], [ 169.4078, 69.7568 ], [ 169.4, 69.7542 ], [ 169.3938, 69.7542 ], [ 169.3896, 69.7562 ], [ 169.3687, 69.7542 ], [ 169.3646, 69.7562 ], [ 169.35, 69.7562 ], [ 169.3458, 69.7583 ], [ 169.3188, 69.7583 ], [ 169.3146, 69.7562 ], [ 169.3021, 69.7562 ], [ 169.2922, 69.7589 ], [ 169.2875, 69.7625 ], [ 169.2713, 69.7557 ], [ 169.2713, 69.7526 ], [ 169.2755, 69.7484 ], [ 169.2807, 69.7474 ], [ 169.2828, 69.7453 ], [ 169.2797, 69.7422 ], [ 169.2849, 69.7412 ], [ 169.2964, 69.7338 ], [ 169.3037, 69.7328 ], [ 169.3047, 69.7297 ], [ 169.312, 69.7286 ], [ 169.3062, 69.725 ], [ 169.2937, 69.725 ], [ 169.2755, 69.7203 ], [ 169.2672, 69.7161 ], [ 169.2682, 69.713 ], [ 169.263, 69.712 ], [ 169.2651, 69.7089 ], [ 169.2786, 69.7057 ], [ 169.2818, 69.7005 ], [ 169.2891, 69.6995 ], [ 169.2912, 69.6964 ], [ 169.2859, 69.6943 ], [ 169.2917, 69.6937 ], [ 169.2953, 69.6911 ], [ 169.2953, 69.688 ], [ 169.2922, 69.687 ], [ 169.2953, 69.6797 ], [ 169.2917, 69.6771 ], [ 169.2943, 69.6734 ], [ 169.3016, 69.6724 ], [ 169.2964, 69.6703 ], [ 169.3016, 69.662 ], [ 169.2984, 69.6589 ], [ 169.3037, 69.6578 ], [ 169.3005, 69.6557 ], [ 169.3037, 69.6495 ], [ 169.3005, 69.6474 ], [ 169.3016, 69.6422 ], [ 169.2943, 69.6391 ], [ 169.2932, 69.6359 ], [ 169.2901, 69.6349 ], [ 169.2912, 69.6318 ], [ 169.2818, 69.6286 ], [ 169.2828, 69.6255 ], [ 169.2797, 69.6245 ], [ 169.2807, 69.6214 ], [ 169.2755, 69.6203 ], [ 169.2766, 69.6172 ], [ 169.2693, 69.6141 ], [ 169.2682, 69.6109 ], [ 169.2651, 69.6099 ], [ 169.2661, 69.6068 ], [ 169.263, 69.6057 ], [ 169.262, 69.6026 ], [ 169.2505, 69.5974 ], [ 169.2495, 69.5943 ], [ 169.2422, 69.5911 ], [ 169.2432, 69.588 ], [ 169.238, 69.587 ], [ 169.2391, 69.5839 ], [ 169.2255, 69.5787 ], [ 169.2266, 69.5755 ], [ 169.2193, 69.5724 ], [ 169.2182, 69.5693 ], [ 169.2088, 69.5682 ], [ 169.1953, 69.5609 ], [ 169.1901, 69.5599 ], [ 169.1911, 69.5568 ], [ 169.187, 69.5547 ], [ 169.1708, 69.5542 ], [ 169.1667, 69.5563 ], [ 169.1521, 69.5563 ], [ 169.1479, 69.5583 ], [ 169.1083, 69.5583 ], [ 169.1042, 69.5604 ], [ 169.0979, 69.5583 ], [ 169.0917, 69.5583 ], [ 169.0875, 69.5604 ], [ 169.0708, 69.5604 ], [ 169.0667, 69.5625 ], [ 169.0458, 69.5625 ], [ 169.0417, 69.5646 ], [ 169.0167, 69.5646 ], [ 169.0021, 69.5604 ], [ 168.9896, 69.5604 ], [ 168.9812, 69.5563 ], [ 168.9563, 69.5563 ], [ 168.9521, 69.5542 ], [ 168.9146, 69.5542 ], [ 168.9104, 69.5563 ], [ 168.9021, 69.5563 ], [ 168.8979, 69.5583 ], [ 168.8833, 69.5583 ], [ 168.8792, 69.5604 ], [ 168.8687, 69.5604 ], [ 168.8646, 69.5625 ], [ 168.8188, 69.5646 ], [ 168.8, 69.5708 ], [ 168.7729, 69.5708 ], [ 168.7688, 69.5729 ], [ 168.7479, 69.5729 ], [ 168.7437, 69.575 ], [ 168.7333, 69.575 ], [ 168.7292, 69.5771 ], [ 168.7146, 69.5771 ], [ 168.7021, 69.5813 ], [ 168.6812, 69.5813 ], [ 168.6771, 69.5833 ], [ 168.6542, 69.5854 ], [ 168.65, 69.5875 ], [ 168.6417, 69.5875 ], [ 168.6375, 69.5896 ], [ 168.6229, 69.5896 ], [ 168.6187, 69.5917 ], [ 168.6083, 69.5917 ], [ 168.5979, 69.5958 ], [ 168.5859, 69.5964 ], [ 168.5891, 69.5995 ], [ 168.5729, 69.6042 ], [ 168.5646, 69.6042 ], [ 168.5604, 69.6062 ], [ 168.5271, 69.6062 ], [ 168.5146, 69.6104 ], [ 168.5063, 69.6104 ], [ 168.5021, 69.6125 ], [ 168.4979, 69.6125 ], [ 168.4937, 69.6167 ], [ 168.4833, 69.6167 ], [ 168.4646, 69.6229 ], [ 168.45, 69.6229 ], [ 168.4401, 69.6255 ], [ 168.4354, 69.6292 ], [ 168.4271, 69.6292 ], [ 168.4146, 69.6333 ], [ 168.3938, 69.6333 ], [ 168.3896, 69.6354 ], [ 168.3813, 69.6354 ], [ 168.3771, 69.6375 ], [ 168.3542, 69.6396 ], [ 168.3443, 69.6422 ], [ 168.3417, 69.6458 ], [ 168.3313, 69.6458 ], [ 168.3234, 69.6484 ], [ 168.3188, 69.6521 ], [ 168.3104, 69.65 ], [ 168.3089, 69.6516 ], [ 168.312, 69.6526 ], [ 168.3104, 69.6542 ], [ 168.3021, 69.6542 ], [ 168.2833, 69.6604 ], [ 168.2729, 69.6604 ], [ 168.2682, 69.6641 ], [ 168.2521, 69.6687 ], [ 168.2375, 69.6687 ], [ 168.2312, 69.6729 ], [ 168.2083, 69.675 ], [ 168.2042, 69.6771 ], [ 168.1854, 69.675 ], [ 168.1667, 69.6813 ], [ 168.1609, 69.6818 ], [ 168.1536, 69.6911 ], [ 168.1401, 69.6943 ], [ 168.1286, 69.7016 ], [ 168.1214, 69.7026 ], [ 168.1182, 69.7078 ], [ 168.1109, 69.7089 ], [ 168.1182, 69.7161 ], [ 168.1109, 69.7255 ], [ 168.1162, 69.7286 ], [ 168.1104, 69.7292 ], [ 168.1089, 69.7307 ], [ 168.1182, 69.7318 ], [ 168.1151, 69.738 ], [ 168.1182, 69.7412 ], [ 168.1109, 69.7422 ], [ 168.1083, 69.7458 ], [ 168.0833, 69.75 ], [ 168.0786, 69.7536 ], [ 168.0708, 69.7562 ], [ 168.0625, 69.7542 ], [ 168.0562, 69.7583 ], [ 168.0333, 69.7604 ], [ 168.0229, 69.7646 ], [ 168.0125, 69.7646 ], [ 168.0083, 69.7667 ], [ 168.0, 69.7667 ], [ 167.9958, 69.7688 ], [ 167.9792, 69.7688 ], [ 167.9688, 69.7729 ], [ 167.9583, 69.7729 ], [ 167.9396, 69.7792 ], [ 167.9339, 69.7797 ], [ 167.9292, 69.7833 ], [ 167.8958, 69.7854 ], [ 167.8833, 69.7896 ], [ 167.85, 69.7896 ], [ 167.8375, 69.7937 ], [ 167.8292, 69.7937 ], [ 167.825, 69.7958 ], [ 167.8208, 69.7958 ], [ 167.8161, 69.7995 ], [ 167.8109, 69.8005 ], [ 167.8078, 69.8037 ], [ 167.8, 69.8063 ], [ 167.7958, 69.8063 ], [ 167.7917, 69.8083 ], [ 167.7833, 69.8063 ], [ 167.7797, 69.8089 ], [ 167.7891, 69.813 ], [ 167.788, 69.8161 ], [ 167.7953, 69.8193 ], [ 167.7964, 69.8224 ], [ 167.7995, 69.8234 ], [ 167.7984, 69.8266 ], [ 167.8057, 69.8297 ], [ 167.8068, 69.8328 ], [ 167.8182, 69.838 ], [ 167.8172, 69.8411 ], [ 167.8307, 69.8464 ], [ 167.8297, 69.8495 ], [ 167.8391, 69.8526 ], [ 167.838, 69.8557 ], [ 167.8495, 69.8609 ], [ 167.8505, 69.8641 ], [ 167.8662, 69.8714 ], [ 167.8672, 69.8745 ], [ 167.8745, 69.8776 ], [ 167.8734, 69.8807 ], [ 167.887, 69.8859 ], [ 167.8859, 69.8891 ], [ 167.8901, 69.8912 ], [ 167.8995, 69.8922 ], [ 167.9005, 69.8953 ], [ 167.9078, 69.8984 ], [ 167.9068, 69.9016 ], [ 167.912, 69.9026 ], [ 167.9287, 69.9109 ], [ 167.9297, 69.9141 ], [ 167.9354, 69.9167 ], [ 167.9432, 69.9172 ], [ 167.9797, 69.9349 ], [ 167.9896, 69.9354 ], [ 168.0047, 69.9432 ], [ 168.0141, 69.9443 ], [ 168.0182, 69.9464 ], [ 168.0208, 69.95 ], [ 168.0271, 69.9479 ], [ 168.0354, 69.9479 ], [ 168.0396, 69.95 ], [ 168.0474, 69.9505 ], [ 168.0599, 69.9568 ], [ 168.0599, 69.9599 ], [ 168.0568, 69.9641 ], [ 168.0813, 69.975 ], [ 168.0938, 69.975 ], [ 168.1021, 69.9792 ], [ 168.112, 69.9797 ], [ 168.1162, 69.9818 ], [ 168.1151, 69.9849 ], [ 168.1208, 69.9875 ], [ 168.1271, 69.9875 ], [ 168.1313, 69.9896 ], [ 168.1396, 69.9875 ], [ 168.1453, 69.9901 ], [ 168.1463, 69.9932 ], [ 168.1505, 69.9953 ], [ 168.1583, 69.9958 ], [ 168.1625, 69.9979 ], [ 168.1708, 69.9979 ], [ 168.175, 69.9958 ], [ 168.1896, 69.9958 ], [ 168.1938, 69.9979 ], [ 168.2016, 69.9984 ], [ 168.2005, 70.0016 ], [ 168.2021, 70.0021 ], [ 168.2083, 70.0 ], [ 168.212, 70.0005 ], [ 168.2109, 70.0036 ], [ 168.2125, 70.0042 ], [ 168.2292, 70.0 ], [ 168.2307, 70.0005 ], [ 168.2297, 70.0036 ], [ 168.2312, 70.0042 ], [ 168.2563, 70.0042 ], [ 168.2625, 70.0062 ], [ 168.2641, 70.0047 ], [ 168.2609, 70.0026 ], [ 168.2646, 70.0021 ], [ 168.2708, 70.0042 ], [ 168.275, 70.0021 ], [ 168.2776, 70.0057 ], [ 168.2833, 70.0083 ], [ 168.2917, 70.0062 ], [ 168.2958, 70.0083 ], [ 168.3037, 70.0089 ], [ 168.3021, 70.0104 ], [ 168.2964, 70.0109 ], [ 168.2979, 70.0125 ], [ 168.3062, 70.0125 ], [ 168.3188, 70.0083 ], [ 168.325, 70.0104 ], [ 168.3313, 70.0083 ], [ 168.3438, 70.0083 ], [ 168.3542, 70.0125 ], [ 168.3833, 70.0146 ], [ 168.3875, 70.0167 ], [ 168.3938, 70.0146 ], [ 168.4, 70.0167 ], [ 168.4083, 70.0167 ], [ 168.4125, 70.0146 ], [ 168.4271, 70.0208 ], [ 168.4307, 70.0203 ], [ 168.4339, 70.0151 ], [ 168.4375, 70.0125 ], [ 168.4495, 70.0172 ], [ 168.4521, 70.0208 ], [ 168.4557, 70.0203 ], [ 168.4568, 70.0172 ], [ 168.4604, 70.0146 ], [ 168.4641, 70.0151 ], [ 168.463, 70.0182 ], [ 168.4646, 70.0188 ], [ 168.4708, 70.0167 ], [ 168.4792, 70.0167 ], [ 168.4854, 70.0188 ], [ 168.4979, 70.0146 ], [ 168.5125, 70.0208 ], [ 168.5208, 70.0208 ], [ 168.538, 70.0151 ] ] ], [ [ [ 167.7786, 69.7922 ], [ 167.7708, 69.7917 ], [ 167.7672, 69.7953 ], [ 167.7729, 69.7979 ], [ 167.7849, 69.7953 ], [ 167.7786, 69.7922 ] ] ], [ [ [ 170.5729, 69.8 ], [ 170.5688, 69.8 ], [ 170.5672, 69.8016 ], [ 170.5729, 69.8021 ], [ 170.5729, 69.8 ] ] ], [ [ [ 174.2458, 69.8667 ], [ 174.2443, 69.8682 ], [ 174.2495, 69.8682 ], [ 174.2495, 69.8672 ], [ 174.2458, 69.8667 ] ] ], [ [ [ 173.9208, 69.8792 ], [ 173.9193, 69.8807 ], [ 173.9245, 69.8807 ], [ 173.9245, 69.8797 ], [ 173.9208, 69.8792 ] ] ], [ [ [ 173.9682, 69.8859 ], [ 173.9604, 69.8854 ], [ 173.9588, 69.887 ], [ 173.9667, 69.8875 ], [ 173.9682, 69.8859 ] ] ], [ [ [ 175.275, 69.8396 ], [ 175.2688, 69.8417 ], [ 175.2604, 69.8417 ], [ 175.2526, 69.8453 ], [ 175.2583, 69.8479 ], [ 175.2667, 69.8458 ], [ 175.2708, 69.8479 ], [ 175.2833, 69.8479 ], [ 175.2875, 69.85 ], [ 175.3099, 69.8495 ], [ 175.3083, 69.8479 ], [ 175.2958, 69.8479 ], [ 175.2917, 69.8458 ], [ 175.2833, 69.8458 ], [ 175.2818, 69.8453 ], [ 175.2828, 69.8422 ], [ 175.275, 69.8396 ] ] ], [ [ [ 175.675, 69.8375 ], [ 175.6651, 69.8401 ], [ 175.662, 69.8453 ], [ 175.6568, 69.8474 ], [ 175.6662, 69.8484 ], [ 175.6609, 69.8505 ], [ 175.6625, 69.8521 ], [ 175.6724, 69.8516 ], [ 175.6771, 69.8396 ], [ 175.675, 69.8375 ] ] ], [ [ [ 175.3396, 69.85 ], [ 175.3354, 69.85 ], [ 175.3338, 69.8516 ], [ 175.3438, 69.8521 ], [ 175.3479, 69.8542 ], [ 175.3729, 69.8542 ], [ 175.3771, 69.8562 ], [ 175.3854, 69.8562 ], [ 175.3875, 69.8542 ], [ 175.3854, 69.8521 ], [ 175.3667, 69.8521 ], [ 175.3625, 69.85 ], [ 175.3542, 69.85 ], [ 175.35, 69.8479 ], [ 175.3396, 69.85 ] ] ], [ [ [ 175.4292, 69.8562 ], [ 175.425, 69.8562 ], [ 175.4208, 69.8542 ], [ 175.4125, 69.8542 ], [ 175.4083, 69.8562 ], [ 175.4, 69.8562 ], [ 175.3984, 69.8578 ], [ 175.4125, 69.8583 ], [ 175.4167, 69.8604 ], [ 175.4229, 69.8583 ], [ 175.4328, 69.8578 ], [ 175.4292, 69.8562 ] ] ], [ [ [ 175.8042, 69.8646 ], [ 175.8167, 69.8646 ], [ 175.8292, 69.8604 ], [ 175.8375, 69.8604 ], [ 175.8391, 69.8588 ], [ 175.8313, 69.8562 ], [ 175.8062, 69.8562 ], [ 175.7937, 69.8604 ], [ 175.7854, 69.8604 ], [ 175.7813, 69.8625 ], [ 175.7688, 69.8625 ], [ 175.7646, 69.8604 ], [ 175.7563, 69.8604 ], [ 175.7484, 69.8578 ], [ 175.7495, 69.8526 ], [ 175.7417, 69.85 ], [ 175.738, 69.8526 ], [ 175.7349, 69.862 ], [ 175.725, 69.8625 ], [ 175.7208, 69.8646 ], [ 175.6938, 69.8646 ], [ 175.6776, 69.8578 ], [ 175.675, 69.8542 ], [ 175.6687, 69.8562 ], [ 175.6589, 69.8568 ], [ 175.6578, 69.8599 ], [ 175.6542, 69.8625 ], [ 175.6333, 69.8625 ], [ 175.6313, 69.8646 ], [ 175.6333, 69.8667 ], [ 175.6396, 69.8646 ], [ 175.6479, 69.8646 ], [ 175.6521, 69.8667 ], [ 175.6958, 69.8667 ], [ 175.7, 69.8688 ], [ 175.7729, 69.8688 ], [ 175.7771, 69.8667 ], [ 175.7854, 69.8688 ], [ 175.7896, 69.8667 ], [ 175.7979, 69.8667 ], [ 175.8042, 69.8646 ] ] ], [ [ [ 175.4625, 69.8625 ], [ 175.4729, 69.8625 ], [ 175.4771, 69.8646 ], [ 175.4958, 69.8646 ], [ 175.4995, 69.8609 ], [ 175.4505, 69.8609 ], [ 175.4521, 69.8625 ], [ 175.4625, 69.8625 ] ] ], [ [ [ 175.6245, 69.863 ], [ 175.6167, 69.8625 ], [ 175.6151, 69.8641 ], [ 175.6229, 69.8646 ], [ 175.6245, 69.863 ] ] ], [ [ [ 175.5229, 69.8667 ], [ 175.5271, 69.8667 ], [ 175.5286, 69.8651 ], [ 175.5109, 69.8651 ], [ 175.5125, 69.8667 ], [ 175.5229, 69.8667 ] ] ], [ [ [ 175.6646, 69.8708 ], [ 175.6662, 69.8693 ], [ 175.6547, 69.8693 ], [ 175.6563, 69.8708 ], [ 175.6646, 69.8708 ] ] ], [ [ [ 175.6812, 69.8729 ], [ 175.6875, 69.8729 ], [ 175.6917, 69.875 ], [ 175.712, 69.8745 ], [ 175.7104, 69.8729 ], [ 175.6979, 69.8729 ], [ 175.6938, 69.8708 ], [ 175.6714, 69.8714 ], [ 175.6729, 69.8729 ], [ 175.6812, 69.8729 ] ] ], [ [ [ 169.3667, 69.9 ], [ 169.3703, 69.8974 ], [ 169.3724, 69.8943 ], [ 169.3672, 69.8943 ], [ 169.363, 69.8984 ], [ 169.3667, 69.9 ] ] ], [ [ [ 169.2, 69.9396 ], [ 169.2063, 69.9375 ], [ 169.2146, 69.9375 ], [ 169.2271, 69.9333 ], [ 169.2625, 69.9292 ], [ 169.2833, 69.9229 ], [ 169.2917, 69.9229 ], [ 169.2979, 69.9187 ], [ 169.3042, 69.9208 ], [ 169.3104, 69.9167 ], [ 169.3224, 69.9161 ], [ 169.3234, 69.913 ], [ 169.3307, 69.912 ], [ 169.3338, 69.9089 ], [ 169.3438, 69.9083 ], [ 169.3537, 69.9057 ], [ 169.3542, 69.9042 ], [ 169.3521, 69.9021 ], [ 169.3438, 69.9021 ], [ 169.3333, 69.9063 ], [ 169.325, 69.9042 ], [ 169.3062, 69.9104 ], [ 169.3005, 69.9109 ], [ 169.3057, 69.913 ], [ 169.3042, 69.9146 ], [ 169.3, 69.9146 ], [ 169.2958, 69.9167 ], [ 169.2875, 69.9146 ], [ 169.2771, 69.9187 ], [ 169.2667, 69.9187 ], [ 169.2625, 69.9208 ], [ 169.2563, 69.9187 ], [ 169.2312, 69.9187 ], [ 169.2271, 69.9208 ], [ 169.2104, 69.9208 ], [ 169.2078, 69.9245 ], [ 169.1922, 69.9276 ], [ 169.187, 69.9349 ], [ 169.1734, 69.938 ], [ 169.1812, 69.9417 ], [ 169.1854, 69.9396 ], [ 169.2, 69.9396 ] ] ], [ [ [ 169.1125, 69.9354 ], [ 169.1162, 69.9318 ], [ 169.1047, 69.9318 ], [ 169.1047, 69.9328 ], [ 169.1125, 69.9354 ] ] ], [ [ [ 169.1463, 69.9443 ], [ 169.1625, 69.9437 ], [ 169.1724, 69.9401 ], [ 169.1333, 69.9396 ], [ 169.1276, 69.937 ], [ 169.1266, 69.9339 ], [ 169.1229, 69.9333 ], [ 169.1125, 69.9375 ], [ 169.1062, 69.9354 ], [ 169.0979, 69.9354 ], [ 169.0964, 69.937 ], [ 169.1042, 69.9375 ], [ 169.1057, 69.9391 ], [ 169.0958, 69.9396 ], [ 169.0896, 69.9375 ], [ 169.0797, 69.9422 ], [ 169.0854, 69.9479 ], [ 169.1292, 69.9479 ], [ 169.1333, 69.9458 ], [ 169.1438, 69.9458 ], [ 169.1463, 69.9443 ] ] ], [ [ [ 168.5422, 70.0203 ], [ 168.5453, 70.0203 ], [ 168.5453, 70.0193 ], [ 168.5422, 70.0193 ], [ 168.5422, 70.0203 ] ] ], [ [ [ 170.5729, 70.0875 ], [ 170.575, 70.0854 ], [ 170.5729, 70.0833 ], [ 170.5646, 70.0833 ], [ 170.5625, 70.0854 ], [ 170.5646, 70.0875 ], [ 170.5729, 70.0875 ] ] ], [ [ [ 170.5667, 70.0917 ], [ 170.5651, 70.0932 ], [ 170.5703, 70.0932 ], [ 170.5703, 70.0922 ], [ 170.5667, 70.0917 ] ] ], [ [ [ 170.5703, 70.112 ], [ 170.5672, 70.1099 ], [ 170.5693, 70.1068 ], [ 170.5807, 70.1057 ], [ 170.5849, 70.0984 ], [ 170.5792, 70.0958 ], [ 170.5708, 70.0979 ], [ 170.5589, 70.0932 ], [ 170.5599, 70.0901 ], [ 170.5521, 70.0875 ], [ 170.5146, 70.0875 ], [ 170.5104, 70.0896 ], [ 170.4875, 70.0896 ], [ 170.4771, 70.0938 ], [ 170.4563, 70.0938 ], [ 170.4521, 70.0958 ], [ 170.4417, 70.0958 ], [ 170.4339, 70.0984 ], [ 170.4297, 70.1026 ], [ 170.4349, 70.1057 ], [ 170.4276, 70.1068 ], [ 170.4292, 70.1083 ], [ 170.4479, 70.1083 ], [ 170.4521, 70.1104 ], [ 170.4604, 70.1104 ], [ 170.4646, 70.1125 ], [ 170.5063, 70.1125 ], [ 170.5104, 70.1146 ], [ 170.525, 70.1104 ], [ 170.5313, 70.1125 ], [ 170.5375, 70.1125 ], [ 170.5417, 70.1146 ], [ 170.5703, 70.112 ] ] ], [ [ [ 170.5979, 70.1042 ], [ 170.6016, 70.1036 ], [ 170.6016, 70.1005 ], [ 170.5901, 70.1005 ], [ 170.5979, 70.1042 ] ] ], [ [ [ 170.5813, 70.1104 ], [ 170.5776, 70.1109 ], [ 170.5776, 70.112 ], [ 170.5828, 70.112 ], [ 170.5813, 70.1104 ] ] ], [ [ [ 179.5479, 69.1708 ], [ 179.5495, 69.1693 ], [ 179.538, 69.1693 ], [ 179.5396, 69.1708 ], [ 179.5479, 69.1708 ] ] ], [ [ [ 179.4125, 69.2188 ], [ 179.4047, 69.2224 ], [ 179.4146, 69.225 ], [ 179.4172, 69.2214 ], [ 179.4224, 69.2203 ], [ 179.4208, 69.2188 ], [ 179.4125, 69.2188 ] ] ], [ [ [ 179.4047, 69.2286 ], [ 179.4078, 69.2286 ], [ 179.4078, 69.2276 ], [ 179.4047, 69.2276 ], [ 179.4047, 69.2286 ] ] ], [ [ [ 178.8286, 69.2703 ], [ 178.8286, 69.2672 ], [ 178.8193, 69.2672 ], [ 178.8234, 69.2703 ], [ 178.8286, 69.2703 ] ] ], [ [ [ 179.2417, 69.275 ], [ 179.2453, 69.2734 ], [ 179.2312, 69.2729 ], [ 179.2271, 69.2708 ], [ 179.2146, 69.2708 ], [ 179.2104, 69.2688 ], [ 179.2068, 69.2693 ], [ 179.2063, 69.2708 ], [ 179.2125, 69.2771 ], [ 179.2208, 69.2771 ], [ 179.225, 69.275 ], [ 179.2417, 69.275 ] ] ], [ [ [ 179.2078, 69.287 ], [ 179.2078, 69.2839 ], [ 179.2063, 69.2833 ], [ 179.2026, 69.2859 ], [ 179.2042, 69.2875 ], [ 179.2078, 69.287 ] ] ], [ [ [ 179.1266, 69.3068 ], [ 179.1229, 69.3063 ], [ 179.1214, 69.3078 ], [ 179.125, 69.3083 ], [ 179.1266, 69.3068 ] ] ], [ [ [ 179.0292, 69.3333 ], [ 179.0276, 69.3349 ], [ 179.0328, 69.3349 ], [ 179.0328, 69.3339 ], [ 179.0292, 69.3333 ] ] ], [ [ [ 178.4557, 69.4537 ], [ 178.4542, 69.4521 ], [ 178.4443, 69.4526 ], [ 178.4458, 69.4542 ], [ 178.4557, 69.4537 ] ] ], [ [ [ 178.725, 70.9563 ], [ 178.7229, 70.9583 ], [ 178.725, 70.9604 ], [ 178.7349, 70.9578 ], [ 178.7333, 70.9563 ], [ 178.725, 70.9563 ] ] ], [ [ [ 178.7125, 70.9583 ], [ 178.7109, 70.9599 ], [ 178.7161, 70.9599 ], [ 178.7161, 70.9589 ], [ 178.7125, 70.9583 ] ] ], [ [ [ 178.7063, 70.9708 ], [ 178.7083, 70.9688 ], [ 178.7063, 70.9667 ], [ 178.6979, 70.9667 ], [ 178.6958, 70.9688 ], [ 178.6979, 70.9708 ], [ 178.7063, 70.9708 ] ] ], [ [ [ 179.5313, 70.7354 ], [ 179.5297, 70.737 ], [ 179.5349, 70.737 ], [ 179.5349, 70.7359 ], [ 179.5313, 70.7354 ] ] ], [ [ [ 178.7729, 70.9812 ], [ 178.7713, 70.9828 ], [ 178.7766, 70.9828 ], [ 178.7766, 70.9818 ], [ 178.7729, 70.9812 ] ] ], [ [ [ 178.9625, 71.0813 ], [ 178.9609, 71.0828 ], [ 178.9661, 71.0828 ], [ 178.9661, 71.0818 ], [ 178.9625, 71.0813 ] ] ], [ [ [ 178.8792, 71.0875 ], [ 178.8776, 71.0891 ], [ 178.8828, 71.0891 ], [ 178.8828, 71.088 ], [ 178.8792, 71.0875 ] ] ], [ [ [ 178.6542, 70.9771 ], [ 178.6505, 70.9776 ], [ 178.6505, 70.9786 ], [ 178.6557, 70.9786 ], [ 178.6542, 70.9771 ] ] ], [ [ [ 178.7417, 70.9896 ], [ 178.738, 70.9901 ], [ 178.738, 70.9912 ], [ 178.7432, 70.9912 ], [ 178.7417, 70.9896 ] ] ], [ [ [ 178.7021, 71.0 ], [ 178.6984, 71.0005 ], [ 178.6984, 71.0016 ], [ 178.7036, 71.0016 ], [ 178.7021, 71.0 ] ] ], [ [ [ 178.6625, 71.0062 ], [ 178.6589, 71.0068 ], [ 178.6589, 71.0078 ], [ 178.6641, 71.0078 ], [ 178.6625, 71.0062 ] ] ], [ [ [ 178.6438, 71.0354 ], [ 178.6474, 71.0339 ], [ 178.6339, 71.0339 ], [ 178.6354, 71.0354 ], [ 178.6438, 71.0354 ] ] ], [ [ [ 178.675, 71.0396 ], [ 178.6714, 71.0401 ], [ 178.6714, 71.0411 ], [ 178.6766, 71.0411 ], [ 178.675, 71.0396 ] ] ], [ [ [ 178.7161, 71.088 ], [ 178.7104, 71.0875 ], [ 178.7088, 71.0891 ], [ 178.7146, 71.0896 ], [ 178.7161, 71.088 ] ] ], [ [ [ 178.7458, 71.0917 ], [ 178.7422, 71.0922 ], [ 178.7422, 71.0932 ], [ 178.7474, 71.0932 ], [ 178.7458, 71.0917 ] ] ], [ [ [ 178.7125, 71.0917 ], [ 178.7088, 71.0922 ], [ 178.7088, 71.0932 ], [ 178.7141, 71.0932 ], [ 178.7125, 71.0917 ] ] ], [ [ [ 178.7, 71.0917 ], [ 178.6964, 71.0922 ], [ 178.6964, 71.0932 ], [ 178.7016, 71.0932 ], [ 178.7, 71.0917 ] ] ], [ [ [ 178.6812, 71.0938 ], [ 178.6776, 71.0943 ], [ 178.6776, 71.0953 ], [ 178.6828, 71.0953 ], [ 178.6812, 71.0938 ] ] ], [ [ [ 178.7724, 71.0984 ], [ 178.7667, 71.0979 ], [ 178.7651, 71.0995 ], [ 178.7708, 71.1 ], [ 178.7724, 71.0984 ] ] ], [ [ [ 178.8958, 71.0896 ], [ 178.8938, 71.0875 ], [ 178.8901, 71.088 ], [ 178.8896, 71.0896 ], [ 178.8917, 71.0917 ], [ 178.8953, 71.0911 ], [ 178.8958, 71.0896 ] ] ], [ [ [ 178.8729, 71.0917 ], [ 178.8745, 71.0901 ], [ 178.863, 71.0901 ], [ 178.8646, 71.0917 ], [ 178.8729, 71.0917 ] ] ], [ [ [ 178.7833, 71.1146 ], [ 178.7708, 71.1146 ], [ 178.7693, 71.1141 ], [ 178.7703, 71.1109 ], [ 178.7688, 71.1104 ], [ 178.7583, 71.1104 ], [ 178.7563, 71.1125 ], [ 178.7568, 71.1141 ], [ 178.7646, 71.1167 ], [ 178.7729, 71.1167 ], [ 178.7771, 71.1188 ], [ 178.7891, 71.1162 ], [ 178.7875, 71.1146 ], [ 178.7833, 71.1146 ] ] ], [ [ [ 178.9125, 71.1188 ], [ 178.9208, 71.1188 ], [ 178.9229, 71.1167 ], [ 178.9208, 71.1146 ], [ 178.9047, 71.1151 ], [ 178.9125, 71.1188 ] ] ], [ [ [ 178.7583, 71.1021 ], [ 178.7625, 71.1021 ], [ 178.7641, 71.1005 ], [ 178.7526, 71.1005 ], [ 178.7542, 71.1021 ], [ 178.7583, 71.1021 ] ] ], [ [ [ 178.7354, 71.1042 ], [ 178.75, 71.1042 ], [ 178.7516, 71.1026 ], [ 178.7354, 71.1021 ], [ 178.7312, 71.1 ], [ 178.7068, 71.1026 ], [ 178.7208, 71.1083 ], [ 178.7354, 71.1042 ] ] ], [ [ [ 178.8875, 71.1188 ], [ 178.8911, 71.1182 ], [ 178.8911, 71.1151 ], [ 178.8797, 71.1151 ], [ 178.8875, 71.1188 ] ] ], [ [ [ 178.8771, 71.1167 ], [ 178.8734, 71.1172 ], [ 178.8734, 71.1182 ], [ 178.8786, 71.1182 ], [ 178.8771, 71.1167 ] ] ], [ [ [ 178.7979, 71.1208 ], [ 178.7943, 71.1214 ], [ 178.7943, 71.1224 ], [ 178.7995, 71.1224 ], [ 178.7979, 71.1208 ] ] ], [ [ [ 178.85, 71.125 ], [ 178.8464, 71.1255 ], [ 178.8464, 71.1266 ], [ 178.8516, 71.1266 ], [ 178.85, 71.125 ] ] ], [ [ [ 178.8687, 71.1271 ], [ 178.8703, 71.1255 ], [ 178.8589, 71.1255 ], [ 178.8604, 71.1271 ], [ 178.8687, 71.1271 ] ] ], [ [ [ 178.9396, 71.1292 ], [ 178.9359, 71.1297 ], [ 178.9359, 71.1307 ], [ 178.9411, 71.1307 ], [ 178.9396, 71.1292 ] ] ], [ [ [ 178.9375, 71.1417 ], [ 178.9339, 71.1422 ], [ 178.9339, 71.1432 ], [ 178.9391, 71.1432 ], [ 178.9375, 71.1417 ] ] ], [ [ [ 179.9896, 70.9729 ], [ 179.975, 70.9729 ], [ 179.9708, 70.9708 ], [ 179.9438, 70.9708 ], [ 179.9396, 70.9688 ], [ 179.925, 70.9688 ], [ 179.9208, 70.9667 ], [ 179.9063, 70.9667 ], [ 179.8958, 70.9625 ], [ 179.8875, 70.9625 ], [ 179.8771, 70.9583 ], [ 179.8417, 70.9583 ], [ 179.8375, 70.9563 ], [ 179.8167, 70.9542 ], [ 179.8125, 70.9521 ], [ 179.7979, 70.9521 ], [ 179.7953, 70.9484 ], [ 179.7901, 70.9474 ], [ 179.7912, 70.9443 ], [ 179.7734, 70.9391 ], [ 179.7724, 70.9359 ], [ 179.7609, 70.9328 ], [ 179.7661, 70.9307 ], [ 179.7609, 70.9276 ], [ 179.7661, 70.9266 ], [ 179.7667, 70.925 ], [ 179.7661, 70.9213 ], [ 179.763, 70.9203 ], [ 179.762, 70.9172 ], [ 179.738, 70.912 ], [ 179.737, 70.9089 ], [ 179.7318, 70.9078 ], [ 179.7292, 70.9042 ], [ 179.7146, 70.9042 ], [ 179.7104, 70.9 ], [ 179.6896, 70.8979 ], [ 179.6755, 70.8932 ], [ 179.6745, 70.888 ], [ 179.663, 70.8849 ], [ 179.6641, 70.8818 ], [ 179.6625, 70.8812 ], [ 179.6417, 70.8792 ], [ 179.625, 70.8729 ], [ 179.6167, 70.8729 ], [ 179.6125, 70.8708 ], [ 179.5917, 70.8708 ], [ 179.5875, 70.8688 ], [ 179.5792, 70.8688 ], [ 179.575, 70.8708 ], [ 179.5583, 70.8708 ], [ 179.5557, 70.8745 ], [ 179.5505, 70.8766 ], [ 179.562, 70.8807 ], [ 179.5542, 70.8833 ], [ 179.5505, 70.8828 ], [ 179.5479, 70.8792 ], [ 179.5188, 70.8792 ], [ 179.5146, 70.8812 ], [ 179.5063, 70.8812 ], [ 179.5021, 70.8833 ], [ 179.4812, 70.8833 ], [ 179.4771, 70.8812 ], [ 179.45, 70.8812 ], [ 179.4422, 70.8786 ], [ 179.45, 70.875 ], [ 179.4807, 70.8745 ], [ 179.4729, 70.8708 ], [ 179.4583, 70.8708 ], [ 179.4542, 70.8688 ], [ 179.4063, 70.8688 ], [ 179.4021, 70.8708 ], [ 179.3875, 70.8708 ], [ 179.3833, 70.8729 ], [ 179.3479, 70.8729 ], [ 179.3438, 70.875 ], [ 179.2917, 70.875 ], [ 179.2875, 70.8729 ], [ 179.2604, 70.8729 ], [ 179.2563, 70.8708 ], [ 179.2479, 70.8729 ], [ 179.2437, 70.8708 ], [ 179.2292, 70.8708 ], [ 179.225, 70.8688 ], [ 179.1896, 70.8688 ], [ 179.1833, 70.8708 ], [ 179.1792, 70.8688 ], [ 179.1708, 70.8688 ], [ 179.1667, 70.8667 ], [ 179.1333, 70.8646 ], [ 179.1292, 70.8625 ], [ 179.1083, 70.8625 ], [ 179.1042, 70.8604 ], [ 179.0958, 70.8604 ], [ 179.0917, 70.8583 ], [ 179.0771, 70.8583 ], [ 179.0729, 70.8562 ], [ 179.0646, 70.8562 ], [ 179.063, 70.8557 ], [ 179.0641, 70.8526 ], [ 179.0562, 70.85 ], [ 179.0417, 70.85 ], [ 179.0375, 70.8479 ], [ 179.0292, 70.8479 ], [ 179.0188, 70.8438 ], [ 179.0104, 70.8438 ], [ 178.9937, 70.8375 ], [ 178.9854, 70.8375 ], [ 178.9547, 70.8266 ], [ 178.9536, 70.8234 ], [ 178.9438, 70.8229 ], [ 178.9411, 70.8193 ], [ 178.9271, 70.8146 ], [ 178.9187, 70.8146 ], [ 178.8943, 70.8057 ], [ 178.8917, 70.8021 ], [ 178.8771, 70.8021 ], [ 178.8542, 70.7937 ], [ 178.8458, 70.7937 ], [ 178.8375, 70.7896 ], [ 178.8208, 70.7896 ], [ 178.8193, 70.7901 ], [ 178.8193, 70.7932 ], [ 178.837, 70.7984 ], [ 178.838, 70.8016 ], [ 178.8432, 70.8037 ], [ 178.8125, 70.8042 ], [ 178.8047, 70.8068 ], [ 178.8047, 70.8099 ], [ 178.8099, 70.8109 ], [ 178.8109, 70.8141 ], [ 178.8224, 70.8151 ], [ 178.8208, 70.8187 ], [ 178.8214, 70.8224 ], [ 178.8266, 70.8245 ], [ 178.8188, 70.8271 ], [ 178.8068, 70.8276 ], [ 178.812, 70.8307 ], [ 178.8068, 70.8318 ], [ 178.8057, 70.837 ], [ 178.8005, 70.838 ], [ 178.7984, 70.8401 ], [ 178.7974, 70.8516 ], [ 178.7901, 70.8526 ], [ 178.7901, 70.8557 ], [ 178.7953, 70.8578 ], [ 178.7901, 70.8588 ], [ 178.7891, 70.862 ], [ 178.7755, 70.8651 ], [ 178.7745, 70.8682 ], [ 178.7693, 70.8693 ], [ 178.7708, 70.8708 ], [ 178.7807, 70.8714 ], [ 178.7818, 70.8745 ], [ 178.787, 70.8755 ], [ 178.7875, 70.8771 ], [ 178.787, 70.8786 ], [ 178.7797, 70.8797 ], [ 178.7849, 70.8818 ], [ 178.7849, 70.8849 ], [ 178.7734, 70.888 ], [ 178.7734, 70.8912 ], [ 178.7766, 70.8922 ], [ 178.7766, 70.8953 ], [ 178.7651, 70.8984 ], [ 178.762, 70.9036 ], [ 178.7568, 70.9047 ], [ 178.7557, 70.9078 ], [ 178.7443, 70.9109 ], [ 178.7432, 70.9141 ], [ 178.7297, 70.9151 ], [ 178.7287, 70.9203 ], [ 178.7234, 70.9213 ], [ 178.7203, 70.9307 ], [ 178.7151, 70.9318 ], [ 178.7141, 70.9349 ], [ 178.7068, 70.9359 ], [ 178.7057, 70.9453 ], [ 178.6958, 70.9458 ], [ 178.6859, 70.9484 ], [ 178.6911, 70.9505 ], [ 178.6859, 70.9537 ], [ 178.7, 70.9583 ], [ 178.7208, 70.9563 ], [ 178.725, 70.9521 ], [ 178.7542, 70.9521 ], [ 178.7557, 70.9526 ], [ 178.7557, 70.9557 ], [ 178.7422, 70.9568 ], [ 178.7412, 70.9641 ], [ 178.7333, 70.9667 ], [ 178.7151, 70.9672 ], [ 178.7151, 70.9703 ], [ 178.7203, 70.9724 ], [ 178.7125, 70.975 ], [ 178.688, 70.9755 ], [ 178.6932, 70.9776 ], [ 178.6932, 70.9807 ], [ 178.6854, 70.9833 ], [ 178.6771, 70.9833 ], [ 178.6667, 70.9792 ], [ 178.6583, 70.9792 ], [ 178.6484, 70.9818 ], [ 178.6536, 70.9838 ], [ 178.6547, 70.9891 ], [ 178.6646, 70.9896 ], [ 178.6662, 70.9912 ], [ 178.6563, 70.9917 ], [ 178.6463, 70.9943 ], [ 178.6542, 70.9979 ], [ 178.6578, 70.9974 ], [ 178.6625, 70.9938 ], [ 178.6662, 70.9943 ], [ 178.6651, 70.9995 ], [ 178.6703, 71.0016 ], [ 178.6651, 71.0026 ], [ 178.6651, 71.0057 ], [ 178.6812, 71.0062 ], [ 178.6891, 71.0026 ], [ 178.6792, 71.0021 ], [ 178.6776, 71.0005 ], [ 178.6812, 70.9979 ], [ 178.6911, 70.9974 ], [ 178.6859, 70.9943 ], [ 178.6938, 70.9917 ], [ 178.6974, 70.9922 ], [ 178.6984, 70.9953 ], [ 178.7063, 70.9979 ], [ 178.7146, 70.9979 ], [ 178.7224, 70.9953 ], [ 178.7172, 70.9922 ], [ 178.7229, 70.9917 ], [ 178.7245, 70.9901 ], [ 178.713, 70.9891 ], [ 178.7182, 70.987 ], [ 178.7193, 70.9838 ], [ 178.7245, 70.9828 ], [ 178.7193, 70.9807 ], [ 178.7208, 70.9792 ], [ 178.7328, 70.9797 ], [ 178.7312, 70.9854 ], [ 178.7333, 70.9875 ], [ 178.7479, 70.9875 ], [ 178.7521, 70.9854 ], [ 178.7641, 70.9849 ], [ 178.7641, 70.9818 ], [ 178.7589, 70.9807 ], [ 178.7589, 70.9776 ], [ 178.7849, 70.9745 ], [ 178.7849, 70.9714 ], [ 178.7833, 70.9708 ], [ 178.775, 70.9708 ], [ 178.7708, 70.9688 ], [ 178.7625, 70.9688 ], [ 178.7547, 70.9661 ], [ 178.7547, 70.963 ], [ 178.7708, 70.9646 ], [ 178.775, 70.9667 ], [ 178.7896, 70.9667 ], [ 178.7917, 70.9688 ], [ 178.7912, 70.9745 ], [ 178.7891, 70.9766 ], [ 178.7776, 70.9807 ], [ 178.7854, 70.9833 ], [ 178.825, 70.9833 ], [ 178.8266, 70.9849 ], [ 178.8167, 70.9854 ], [ 178.8104, 70.9875 ], [ 178.8062, 70.9917 ], [ 178.7833, 70.9917 ], [ 178.7792, 70.9938 ], [ 178.7708, 70.9938 ], [ 178.7646, 70.9917 ], [ 178.7604, 70.9938 ], [ 178.7521, 70.9938 ], [ 178.7422, 70.9964 ], [ 178.7412, 71.0016 ], [ 178.7312, 71.0021 ], [ 178.7234, 71.0047 ], [ 178.7214, 71.0078 ], [ 178.725, 71.0083 ], [ 178.7292, 71.0062 ], [ 178.7458, 71.0062 ], [ 178.75, 71.0042 ], [ 178.7599, 71.0036 ], [ 178.7625, 71.0 ], [ 178.7792, 71.0 ], [ 178.7854, 70.9979 ], [ 178.7932, 71.0005 ], [ 178.7932, 71.0036 ], [ 178.7859, 71.0047 ], [ 178.7854, 71.0062 ], [ 178.7859, 71.0078 ], [ 178.7912, 71.0099 ], [ 178.7813, 71.0104 ], [ 178.7797, 71.012 ], [ 178.7896, 71.0125 ], [ 178.7974, 71.0151 ], [ 178.7984, 71.0182 ], [ 178.8016, 71.0203 ], [ 178.7937, 71.0229 ], [ 178.7854, 71.0229 ], [ 178.7828, 71.0193 ], [ 178.7713, 71.0182 ], [ 178.7766, 71.0151 ], [ 178.7708, 71.0125 ], [ 178.7479, 71.0125 ], [ 178.7375, 71.0167 ], [ 178.7229, 71.0167 ], [ 178.7167, 71.0188 ], [ 178.7026, 71.0141 ], [ 178.7016, 71.0089 ], [ 178.6854, 71.0083 ], [ 178.6839, 71.0089 ], [ 178.6839, 71.012 ], [ 178.6891, 71.013 ], [ 178.688, 71.0162 ], [ 178.6995, 71.0172 ], [ 178.6995, 71.0203 ], [ 178.6917, 71.0229 ], [ 178.6771, 71.0229 ], [ 178.6667, 71.0188 ], [ 178.6583, 71.0188 ], [ 178.6521, 71.0167 ], [ 178.6505, 71.0172 ], [ 178.6505, 71.0203 ], [ 178.6557, 71.0224 ], [ 178.6484, 71.0234 ], [ 178.6484, 71.0266 ], [ 178.6599, 71.0276 ], [ 178.6609, 71.0328 ], [ 178.6625, 71.0333 ], [ 178.6787, 71.0339 ], [ 178.6797, 71.0391 ], [ 178.6812, 71.0396 ], [ 178.7021, 71.0396 ], [ 178.7083, 71.0375 ], [ 178.7161, 71.0411 ], [ 178.7063, 71.0417 ], [ 178.7, 71.0458 ], [ 178.6792, 71.0458 ], [ 178.6687, 71.0417 ], [ 178.6604, 71.0417 ], [ 178.6563, 71.0396 ], [ 178.65, 71.0417 ], [ 178.6438, 71.0396 ], [ 178.6375, 71.0417 ], [ 178.6292, 71.0396 ], [ 178.6276, 71.0401 ], [ 178.6276, 71.0432 ], [ 178.6328, 71.0453 ], [ 178.6271, 71.0458 ], [ 178.6255, 71.0474 ], [ 178.637, 71.0484 ], [ 178.6318, 71.0505 ], [ 178.6313, 71.0583 ], [ 178.6349, 71.062 ], [ 178.6297, 71.063 ], [ 178.6297, 71.0703 ], [ 178.6375, 71.0729 ], [ 178.6521, 71.0729 ], [ 178.6563, 71.0708 ], [ 178.6646, 71.0708 ], [ 178.6667, 71.0729 ], [ 178.6646, 71.075 ], [ 178.6479, 71.075 ], [ 178.6463, 71.0766 ], [ 178.6583, 71.0771 ], [ 178.6625, 71.0792 ], [ 178.6833, 71.0792 ], [ 178.6854, 71.0771 ], [ 178.6859, 71.0693 ], [ 178.6979, 71.0667 ], [ 178.7021, 71.0708 ], [ 178.7245, 71.0734 ], [ 178.7234, 71.0766 ], [ 178.7287, 71.0776 ], [ 178.7312, 71.0813 ], [ 178.7396, 71.0813 ], [ 178.7437, 71.0792 ], [ 178.7495, 71.0797 ], [ 178.7536, 71.0828 ], [ 178.7484, 71.0839 ], [ 178.7484, 71.0891 ], [ 178.7771, 71.0917 ], [ 178.7786, 71.0922 ], [ 178.7786, 71.0953 ], [ 178.7734, 71.0974 ], [ 178.7813, 71.1 ], [ 178.7849, 71.0995 ], [ 178.7859, 71.0964 ], [ 178.7912, 71.0964 ], [ 178.7922, 71.0995 ], [ 178.7974, 71.1016 ], [ 178.7854, 71.1021 ], [ 178.7839, 71.1036 ], [ 178.8021, 71.1042 ], [ 178.8141, 71.1088 ], [ 178.8141, 71.112 ], [ 178.8089, 71.1141 ], [ 178.8167, 71.1167 ], [ 178.8313, 71.1167 ], [ 178.8354, 71.1188 ], [ 178.8438, 71.1188 ], [ 178.8479, 71.1208 ], [ 178.8687, 71.1146 ], [ 178.8786, 71.1141 ], [ 178.8734, 71.1109 ], [ 178.887, 71.1078 ], [ 178.887, 71.1047 ], [ 178.8818, 71.1036 ], [ 178.8792, 71.1 ], [ 178.8667, 71.1042 ], [ 178.8625, 71.1042 ], [ 178.8583, 71.1021 ], [ 178.8359, 71.0995 ], [ 178.8375, 71.0979 ], [ 178.8542, 71.0979 ], [ 178.8557, 71.0964 ], [ 178.8458, 71.0958 ], [ 178.8255, 71.0891 ], [ 178.8255, 71.0859 ], [ 178.8307, 71.0849 ], [ 178.8255, 71.0828 ], [ 178.8276, 71.0776 ], [ 178.8375, 71.0771 ], [ 178.8604, 71.0854 ], [ 178.8687, 71.0854 ], [ 178.875, 71.0875 ], [ 178.8771, 71.0854 ], [ 178.8766, 71.0797 ], [ 178.8651, 71.0766 ], [ 178.8651, 71.0734 ], [ 178.8771, 71.0729 ], [ 178.8786, 71.0734 ], [ 178.8776, 71.0766 ], [ 178.8833, 71.0771 ], [ 178.8875, 71.075 ], [ 178.9146, 71.075 ], [ 178.9208, 71.0729 ], [ 178.9224, 71.0734 ], [ 178.9224, 71.0766 ], [ 178.9172, 71.0776 ], [ 178.9167, 71.0792 ], [ 178.9172, 71.0807 ], [ 178.9224, 71.0828 ], [ 178.9167, 71.0833 ], [ 178.9151, 71.0849 ], [ 178.925, 71.0875 ], [ 178.9297, 71.0839 ], [ 178.9349, 71.0828 ], [ 178.9297, 71.0797 ], [ 178.9375, 71.0771 ], [ 178.9411, 71.0776 ], [ 178.9422, 71.0828 ], [ 178.9521, 71.0833 ], [ 178.9536, 71.0828 ], [ 178.9547, 71.0776 ], [ 178.9646, 71.075 ], [ 178.9745, 71.0745 ], [ 178.9693, 71.0724 ], [ 178.9682, 71.0693 ], [ 178.9568, 71.0682 ], [ 178.9568, 71.0651 ], [ 178.9583, 71.0646 ], [ 178.9812, 71.0646 ], [ 178.9854, 71.0667 ], [ 178.9953, 71.0672 ], [ 178.9937, 71.0687 ], [ 178.9818, 71.0693 ], [ 178.9807, 71.0787 ], [ 178.9672, 71.0797 ], [ 178.9688, 71.0813 ], [ 178.9787, 71.0818 ], [ 178.9787, 71.087 ], [ 178.9708, 71.0896 ], [ 178.9625, 71.0896 ], [ 178.9583, 71.0917 ], [ 178.9479, 71.0917 ], [ 178.9438, 71.0938 ], [ 178.9229, 71.0938 ], [ 178.9214, 71.0943 ], [ 178.9203, 71.0995 ], [ 178.9182, 71.1016 ], [ 178.9068, 71.1026 ], [ 178.9068, 71.1057 ], [ 178.9146, 71.1083 ], [ 178.9229, 71.1083 ], [ 178.9354, 71.1042 ], [ 178.9396, 71.1062 ], [ 178.9542, 71.1062 ], [ 178.9583, 71.1083 ], [ 178.9828, 71.1088 ], [ 178.9828, 71.112 ], [ 178.9755, 71.113 ], [ 178.9807, 71.1151 ], [ 178.9792, 71.1167 ], [ 178.9646, 71.1167 ], [ 178.9583, 71.1146 ], [ 178.9542, 71.1167 ], [ 178.9312, 71.1188 ], [ 178.9271, 71.1208 ], [ 178.9187, 71.1208 ], [ 178.9146, 71.1229 ], [ 178.9109, 71.1224 ], [ 178.9099, 71.1193 ], [ 178.9047, 71.1193 ], [ 178.9042, 71.125 ], [ 178.9063, 71.1271 ], [ 178.9146, 71.1271 ], [ 178.9187, 71.125 ], [ 178.9271, 71.125 ], [ 178.9333, 71.1229 ], [ 178.9474, 71.1276 ], [ 178.9474, 71.1307 ], [ 178.9422, 71.1318 ], [ 178.9438, 71.1333 ], [ 178.9787, 71.1339 ], [ 178.9771, 71.1354 ], [ 178.9688, 71.1354 ], [ 178.9609, 71.138 ], [ 178.9588, 71.1412 ], [ 178.9703, 71.1443 ], [ 178.9625, 71.1479 ], [ 178.9354, 71.1479 ], [ 178.9328, 71.1443 ], [ 178.925, 71.1417 ], [ 178.9083, 71.1458 ], [ 178.9042, 71.1417 ], [ 178.8896, 71.1417 ], [ 178.8854, 71.1396 ], [ 178.8646, 71.1375 ], [ 178.8604, 71.1354 ], [ 178.8333, 71.1354 ], [ 178.8292, 71.1333 ], [ 178.8208, 71.1354 ], [ 178.8104, 71.1312 ], [ 178.7958, 71.1312 ], [ 178.7917, 71.1292 ], [ 178.7833, 71.1292 ], [ 178.7792, 71.1271 ], [ 178.7755, 71.1276 ], [ 178.7734, 71.1297 ], [ 178.7729, 71.1333 ], [ 178.775, 71.1354 ], [ 178.7849, 71.1359 ], [ 178.7859, 71.1412 ], [ 178.7912, 71.1422 ], [ 178.7922, 71.1453 ], [ 178.8, 71.1479 ], [ 178.8271, 71.1479 ], [ 178.8349, 71.1505 ], [ 178.8359, 71.1557 ], [ 178.8474, 71.1589 ], [ 178.8464, 71.162 ], [ 178.8662, 71.163 ], [ 178.8646, 71.1646 ], [ 178.8589, 71.1651 ], [ 178.8589, 71.1682 ], [ 178.8604, 71.1687 ], [ 178.8687, 71.1687 ], [ 178.8729, 71.1667 ], [ 178.8833, 71.1667 ], [ 178.8875, 71.1687 ], [ 178.8938, 71.1667 ], [ 178.9036, 71.1672 ], [ 178.9036, 71.1703 ], [ 178.9021, 71.1708 ], [ 178.8901, 71.1713 ], [ 178.8953, 71.1734 ], [ 178.8901, 71.1766 ], [ 178.9016, 71.1776 ], [ 178.8963, 71.1797 ], [ 178.8943, 71.187 ], [ 178.8995, 71.188 ], [ 178.9005, 71.1953 ], [ 178.9057, 71.1964 ], [ 178.9005, 71.1984 ], [ 178.9, 71.2 ], [ 178.9005, 71.2016 ], [ 178.9057, 71.2037 ], [ 178.8984, 71.2047 ], [ 178.8984, 71.2078 ], [ 178.9229, 71.2083 ], [ 178.9292, 71.2063 ], [ 178.9333, 71.2083 ], [ 178.9432, 71.2089 ], [ 178.9354, 71.2125 ], [ 178.9234, 71.2109 ], [ 178.9234, 71.2141 ], [ 178.9287, 71.2151 ], [ 178.9234, 71.2182 ], [ 178.9312, 71.2208 ], [ 178.9396, 71.2208 ], [ 178.9625, 71.2292 ], [ 178.9708, 71.2292 ], [ 178.9812, 71.2333 ], [ 179.0208, 71.2375 ], [ 179.0234, 71.2412 ], [ 179.0333, 71.2417 ], [ 179.0437, 71.2458 ], [ 179.0583, 71.2458 ], [ 179.0625, 71.2479 ], [ 179.0729, 71.2479 ], [ 179.0771, 71.25 ], [ 179.1104, 71.2521 ], [ 179.1146, 71.2542 ], [ 179.1354, 71.2562 ], [ 179.1396, 71.2583 ], [ 179.1479, 71.2583 ], [ 179.1646, 71.2646 ], [ 179.1729, 71.2646 ], [ 179.1807, 71.2672 ], [ 179.1818, 71.2724 ], [ 179.1833, 71.2729 ], [ 179.2328, 71.2734 ], [ 179.2276, 71.2755 ], [ 179.2328, 71.2786 ], [ 179.2208, 71.2792 ], [ 179.2193, 71.2797 ], [ 179.2193, 71.2828 ], [ 179.2245, 71.2839 ], [ 179.2255, 71.2911 ], [ 179.237, 71.2943 ], [ 179.238, 71.2995 ], [ 179.2495, 71.3026 ], [ 179.2484, 71.3078 ], [ 179.2536, 71.3089 ], [ 179.2547, 71.312 ], [ 179.3062, 71.3292 ], [ 179.3167, 71.3292 ], [ 179.3208, 71.3313 ], [ 179.3417, 71.3292 ], [ 179.3495, 71.3318 ], [ 179.3505, 71.337 ], [ 179.3557, 71.338 ], [ 179.3568, 71.3411 ], [ 179.362, 71.3422 ], [ 179.363, 71.3453 ], [ 179.3682, 71.3464 ], [ 179.3693, 71.3495 ], [ 179.3745, 71.3505 ], [ 179.3755, 71.3536 ], [ 179.387, 71.3568 ], [ 179.3859, 71.3599 ], [ 179.3995, 71.363 ], [ 179.3984, 71.3662 ], [ 179.4125, 71.3708 ], [ 179.4229, 71.3708 ], [ 179.4271, 71.3729 ], [ 179.4354, 71.3729 ], [ 179.4396, 71.375 ], [ 179.4667, 71.375 ], [ 179.4708, 71.3771 ], [ 179.4891, 71.3776 ], [ 179.4891, 71.3807 ], [ 179.4818, 71.3818 ], [ 179.487, 71.3849 ], [ 179.4818, 71.3859 ], [ 179.4807, 71.3891 ], [ 179.4755, 71.3901 ], [ 179.4734, 71.3974 ], [ 179.4792, 71.3979 ], [ 179.4807, 71.3995 ], [ 179.475, 71.4 ], [ 179.4734, 71.4016 ], [ 179.4787, 71.4026 ], [ 179.4797, 71.4057 ], [ 179.5063, 71.4146 ], [ 179.5167, 71.4146 ], [ 179.5208, 71.4167 ], [ 179.5354, 71.4167 ], [ 179.5396, 71.4187 ], [ 179.5604, 71.4187 ], [ 179.5646, 71.4208 ], [ 179.5813, 71.4208 ], [ 179.5838, 71.4245 ], [ 179.5917, 71.4271 ], [ 179.6, 71.4271 ], [ 179.6042, 71.4292 ], [ 179.6125, 71.4292 ], [ 179.6167, 71.4333 ], [ 179.625, 71.4333 ], [ 179.6292, 71.4354 ], [ 179.6438, 71.4354 ], [ 179.6479, 71.4375 ], [ 179.6583, 71.4375 ], [ 179.6687, 71.4417 ], [ 179.6896, 71.4417 ], [ 179.7021, 71.4375 ], [ 179.7229, 71.4375 ], [ 179.7271, 71.4396 ], [ 179.7333, 71.4375 ], [ 179.7563, 71.4396 ], [ 179.7625, 71.4375 ], [ 179.7667, 71.4396 ], [ 179.775, 71.4396 ], [ 179.7792, 71.4417 ], [ 179.7875, 71.4417 ], [ 179.8016, 71.4464 ], [ 179.7964, 71.4495 ], [ 179.8141, 71.4547 ], [ 179.8141, 71.462 ], [ 179.7979, 71.4625 ], [ 179.7964, 71.463 ], [ 179.7964, 71.4661 ], [ 179.8188, 71.4667 ], [ 179.8203, 71.4672 ], [ 179.8214, 71.4724 ], [ 179.8292, 71.475 ], [ 179.8438, 71.475 ], [ 179.8479, 71.4771 ], [ 179.8521, 71.4771 ], [ 179.8562, 71.475 ], [ 179.8771, 71.475 ], [ 179.8974, 71.4818 ], [ 179.8984, 71.4849 ], [ 179.9036, 71.4859 ], [ 179.8984, 71.488 ], [ 179.8974, 71.4912 ], [ 179.8922, 71.4922 ], [ 179.8974, 71.4943 ], [ 179.8984, 71.4974 ], [ 179.9125, 71.5021 ], [ 179.9271, 71.5021 ], [ 179.9297, 71.5057 ], [ 179.9349, 71.5068 ], [ 179.9297, 71.5089 ], [ 179.9297, 71.5141 ], [ 179.9349, 71.5151 ], [ 179.9359, 71.5182 ], [ 179.9411, 71.5193 ], [ 179.9422, 71.5224 ], [ 179.9917, 71.5229 ], [ 179.9932, 71.5245 ], [ 179.988, 71.5266 ], [ 179.9979, 71.5292 ], [ 179.9995, 71.3901 ], [ 179.9995, 71.112 ], [ 179.9979, 70.9729 ], [ 179.9896, 70.9729 ] ] ], [ [ [ -180.0, 64.9578 ], [ -179.9984, 64.9578 ], [ -179.9984, 64.9568 ], [ -180.0, 64.9568 ], [ -180.0, 64.9578 ] ] ], [ [ [ -179.9979, 64.9688 ], [ -179.9984, 64.9672 ], [ -180.0, 64.9677 ], [ -180.0, 64.9698 ], [ -179.9984, 64.9703 ], [ -179.9979, 64.9688 ] ] ], [ [ [ -179.9979, 64.9812 ], [ -179.9984, 64.9797 ], [ -180.0, 64.9802 ], [ -180.0, 64.9823 ], [ -179.9984, 64.9828 ], [ -179.9979, 64.9812 ] ] ], [ [ [ -179.9979, 64.9958 ], [ -179.9984, 64.9943 ], [ -180.0, 64.9948 ], [ -180.0, 64.9969 ], [ -179.9984, 64.9974 ], [ -179.9979, 64.9958 ] ] ], [ [ [ -179.9984, 65.012 ], [ -179.9979, 65.0104 ], [ -179.9984, 65.0068 ], [ -180.0, 65.0078 ], [ -180.0, 65.0109 ], [ -179.9984, 65.012 ] ] ], [ [ [ -179.9979, 65.025 ], [ -179.9984, 65.0214 ], [ -180.0, 65.0229 ], [ -180.0, 65.0271 ], [ -179.9979, 65.0292 ], [ -179.9964, 65.0286 ], [ -179.9979, 65.025 ] ] ], [ [ [ -180.0, 65.0349 ], [ -179.9984, 65.0349 ], [ -179.9984, 65.0339 ], [ -180.0, 65.0339 ], [ -180.0, 65.0349 ] ] ], [ [ [ -179.5036, 65.4005 ], [ -179.5021, 65.4021 ], [ -179.4984, 65.4016 ], [ -179.5, 65.4 ], [ -179.5036, 65.4005 ] ] ], [ [ [ -179.4542, 65.4333 ], [ -179.4526, 65.4318 ], [ -179.4557, 65.4318 ], [ -179.4557, 65.4328 ], [ -179.4542, 65.4333 ] ] ], [ [ [ -179.3813, 65.4646 ], [ -179.4078, 65.4557 ], [ -179.4109, 65.4505 ], [ -179.4203, 65.4474 ], [ -179.4297, 65.4401 ], [ -179.4479, 65.4354 ], [ -179.4557, 65.4359 ], [ -179.4521, 65.4396 ], [ -179.4401, 65.4401 ], [ -179.4432, 65.4432 ], [ -179.438, 65.4453 ], [ -179.4411, 65.4464 ], [ -179.4422, 65.4495 ], [ -179.4453, 65.4516 ], [ -179.4312, 65.45 ], [ -179.4271, 65.4521 ], [ -179.4229, 65.45 ], [ -179.4182, 65.4537 ], [ -179.413, 65.4547 ], [ -179.413, 65.4599 ], [ -179.4162, 65.462 ], [ -179.4068, 65.463 ], [ -179.4099, 65.4661 ], [ -179.4036, 65.4703 ], [ -179.3958, 65.4708 ], [ -179.3917, 65.4688 ], [ -179.3854, 65.4688 ], [ -179.3797, 65.4661 ], [ -179.3813, 65.4646 ] ] ], [ [ [ -179.288, 65.5391 ], [ -179.2755, 65.5349 ], [ -179.2745, 65.5318 ], [ -179.2713, 65.5307 ], [ -179.2713, 65.5276 ], [ -179.2766, 65.5266 ], [ -179.2776, 65.5234 ], [ -179.2828, 65.5224 ], [ -179.2859, 65.5193 ], [ -179.2953, 65.5162 ], [ -179.2964, 65.513 ], [ -179.3016, 65.512 ], [ -179.3089, 65.5068 ], [ -179.3141, 65.5057 ], [ -179.3266, 65.4974 ], [ -179.3297, 65.4922 ], [ -179.3437, 65.4875 ], [ -179.3458, 65.4896 ], [ -179.3432, 65.4953 ], [ -179.3338, 65.4964 ], [ -179.3318, 65.4984 ], [ -179.3307, 65.5036 ], [ -179.3255, 65.5047 ], [ -179.3234, 65.5099 ], [ -179.3375, 65.5083 ], [ -179.3417, 65.5062 ], [ -179.3495, 65.5068 ], [ -179.3484, 65.5099 ], [ -179.3521, 65.5125 ], [ -179.3505, 65.5182 ], [ -179.3537, 65.5193 ], [ -179.3547, 65.5245 ], [ -179.362, 65.5276 ], [ -179.3609, 65.5307 ], [ -179.3667, 65.5333 ], [ -179.3833, 65.5333 ], [ -179.3901, 65.537 ], [ -179.3974, 65.538 ], [ -179.3979, 65.5396 ], [ -179.3953, 65.5453 ], [ -179.3938, 65.5458 ], [ -179.3854, 65.5417 ], [ -179.3792, 65.5417 ], [ -179.3667, 65.5375 ], [ -179.3604, 65.5375 ], [ -179.3521, 65.5333 ], [ -179.3479, 65.5354 ], [ -179.3401, 65.5359 ], [ -179.338, 65.5391 ], [ -179.3474, 65.5401 ], [ -179.3464, 65.5432 ], [ -179.35, 65.5458 ], [ -179.3495, 65.5474 ], [ -179.3443, 65.5484 ], [ -179.3432, 65.5516 ], [ -179.338, 65.5526 ], [ -179.3417, 65.5563 ], [ -179.3375, 65.5604 ], [ -179.3349, 65.5568 ], [ -179.3057, 65.5422 ], [ -179.2937, 65.5417 ], [ -179.288, 65.5391 ] ] ], [ [ [ -179.4021, 65.5646 ], [ -179.4005, 65.5641 ], [ -179.4005, 65.5609 ], [ -179.4099, 65.5609 ], [ -179.4078, 65.5661 ], [ -179.4021, 65.5646 ] ] ], [ [ [ -179.5458, 65.6979 ], [ -179.5443, 65.6974 ], [ -179.5443, 65.6964 ], [ -179.5474, 65.6964 ], [ -179.5458, 65.6979 ] ] ], [ [ [ -179.7672, 66.0922 ], [ -179.7708, 66.0896 ], [ -179.7729, 66.0917 ], [ -179.7688, 66.0958 ], [ -179.7672, 66.0953 ], [ -179.7672, 66.0922 ] ] ], [ [ [ -179.7854, 66.0938 ], [ -179.7839, 66.0922 ], [ -179.787, 66.0922 ], [ -179.787, 66.0932 ], [ -179.7854, 66.0938 ] ] ], [ [ [ -179.6375, 66.1167 ], [ -179.6318, 66.1162 ], [ -179.6318, 66.113 ], [ -179.6396, 66.1125 ], [ -179.6453, 66.1151 ], [ -179.6438, 66.1167 ], [ -179.6375, 66.1167 ] ] ], [ [ [ -175.4771, 64.8771 ], [ -175.4807, 64.8786 ], [ -175.4734, 64.8786 ], [ -175.4734, 64.8776 ], [ -175.4771, 64.8771 ] ] ], [ [ [ -175.9432, 65.3325 ], [ -175.9417, 65.3333 ], [ -175.9401, 65.3318 ], [ -175.9417, 65.3313 ], [ -175.9432, 65.3325 ] ] ], [ [ [ -176.0083, 65.3896 ], [ -175.9974, 65.3839 ], [ -175.9776, 65.3786 ], [ -175.9766, 65.3755 ], [ -175.9734, 65.3745 ], [ -175.9745, 65.3714 ], [ -175.9708, 65.3688 ], [ -175.9734, 65.3651 ], [ -175.9787, 65.363 ], [ -175.9755, 65.362 ], [ -175.9745, 65.3588 ], [ -175.9714, 65.3568 ], [ -175.9792, 65.3562 ], [ -175.9849, 65.3588 ], [ -175.9839, 65.3641 ], [ -175.9854, 65.3646 ], [ -175.9932, 65.363 ], [ -175.9922, 65.3662 ], [ -175.9953, 65.3672 ], [ -175.9964, 65.3703 ], [ -176.0021, 65.3708 ], [ -176.0078, 65.3734 ], [ -176.0109, 65.3807 ], [ -176.0182, 65.3818 ], [ -176.0307, 65.388 ], [ -176.0297, 65.3912 ], [ -176.0328, 65.3932 ], [ -176.0271, 65.3938 ], [ -176.0229, 65.3917 ], [ -176.0083, 65.3896 ] ] ], [ [ [ -177.2068, 65.5141 ], [ -177.2083, 65.5125 ], [ -177.2161, 65.513 ], [ -177.2125, 65.5167 ], [ -177.2068, 65.5141 ] ] ], [ [ [ -177.1755, 65.5255 ], [ -177.1932, 65.5203 ], [ -177.1958, 65.5167 ], [ -177.2057, 65.5214 ], [ -177.2042, 65.5229 ], [ -177.1979, 65.5229 ], [ -177.1901, 65.5255 ], [ -177.1901, 65.5286 ], [ -177.1974, 65.5318 ], [ -177.1958, 65.5333 ], [ -177.1896, 65.5333 ], [ -177.1854, 65.5313 ], [ -177.1792, 65.5333 ], [ -177.1693, 65.5307 ], [ -177.1693, 65.5276 ], [ -177.1755, 65.5255 ] ] ], [ [ [ -177.0771, 65.6 ], [ -177.0583, 65.6 ], [ -177.0526, 65.5964 ], [ -177.0849, 65.5922 ], [ -177.0807, 65.5995 ], [ -177.0771, 65.6 ] ] ], [ [ [ -177.0125, 65.6 ], [ -177.025, 65.6 ], [ -177.0292, 65.5979 ], [ -177.0516, 65.5984 ], [ -177.05, 65.6 ], [ -177.0375, 65.6 ], [ -177.0229, 65.6042 ], [ -177.0167, 65.6042 ], [ -177.0125, 65.6021 ], [ -177.0021, 65.6021 ], [ -176.9854, 65.6083 ], [ -176.9646, 65.6083 ], [ -176.9588, 65.6057 ], [ -176.9667, 65.6021 ], [ -176.9937, 65.6021 ], [ -176.9979, 65.6 ], [ -177.0125, 65.6 ] ] ], [ [ [ -176.5292, 65.5479 ], [ -176.5229, 65.5479 ], [ -176.5172, 65.5443 ], [ -176.5208, 65.5417 ], [ -176.5307, 65.5463 ], [ -176.5292, 65.5479 ] ] ], [ [ [ -175.3021, 67.3354 ], [ -175.3005, 67.3339 ], [ -175.3036, 67.3339 ], [ -175.3036, 67.3349 ], [ -175.3021, 67.3354 ] ] ], [ [ [ -175.1708, 67.3396 ], [ -175.1693, 67.3391 ], [ -175.1693, 67.338 ], [ -175.1724, 67.338 ], [ -175.1708, 67.3396 ] ] ], [ [ [ -177.1266, 68.1109 ], [ -177.125, 68.1125 ], [ -177.1214, 68.112 ], [ -177.1229, 68.1104 ], [ -177.1266, 68.1109 ] ] ], [ [ [ -177.1167, 68.1188 ], [ -177.1151, 68.1182 ], [ -177.1151, 68.1172 ], [ -177.1182, 68.1172 ], [ -177.1167, 68.1188 ] ] ], [ [ [ -177.012, 68.1359 ], [ -177.0104, 68.1375 ], [ -177.0068, 68.137 ], [ -177.0083, 68.1354 ], [ -177.012, 68.1359 ] ] ], [ [ [ -178.8646, 65.9083 ], [ -178.863, 65.9068 ], [ -178.8667, 65.9042 ], [ -178.8724, 65.9068 ], [ -178.8708, 65.9083 ], [ -178.8646, 65.9083 ] ] ], [ [ [ -178.513, 66.1088 ], [ -178.5188, 66.1062 ], [ -178.5224, 66.1068 ], [ -178.5229, 66.1083 ], [ -178.5208, 66.1104 ], [ -178.5146, 66.1104 ], [ -178.513, 66.1088 ] ] ], [ [ [ -178.6792, 66.2646 ], [ -178.6776, 66.2641 ], [ -178.6776, 66.263 ], [ -178.6807, 66.263 ], [ -178.6792, 66.2646 ] ] ], [ [ [ -177.9432, 68.2505 ], [ -177.9417, 68.2521 ], [ -177.938, 68.2516 ], [ -177.9396, 68.25 ], [ -177.9432, 68.2505 ] ] ], [ [ [ -179.2912, 66.238 ], [ -179.2896, 66.2396 ], [ -179.2859, 66.2391 ], [ -179.2875, 66.2375 ], [ -179.2912, 66.238 ] ] ], [ [ [ -179.3979, 66.3292 ], [ -179.3963, 66.3287 ], [ -179.3963, 66.3276 ], [ -179.3995, 66.3276 ], [ -179.3979, 66.3292 ] ] ], [ [ [ -179.3562, 68.8354 ], [ -179.3547, 68.8339 ], [ -179.3578, 68.8339 ], [ -179.3578, 68.8349 ], [ -179.3562, 68.8354 ] ] ], [ [ [ -179.3682, 68.8745 ], [ -179.3583, 68.875 ], [ -179.3562, 68.8729 ], [ -179.3568, 68.8714 ], [ -179.3604, 68.8708 ], [ -179.3682, 68.8745 ] ] ], [ [ [ -178.3625, 68.4854 ], [ -178.3458, 68.4854 ], [ -178.3443, 68.4838 ], [ -178.3542, 68.4833 ], [ -178.3646, 68.4792 ], [ -178.3724, 68.4797 ], [ -178.3672, 68.4818 ], [ -178.3661, 68.4849 ], [ -178.3625, 68.4854 ] ] ], [ [ [ -178.3432, 68.4859 ], [ -178.3417, 68.4875 ], [ -178.338, 68.487 ], [ -178.3396, 68.4854 ], [ -178.3432, 68.4859 ] ] ], [ [ [ -178.1641, 68.4922 ], [ -178.1625, 68.4938 ], [ -178.1589, 68.4932 ], [ -178.1604, 68.4917 ], [ -178.1641, 68.4922 ] ] ], [ [ [ -178.1849, 68.4984 ], [ -178.1833, 68.5 ], [ -178.1797, 68.4995 ], [ -178.1812, 68.4979 ], [ -178.1849, 68.4984 ] ] ], [ [ [ -178.2563, 68.5208 ], [ -178.2625, 68.5208 ], [ -178.2641, 68.5224 ], [ -178.2547, 68.5224 ], [ -178.2563, 68.5208 ] ] ], [ [ [ -178.2667, 68.525 ], [ -178.2651, 68.5234 ], [ -178.2682, 68.5234 ], [ -178.2682, 68.5245 ], [ -178.2667, 68.525 ] ] ], [ [ [ -178.3208, 68.5375 ], [ -178.3224, 68.5391 ], [ -178.3172, 68.5391 ], [ -178.3172, 68.538 ], [ -178.3208, 68.5375 ] ] ], [ [ [ -178.3333, 68.5458 ], [ -178.3276, 68.5422 ], [ -178.3417, 68.5417 ], [ -178.3437, 68.5437 ], [ -178.3417, 68.5458 ], [ -178.3333, 68.5458 ] ] ], [ [ [ -176.0937, 67.6875 ], [ -176.1037, 67.688 ], [ -176.0984, 67.6901 ], [ -176.1, 67.6917 ], [ -176.1062, 67.6917 ], [ -176.1104, 67.6937 ], [ -176.1208, 67.6937 ], [ -176.1224, 67.6953 ], [ -176.0979, 67.6958 ], [ -176.0953, 67.6922 ], [ -176.088, 67.6891 ], [ -176.0896, 67.6875 ], [ -176.0937, 67.6875 ] ] ], [ [ [ -176.3, 67.7229 ], [ -176.3062, 67.7229 ], [ -176.3078, 67.7245 ], [ -176.2984, 67.7245 ], [ -176.3, 67.7229 ] ] ], [ [ [ -175.6859, 67.7693 ], [ -175.6938, 67.7688 ], [ -175.6953, 67.7703 ], [ -175.6875, 67.7708 ], [ -175.6859, 67.7693 ] ] ], [ [ [ -175.9411, 67.7734 ], [ -175.9396, 67.775 ], [ -175.9359, 67.7745 ], [ -175.9375, 67.7729 ], [ -175.9411, 67.7734 ] ] ], [ [ [ -175.837, 67.8276 ], [ -175.8354, 67.8292 ], [ -175.8318, 67.8287 ], [ -175.8333, 67.8271 ], [ -175.837, 67.8276 ] ] ], [ [ [ -175.8349, 67.8318 ], [ -175.8333, 67.8333 ], [ -175.8297, 67.8328 ], [ -175.8312, 67.8313 ], [ -175.8349, 67.8318 ] ] ], [ [ [ -175.8896, 67.8417 ], [ -175.8833, 67.8417 ], [ -175.8792, 67.8396 ], [ -175.8667, 67.8396 ], [ -175.8625, 67.8375 ], [ -175.8583, 67.8396 ], [ -175.8437, 67.8396 ], [ -175.8396, 67.8417 ], [ -175.8318, 67.838 ], [ -175.8338, 67.8359 ], [ -175.8391, 67.8349 ], [ -175.8422, 67.8297 ], [ -175.8562, 67.8292 ], [ -175.8604, 67.8313 ], [ -175.8724, 67.8318 ], [ -175.8714, 67.837 ], [ -175.8729, 67.8375 ], [ -175.8792, 67.8354 ], [ -175.8979, 67.8354 ], [ -175.9021, 67.8375 ], [ -175.9099, 67.838 ], [ -175.9182, 67.8422 ], [ -175.9187, 67.8438 ], [ -175.9167, 67.8458 ], [ -175.9083, 67.8458 ], [ -175.9042, 67.8438 ], [ -175.8896, 67.8417 ] ] ], [ [ [ -175.8292, 67.8479 ], [ -175.825, 67.8479 ], [ -175.8234, 67.8464 ], [ -175.8286, 67.8453 ], [ -175.8318, 67.8422 ], [ -175.8349, 67.8422 ], [ -175.8349, 67.8453 ], [ -175.8328, 67.8474 ], [ -175.8292, 67.8479 ] ] ], [ [ [ -175.9734, 67.8578 ], [ -175.975, 67.8562 ], [ -175.9849, 67.8568 ], [ -175.9833, 67.8583 ], [ -175.9734, 67.8578 ] ] ], [ [ [ -176.5224, 67.9109 ], [ -176.5208, 67.9125 ], [ -176.5172, 67.912 ], [ -176.5188, 67.9104 ], [ -176.5224, 67.9109 ] ] ], [ [ [ -176.2644, 67.9271 ], [ -176.2609, 67.9266 ], [ -176.2568, 67.9234 ], [ -176.2604, 67.9229 ], [ -176.2646, 67.925 ], [ -176.2875, 67.925 ], [ -176.2932, 67.9276 ], [ -176.2943, 67.9307 ], [ -176.3016, 67.9349 ], [ -176.2875, 67.9354 ], [ -176.2766, 67.9297 ], [ -176.2688, 67.9292 ], [ -176.2644, 67.9271 ] ] ], [ [ [ -176.3083, 67.9375 ], [ -176.312, 67.938 ], [ -176.312, 67.9391 ], [ -176.3068, 67.9391 ], [ -176.3083, 67.9375 ] ] ], [ [ [ -176.3276, 67.9445 ], [ -176.3354, 67.9437 ], [ -176.337, 67.9453 ], [ -176.3292, 67.9458 ], [ -176.3276, 67.9445 ] ] ], [ [ [ -176.3526, 67.9514 ], [ -176.3542, 67.95 ], [ -176.362, 67.9505 ], [ -176.3604, 67.9521 ], [ -176.3526, 67.9514 ] ] ], [ [ [ -176.3687, 67.9563 ], [ -176.3625, 67.9563 ], [ -176.3609, 67.9547 ], [ -176.3646, 67.9521 ], [ -176.3786, 67.9526 ], [ -176.3766, 67.9557 ], [ -176.3687, 67.9563 ] ] ], [ [ [ -176.5026, 67.9932 ], [ -176.4896, 67.9875 ], [ -176.4776, 67.987 ], [ -176.475, 67.9833 ], [ -176.4568, 67.9807 ], [ -176.4526, 67.9786 ], [ -176.45, 67.975 ], [ -176.4333, 67.9729 ], [ -176.4292, 67.9708 ], [ -176.4187, 67.9708 ], [ -176.4146, 67.9688 ], [ -176.4083, 67.9688 ], [ -176.3938, 67.9625 ], [ -176.3859, 67.962 ], [ -176.3776, 67.9568 ], [ -176.3917, 67.9563 ], [ -176.3958, 67.9583 ], [ -176.4062, 67.9583 ], [ -176.4234, 67.9661 ], [ -176.4417, 67.9688 ], [ -176.4604, 67.9771 ], [ -176.5083, 67.9833 ], [ -176.5203, 67.988 ], [ -176.5193, 67.9912 ], [ -176.5271, 67.9938 ], [ -176.5333, 67.9938 ], [ -176.5349, 67.9953 ], [ -176.525, 67.9979 ], [ -176.5167, 67.9938 ], [ -176.5083, 67.9958 ], [ -176.5026, 67.9932 ] ] ], [ [ [ -174.3786, 66.2963 ], [ -174.3771, 66.2979 ], [ -174.3734, 66.2974 ], [ -174.375, 66.2958 ], [ -174.3786, 66.2963 ] ] ], [ [ [ -174.3396, 66.3 ], [ -174.338, 66.2995 ], [ -174.338, 66.2984 ], [ -174.3411, 66.2984 ], [ -174.3396, 66.3 ] ] ], [ [ [ -174.3995, 66.3359 ], [ -174.3979, 66.3375 ], [ -174.3943, 66.337 ], [ -174.3958, 66.3354 ], [ -174.3995, 66.3359 ] ] ], [ [ [ -174.3578, 66.3359 ], [ -174.3661, 66.3391 ], [ -174.3521, 66.3396 ], [ -174.3479, 66.3417 ], [ -174.3338, 66.3432 ], [ -174.3359, 66.338 ], [ -174.3437, 66.3354 ], [ -174.3578, 66.3359 ] ] ], [ [ [ -174.387, 66.4672 ], [ -174.3838, 66.4651 ], [ -174.3891, 66.4651 ], [ -174.3932, 66.4682 ], [ -174.3896, 66.4688 ], [ -174.387, 66.4672 ] ] ], [ [ [ -174.2521, 66.5042 ], [ -174.2505, 66.5026 ], [ -174.2599, 66.5026 ], [ -174.2563, 66.5062 ], [ -174.2521, 66.5042 ] ] ], [ [ [ -174.162, 66.5776 ], [ -174.1604, 66.5792 ], [ -174.1568, 66.5787 ], [ -174.1583, 66.5771 ], [ -174.162, 66.5776 ] ] ], [ [ [ -174.125, 66.6062 ], [ -174.1214, 66.6047 ], [ -174.1292, 66.6021 ], [ -174.137, 66.6026 ], [ -174.1292, 66.6062 ], [ -174.125, 66.6062 ] ] ], [ [ [ -174.5318, 67.0401 ], [ -174.5542, 67.0375 ], [ -174.5583, 67.0354 ], [ -174.5625, 67.0375 ], [ -174.5688, 67.0354 ], [ -174.575, 67.0354 ], [ -174.5792, 67.0375 ], [ -174.5911, 67.038 ], [ -174.5859, 67.0401 ], [ -174.5849, 67.0432 ], [ -174.5797, 67.0443 ], [ -174.5828, 67.0463 ], [ -174.5813, 67.0479 ], [ -174.5729, 67.0479 ], [ -174.5688, 67.0458 ], [ -174.5417, 67.0458 ], [ -174.5401, 67.0474 ], [ -174.5437, 67.05 ], [ -174.5417, 67.0521 ], [ -174.5229, 67.0521 ], [ -174.5188, 67.0542 ], [ -174.5047, 67.0537 ], [ -174.5088, 67.0463 ], [ -174.5167, 67.0458 ], [ -174.5318, 67.0401 ] ] ], [ [ [ -174.6775, 67.0696 ], [ -174.6786, 67.0651 ], [ -174.6714, 67.062 ], [ -174.6729, 67.0583 ], [ -174.6693, 67.0547 ], [ -174.6729, 67.0479 ], [ -174.6693, 67.0453 ], [ -174.6703, 67.0401 ], [ -174.6672, 67.0391 ], [ -174.6693, 67.0359 ], [ -174.6745, 67.0339 ], [ -174.6714, 67.0328 ], [ -174.6724, 67.0276 ], [ -174.6672, 67.0255 ], [ -174.6708, 67.0229 ], [ -174.6807, 67.0224 ], [ -174.6818, 67.0193 ], [ -174.6958, 67.0146 ], [ -174.7125, 67.0146 ], [ -174.7167, 67.0167 ], [ -174.7245, 67.0172 ], [ -174.7229, 67.0229 ], [ -174.7266, 67.0255 ], [ -174.7245, 67.0307 ], [ -174.7193, 67.0318 ], [ -174.7161, 67.0349 ], [ -174.7047, 67.038 ], [ -174.7016, 67.0432 ], [ -174.6875, 67.0437 ], [ -174.6833, 67.0417 ], [ -174.6771, 67.0417 ], [ -174.6755, 67.0432 ], [ -174.6812, 67.0458 ], [ -174.6995, 67.0463 ], [ -174.6922, 67.0505 ], [ -174.7016, 67.0547 ], [ -174.6963, 67.0568 ], [ -174.6995, 67.0599 ], [ -174.6943, 67.0609 ], [ -174.6896, 67.0646 ], [ -174.6854, 67.0625 ], [ -174.6818, 67.0641 ], [ -174.6917, 67.0687 ], [ -174.6995, 67.0693 ], [ -174.6984, 67.0724 ], [ -174.7, 67.0729 ], [ -174.7078, 67.0724 ], [ -174.7047, 67.0693 ], [ -174.7068, 67.0672 ], [ -174.7099, 67.0672 ], [ -174.7109, 67.0703 ], [ -174.7182, 67.0734 ], [ -174.7193, 67.0766 ], [ -174.7266, 67.0797 ], [ -174.725, 67.0833 ], [ -174.7287, 67.087 ], [ -174.7208, 67.0875 ], [ -174.7167, 67.0854 ], [ -174.7088, 67.0849 ], [ -174.7141, 67.0828 ], [ -174.7161, 67.0797 ], [ -174.7083, 67.0771 ], [ -174.7047, 67.0797 ], [ -174.7036, 67.0828 ], [ -174.6984, 67.0839 ], [ -174.7016, 67.087 ], [ -174.6963, 67.087 ], [ -174.688, 67.0828 ], [ -174.6891, 67.0797 ], [ -174.6818, 67.0766 ], [ -174.6807, 67.0734 ], [ -174.6776, 67.0724 ], [ -174.6775, 67.0696 ] ] ], [ [ [ -174.7, 67.0646 ], [ -174.6984, 67.063 ], [ -174.7016, 67.063 ], [ -174.7016, 67.0641 ], [ -174.7, 67.0646 ] ] ], [ [ [ -174.5063, 67.0667 ], [ -174.5047, 67.0661 ], [ -174.5047, 67.0651 ], [ -174.5078, 67.0651 ], [ -174.5063, 67.0667 ] ] ], [ [ [ -174.4849, 67.0599 ], [ -174.488, 67.0568 ], [ -174.4932, 67.0557 ], [ -174.4958, 67.0521 ], [ -174.5036, 67.0547 ], [ -174.4964, 67.0589 ], [ -174.4932, 67.0661 ], [ -174.4854, 67.0667 ], [ -174.4776, 67.0641 ], [ -174.4797, 67.0609 ], [ -174.4849, 67.0599 ] ] ], [ [ [ -174.3521, 67.0771 ], [ -174.3354, 67.0771 ], [ -174.3338, 67.0755 ], [ -174.3641, 67.0755 ], [ -174.3625, 67.0771 ], [ -174.3521, 67.0771 ] ] ], [ [ [ -174.1203, 66.6068 ], [ -174.1187, 66.6083 ], [ -174.1151, 66.6078 ], [ -174.1167, 66.6062 ], [ -174.1203, 66.6068 ] ] ], [ [ [ -173.9266, 66.6651 ], [ -173.925, 66.6667 ], [ -173.9214, 66.6661 ], [ -173.9229, 66.6646 ], [ -173.9266, 66.6651 ] ] ], [ [ [ -173.9083, 66.675 ], [ -173.9068, 66.6734 ], [ -173.9146, 66.6729 ], [ -173.9167, 66.675 ], [ -173.9146, 66.6771 ], [ -173.9083, 66.675 ] ] ], [ [ [ -173.275, 66.8208 ], [ -173.2734, 66.8193 ], [ -173.2766, 66.8193 ], [ -173.2766, 66.8203 ], [ -173.275, 66.8208 ] ] ], [ [ [ -173.0854, 66.8542 ], [ -173.0917, 66.8542 ], [ -173.0932, 66.8557 ], [ -173.0838, 66.8557 ], [ -173.0854, 66.8542 ] ] ], [ [ [ -173.1, 66.8583 ], [ -173.0984, 66.8578 ], [ -173.0984, 66.8568 ], [ -173.1016, 66.8568 ], [ -173.1, 66.8583 ] ] ], [ [ [ -173.025, 67.0021 ], [ -173.0307, 67.0026 ], [ -173.0297, 67.0057 ], [ -173.0375, 67.0062 ], [ -173.0391, 67.0078 ], [ -173.0354, 67.0104 ], [ -173.0188, 67.0104 ], [ -173.0146, 67.0125 ], [ -173.0109, 67.012 ], [ -173.012, 67.0089 ], [ -173.0088, 67.0078 ], [ -173.0088, 67.0047 ], [ -173.0167, 67.0021 ], [ -173.025, 67.0021 ] ] ], [ [ [ -173.0896, 67.0188 ], [ -173.088, 67.0182 ], [ -173.088, 67.0172 ], [ -173.0911, 67.0172 ], [ -173.0896, 67.0188 ] ] ], [ [ [ -172.1505, 66.9589 ], [ -172.1604, 66.9583 ], [ -172.162, 66.9599 ], [ -172.1521, 66.9604 ], [ -172.1505, 66.9589 ] ] ], [ [ [ -172.1776, 66.962 ], [ -172.1792, 66.9604 ], [ -172.1911, 66.9609 ], [ -172.1896, 66.9625 ], [ -172.1776, 66.962 ] ] ], [ [ [ -172.1938, 66.9625 ], [ -172.2, 66.9625 ], [ -172.2016, 66.9641 ], [ -172.1922, 66.9641 ], [ -172.1938, 66.9625 ] ] ], [ [ [ -172.2068, 66.9661 ], [ -172.2083, 66.9646 ], [ -172.2161, 66.9651 ], [ -172.2146, 66.9667 ], [ -172.2068, 66.9661 ] ] ], [ [ [ -172.2214, 66.9672 ], [ -172.2354, 66.9667 ], [ -172.2396, 66.9688 ], [ -172.25, 66.9688 ], [ -172.2516, 66.9703 ], [ -172.2375, 66.9708 ], [ -172.2333, 66.9688 ], [ -172.2229, 66.9688 ], [ -172.2214, 66.9672 ] ] ], [ [ [ -172.2625, 66.9708 ], [ -172.2661, 66.9714 ], [ -172.2661, 66.9724 ], [ -172.2609, 66.9724 ], [ -172.2625, 66.9708 ] ] ], [ [ [ -172.2766, 66.9714 ], [ -172.275, 66.9729 ], [ -172.2713, 66.9724 ], [ -172.2729, 66.9708 ], [ -172.2766, 66.9714 ] ] ], [ [ [ -172.3151, 66.9786 ], [ -172.3167, 66.9771 ], [ -172.3266, 66.9776 ], [ -172.325, 66.9792 ], [ -172.3151, 66.9786 ] ] ], [ [ [ -174.7266, 67.0901 ], [ -174.725, 67.0917 ], [ -174.7214, 67.0911 ], [ -174.7229, 67.0896 ], [ -174.7266, 67.0901 ] ] ], [ [ [ -174.7229, 67.1062 ], [ -174.7146, 67.1062 ], [ -174.7104, 67.1083 ], [ -174.7083, 67.1062 ], [ -174.7109, 67.1005 ], [ -174.7187, 67.0979 ], [ -174.7224, 67.0984 ], [ -174.7208, 67.1021 ], [ -174.7245, 67.1047 ], [ -174.7229, 67.1062 ] ] ], [ [ [ -174.7083, 67.1125 ], [ -174.7068, 67.1109 ], [ -174.7099, 67.1109 ], [ -174.7099, 67.112 ], [ -174.7083, 67.1125 ] ] ], [ [ [ -174.7146, 67.1167 ], [ -174.7182, 67.1172 ], [ -174.7182, 67.1203 ], [ -174.713, 67.1203 ], [ -174.713, 67.1172 ], [ -174.7146, 67.1167 ] ] ], [ [ [ -174.7224, 67.1432 ], [ -174.7172, 67.1432 ], [ -174.713, 67.1401 ], [ -174.7167, 67.1375 ], [ -174.7287, 67.138 ], [ -174.7234, 67.1401 ], [ -174.7224, 67.1432 ] ] ], [ [ [ -174.6682, 67.1547 ], [ -174.6667, 67.1563 ], [ -174.663, 67.1557 ], [ -174.6646, 67.1542 ], [ -174.6682, 67.1547 ] ] ], [ [ [ -174.6729, 67.1854 ], [ -174.6651, 67.1849 ], [ -174.6672, 67.1776 ], [ -174.6724, 67.1776 ], [ -174.6714, 67.1828 ], [ -174.6745, 67.1839 ], [ -174.6729, 67.1854 ] ] ], [ [ [ -174.6911, 67.1943 ], [ -174.6896, 67.1958 ], [ -174.6859, 67.1953 ], [ -174.6875, 67.1937 ], [ -174.6911, 67.1943 ] ] ], [ [ [ -174.6938, 67.1979 ], [ -174.6922, 67.1974 ], [ -174.6922, 67.1964 ], [ -174.6953, 67.1964 ], [ -174.6938, 67.1979 ] ] ], [ [ [ -174.6786, 67.1984 ], [ -174.6771, 67.2 ], [ -174.6734, 67.1995 ], [ -174.675, 67.1979 ], [ -174.6786, 67.1984 ] ] ], [ [ [ -174.6958, 67.2 ], [ -174.6995, 67.2005 ], [ -174.6958, 67.2042 ], [ -174.6938, 67.2021 ], [ -174.6958, 67.2 ] ] ], [ [ [ -174.6771, 67.2042 ], [ -174.6807, 67.2047 ], [ -174.6807, 67.2057 ], [ -174.6755, 67.2057 ], [ -174.6771, 67.2042 ] ] ], [ [ [ -174.6979, 67.2229 ], [ -174.6943, 67.2224 ], [ -174.6901, 67.2193 ], [ -174.6938, 67.2167 ], [ -174.7016, 67.2172 ], [ -174.6963, 67.2193 ], [ -174.6979, 67.2208 ], [ -174.7042, 67.2208 ], [ -174.7104, 67.2188 ], [ -174.7245, 67.2266 ], [ -174.7208, 67.2271 ], [ -174.7125, 67.2229 ], [ -174.7083, 67.225 ], [ -174.7021, 67.225 ], [ -174.6979, 67.2229 ] ] ], [ [ [ -174.7083, 67.2333 ], [ -174.7026, 67.2276 ], [ -174.7104, 67.2271 ], [ -174.713, 67.2307 ], [ -174.7208, 67.2312 ], [ -174.7292, 67.2354 ], [ -174.7354, 67.2354 ], [ -174.738, 67.2318 ], [ -174.7432, 67.2318 ], [ -174.7391, 67.2412 ], [ -174.7339, 67.2422 ], [ -174.7292, 67.2479 ], [ -174.7255, 67.2474 ], [ -174.7266, 67.2443 ], [ -174.7088, 67.237 ], [ -174.7099, 67.2338 ], [ -174.7083, 67.2333 ] ] ], [ [ [ -174.7401, 67.2505 ], [ -174.7437, 67.2479 ], [ -174.7521, 67.2521 ], [ -174.7583, 67.2521 ], [ -174.7599, 67.2526 ], [ -174.7588, 67.2557 ], [ -174.7641, 67.2578 ], [ -174.7542, 67.2583 ], [ -174.75, 67.2562 ], [ -174.7458, 67.2562 ], [ -174.7401, 67.2536 ], [ -174.7401, 67.2505 ] ] ], [ [ [ -174.7422, 67.2672 ], [ -174.7453, 67.2703 ], [ -174.7375, 67.2708 ], [ -174.7318, 67.2682 ], [ -174.7359, 67.2589 ], [ -174.7375, 67.2583 ], [ -174.7411, 67.2589 ], [ -174.7401, 67.262 ], [ -174.7437, 67.2646 ], [ -174.7422, 67.2672 ] ] ], [ [ [ -172.3349, 66.9797 ], [ -172.3348, 66.9808 ], [ -172.3318, 66.9807 ], [ -172.3318, 66.9797 ], [ -172.3349, 66.9797 ] ] ], [ [ [ -172.3604, 66.9833 ], [ -172.3479, 66.9833 ], [ -172.3464, 66.9818 ], [ -172.3516, 66.9807 ], [ -172.3526, 66.9776 ], [ -172.3604, 66.9771 ], [ -172.3646, 66.9792 ], [ -172.3724, 66.9797 ], [ -172.3714, 66.9828 ], [ -172.3792, 66.9833 ], [ -172.3849, 66.987 ], [ -172.3667, 66.9854 ], [ -172.3604, 66.9833 ] ] ], [ [ [ -172.5568, 67.0057 ], [ -172.5542, 67.0042 ], [ -172.5313, 67.0042 ], [ -172.5255, 67.0005 ], [ -172.5354, 67.0 ], [ -172.5417, 66.9958 ], [ -172.5521, 66.9958 ], [ -172.5562, 66.9979 ], [ -172.5625, 66.9958 ], [ -172.5667, 66.9979 ], [ -172.5833, 66.9979 ], [ -172.5932, 67.0026 ], [ -172.5964, 67.0078 ], [ -172.6042, 67.0083 ], [ -172.6057, 67.0099 ], [ -172.5771, 67.0104 ], [ -172.5729, 67.0062 ], [ -172.5568, 67.0057 ] ] ], [ [ [ -172.7229, 67.0229 ], [ -172.7167, 67.025 ], [ -172.7125, 67.0229 ], [ -172.6875, 67.0208 ], [ -172.6833, 67.0188 ], [ -172.6729, 67.0188 ], [ -172.6687, 67.0167 ], [ -172.6583, 67.0167 ], [ -172.6542, 67.0146 ], [ -172.6375, 67.0146 ], [ -172.6333, 67.0125 ], [ -172.6229, 67.0125 ], [ -172.6214, 67.0109 ], [ -172.6417, 67.0083 ], [ -172.65, 67.0125 ], [ -172.6562, 67.0104 ], [ -172.6646, 67.0146 ], [ -172.6708, 67.0125 ], [ -172.6875, 67.0125 ], [ -172.6974, 67.0151 ], [ -172.7042, 67.0188 ], [ -172.7146, 67.0188 ], [ -172.7292, 67.0229 ], [ -172.7354, 67.0208 ], [ -172.7458, 67.0208 ], [ -172.7484, 67.0245 ], [ -172.7542, 67.025 ], [ -172.7557, 67.0266 ], [ -172.7437, 67.0271 ], [ -172.7396, 67.025 ], [ -172.7271, 67.025 ], [ -172.7229, 67.0229 ] ] ], [ [ [ -172.7646, 67.0271 ], [ -172.7661, 67.0286 ], [ -172.7609, 67.0286 ], [ -172.7609, 67.0276 ], [ -172.7646, 67.0271 ] ] ], [ [ [ -172.7745, 67.0307 ], [ -172.7713, 67.0307 ], [ -172.7713, 67.0297 ], [ -172.7745, 67.0297 ], [ -172.7745, 67.0307 ] ] ], [ [ [ -173.0917, 67.025 ], [ -173.0896, 67.0229 ], [ -173.0922, 67.0193 ], [ -173.1016, 67.0193 ], [ -173.0995, 67.0245 ], [ -173.0917, 67.025 ] ] ], [ [ [ -173.1182, 67.0276 ], [ -173.1167, 67.0292 ], [ -173.113, 67.0286 ], [ -173.1146, 67.0271 ], [ -173.1182, 67.0276 ] ] ], [ [ [ -172.8479, 67.0313 ], [ -172.8464, 67.0297 ], [ -172.8495, 67.0297 ], [ -172.8495, 67.0307 ], [ -172.8479, 67.0313 ] ] ], [ [ [ -174.3786, 67.0703 ], [ -174.3813, 67.0687 ], [ -174.4104, 67.0687 ], [ -174.4146, 67.0667 ], [ -174.4208, 67.0667 ], [ -174.4224, 67.0672 ], [ -174.4214, 67.0703 ], [ -174.4229, 67.0708 ], [ -174.4354, 67.0708 ], [ -174.437, 67.0693 ], [ -174.4292, 67.0687 ], [ -174.4276, 67.0672 ], [ -174.4438, 67.0667 ], [ -174.4542, 67.0625 ], [ -174.4708, 67.0625 ], [ -174.4724, 67.0641 ], [ -174.4672, 67.0661 ], [ -174.4729, 67.0667 ], [ -174.4745, 67.0682 ], [ -174.4625, 67.0687 ], [ -174.4583, 67.0667 ], [ -174.4521, 67.0667 ], [ -174.4505, 67.0682 ], [ -174.4563, 67.0687 ], [ -174.4646, 67.0729 ], [ -174.4833, 67.0729 ], [ -174.4875, 67.0708 ], [ -174.4953, 67.0703 ], [ -174.5, 67.0667 ], [ -174.5042, 67.0687 ], [ -174.5167, 67.0687 ], [ -174.5208, 67.0646 ], [ -174.55, 67.0667 ], [ -174.5516, 67.0661 ], [ -174.5516, 67.063 ], [ -174.5354, 67.0625 ], [ -174.5297, 67.0589 ], [ -174.5479, 67.0583 ], [ -174.5521, 67.0563 ], [ -174.5604, 67.0563 ], [ -174.5646, 67.0542 ], [ -174.5688, 67.0563 ], [ -174.5813, 67.0563 ], [ -174.5953, 67.0516 ], [ -174.5974, 67.0484 ], [ -174.5896, 67.0479 ], [ -174.588, 67.0463 ], [ -174.5958, 67.0437 ], [ -174.6083, 67.0437 ], [ -174.6125, 67.0417 ], [ -174.6208, 67.0417 ], [ -174.6266, 67.0443 ], [ -174.6255, 67.0474 ], [ -174.6286, 67.0484 ], [ -174.6234, 67.0505 ], [ -174.6203, 67.0557 ], [ -174.6151, 67.0568 ], [ -174.6141, 67.0599 ], [ -174.6089, 67.0609 ], [ -174.6057, 67.0661 ], [ -174.5896, 67.0667 ], [ -174.5792, 67.0708 ], [ -174.575, 67.0687 ], [ -174.5688, 67.0708 ], [ -174.5562, 67.0708 ], [ -174.5521, 67.0729 ], [ -174.5354, 67.0729 ], [ -174.5292, 67.0708 ], [ -174.525, 67.0729 ], [ -174.5063, 67.0729 ], [ -174.5021, 67.075 ], [ -174.4625, 67.075 ], [ -174.4583, 67.0771 ], [ -174.4542, 67.075 ], [ -174.4479, 67.0771 ], [ -174.3875, 67.0771 ], [ -174.3833, 67.075 ], [ -174.3792, 67.0771 ], [ -174.3729, 67.0771 ], [ -174.3703, 67.0734 ], [ -174.3672, 67.0724 ], [ -174.3687, 67.0708 ], [ -174.3786, 67.0703 ] ] ], [ [ [ -171.8542, 66.9542 ], [ -171.8526, 66.9537 ], [ -171.8526, 66.9526 ], [ -171.8557, 66.9526 ], [ -171.8542, 66.9542 ] ] ], [ [ [ 170.0875, 68.7917 ], [ 170.0859, 68.7932 ], [ 170.0917, 68.7937 ], [ 170.0917, 68.7917 ], [ 170.0875, 68.7917 ] ] ], [ [ [ -174.7401, 67.2839 ], [ -174.7437, 67.2833 ], [ -174.7453, 67.2849 ], [ -174.7417, 67.2875 ], [ -174.7401, 67.287 ], [ -174.7401, 67.2839 ] ] ], [ [ [ -174.7411, 67.2911 ], [ -174.738, 67.2911 ], [ -174.738, 67.2901 ], [ -174.7411, 67.2901 ], [ -174.7411, 67.2911 ] ] ], [ [ [ -174.7417, 67.2958 ], [ -174.7401, 67.2943 ], [ -174.7437, 67.2917 ], [ -174.7495, 67.2943 ], [ -174.7505, 67.2974 ], [ -174.7583, 67.2979 ], [ -174.7599, 67.2995 ], [ -174.7547, 67.3005 ], [ -174.75, 67.3042 ], [ -174.7422, 67.3016 ], [ -174.7437, 67.2979 ], [ -174.7417, 67.2958 ] ] ], [ [ [ -174.8755, 67.4068 ], [ -174.8792, 67.4063 ], [ -174.8807, 67.4078 ], [ -174.8771, 67.4104 ], [ -174.8755, 67.4099 ], [ -174.8755, 67.4068 ] ] ], [ [ [ -175.288, 67.3484 ], [ -175.2958, 67.3479 ], [ -175.2974, 67.3495 ], [ -175.2896, 67.35 ], [ -175.288, 67.3484 ] ] ], [ [ [ -175.1724, 67.363 ], [ -175.163, 67.362 ], [ -175.162, 67.3588 ], [ -175.1521, 67.3562 ], [ -175.1396, 67.3562 ], [ -175.138, 67.3547 ], [ -175.1516, 67.3516 ], [ -175.1542, 67.3479 ], [ -175.1667, 67.3479 ], [ -175.1708, 67.35 ], [ -175.175, 67.3479 ], [ -175.1953, 67.3484 ], [ -175.187, 67.3536 ], [ -175.1792, 67.3542 ], [ -175.1755, 67.3568 ], [ -175.1812, 67.3604 ], [ -175.1891, 67.3609 ], [ -175.1901, 67.3641 ], [ -175.1932, 67.3651 ], [ -175.1911, 67.3682 ], [ -175.1724, 67.363 ] ] ], [ [ [ -175.1037, 67.4391 ], [ -175.1005, 67.4391 ], [ -175.1005, 67.438 ], [ -175.1037, 67.438 ], [ -175.1037, 67.4391 ] ] ], [ [ [ -175.1125, 67.4417 ], [ -175.1141, 67.4432 ], [ -175.1089, 67.4432 ], [ -175.1089, 67.4422 ], [ -175.1125, 67.4417 ] ] ], [ [ [ -174.9042, 67.4187 ], [ -174.9078, 67.4193 ], [ -174.9078, 67.4203 ], [ -174.9026, 67.4203 ], [ -174.9042, 67.4187 ] ] ], [ [ [ -174.9745, 67.437 ], [ -174.9714, 67.437 ], [ -174.9714, 67.4359 ], [ -174.9745, 67.4359 ], [ -174.9745, 67.437 ] ] ], [ [ [ -174.9683, 67.4418 ], [ -174.9724, 67.4422 ], [ -174.9724, 67.4432 ], [ -174.9672, 67.4432 ], [ -174.9683, 67.4418 ] ] ], [ [ [ -175.0521, 67.4417 ], [ -175.0536, 67.4432 ], [ -175.0484, 67.4432 ], [ -175.0484, 67.4422 ], [ -175.0521, 67.4417 ] ] ], [ [ [ -174.9874, 67.448 ], [ -174.9911, 67.4484 ], [ -174.9911, 67.4495 ], [ -174.9859, 67.4495 ], [ -174.9874, 67.448 ] ] ], [ [ [ -174.612, 67.4693 ], [ -174.6026, 67.4682 ], [ -174.5901, 67.462 ], [ -174.5891, 67.4589 ], [ -174.5859, 67.4568 ], [ -174.5911, 67.4557 ], [ -174.5943, 67.4505 ], [ -174.5979, 67.4479 ], [ -174.6161, 67.4484 ], [ -174.6203, 67.4516 ], [ -174.6151, 67.4537 ], [ -174.6276, 67.4599 ], [ -174.6328, 67.4609 ], [ -174.6318, 67.4641 ], [ -174.6354, 67.4667 ], [ -174.6338, 67.4724 ], [ -174.6411, 67.4766 ], [ -174.6333, 67.4771 ], [ -174.625, 67.4729 ], [ -174.6172, 67.4724 ], [ -174.612, 67.4693 ] ] ], [ [ [ -175.0521, 67.4667 ], [ -175.0505, 67.4661 ], [ -175.0505, 67.463 ], [ -175.0599, 67.463 ], [ -175.0599, 67.4661 ], [ -175.0521, 67.4667 ] ] ], [ [ [ -175.0667, 67.4688 ], [ -175.0682, 67.4703 ], [ -175.063, 67.4703 ], [ -175.063, 67.4693 ], [ -175.0667, 67.4688 ] ] ], [ [ [ -175.0693, 67.4714 ], [ -175.0786, 67.4714 ], [ -175.0776, 67.4745 ], [ -175.0828, 67.4755 ], [ -175.0818, 67.4786 ], [ -175.0896, 67.4792 ], [ -175.0911, 67.4807 ], [ -175.0875, 67.4833 ], [ -175.0755, 67.4828 ], [ -175.0745, 67.4797 ], [ -175.0713, 67.4786 ], [ -175.0724, 67.4734 ], [ -175.0693, 67.4714 ] ] ], [ [ [ -174.6453, 67.4776 ], [ -174.6453, 67.4786 ], [ -174.6422, 67.4786 ], [ -174.6422, 67.4776 ], [ -174.6453, 67.4776 ] ] ], [ [ [ -175.0932, 67.488 ], [ -175.0854, 67.4875 ], [ -175.0838, 67.4859 ], [ -175.0979, 67.4854 ], [ -175.1141, 67.4901 ], [ -175.1224, 67.4953 ], [ -175.1083, 67.4958 ], [ -175.1068, 67.4953 ], [ -175.1078, 67.4922 ], [ -175.1, 67.4917 ], [ -175.0932, 67.488 ] ] ], [ [ [ -175.1255, 67.5016 ], [ -175.1271, 67.5 ], [ -175.1349, 67.5005 ], [ -175.1333, 67.5021 ], [ -175.1255, 67.5016 ] ] ], [ [ [ -175.1375, 67.5042 ], [ -175.1391, 67.5057 ], [ -175.1338, 67.5057 ], [ -175.1338, 67.5047 ], [ -175.1375, 67.5042 ] ] ], [ [ [ -175.1458, 67.5062 ], [ -175.1495, 67.5068 ], [ -175.1495, 67.5078 ], [ -175.1443, 67.5078 ], [ -175.1458, 67.5062 ] ] ], [ [ [ -175.1562, 67.5146 ], [ -175.1526, 67.513 ], [ -175.1604, 67.5104 ], [ -175.1641, 67.5109 ], [ -175.162, 67.5141 ], [ -175.1562, 67.5146 ] ] ], [ [ [ -175.1938, 67.5125 ], [ -175.1953, 67.5141 ], [ -175.1901, 67.5141 ], [ -175.1901, 67.513 ], [ -175.1938, 67.5125 ] ] ], [ [ [ -175.2063, 67.5208 ], [ -175.2078, 67.5224 ], [ -175.2026, 67.5224 ], [ -175.2026, 67.5214 ], [ -175.2063, 67.5208 ] ] ], [ [ [ -175.7484, 67.5964 ], [ -175.7563, 67.5958 ], [ -175.7578, 67.5974 ], [ -175.75, 67.5979 ], [ -175.7484, 67.5964 ] ] ], [ [ [ -175.7729, 67.7979 ], [ -175.7688, 67.7979 ], [ -175.7672, 67.7963 ], [ -175.7828, 67.7963 ], [ -175.7812, 67.7979 ], [ -175.7729, 67.7979 ] ] ], [ [ [ -175.7833, 67.8 ], [ -175.7917, 67.8 ], [ -175.7932, 67.8016 ], [ -175.7896, 67.8042 ], [ -175.7776, 67.8016 ], [ -175.7792, 67.8 ], [ -175.7833, 67.8 ] ] ], [ [ [ -175.7958, 67.8042 ], [ -175.8062, 67.8042 ], [ -175.8104, 67.8063 ], [ -175.8208, 67.8063 ], [ -175.8224, 67.8078 ], [ -175.8042, 67.8083 ], [ -175.8, 67.8063 ], [ -175.7922, 67.8057 ], [ -175.7958, 67.8042 ] ] ], [ [ [ -175.8266, 67.8099 ], [ -175.8234, 67.8099 ], [ -175.8234, 67.8089 ], [ -175.8266, 67.8089 ], [ -175.8266, 67.8099 ] ] ], [ [ [ -176.712, 68.0547 ], [ -176.7042, 68.0542 ], [ -176.6938, 68.05 ], [ -176.6896, 68.05 ], [ -176.675, 68.0437 ], [ -176.6672, 68.0432 ], [ -176.6604, 68.0396 ], [ -176.6542, 68.0396 ], [ -176.6463, 68.037 ], [ -176.6474, 68.0339 ], [ -176.6458, 68.0333 ], [ -176.6333, 68.0333 ], [ -176.6307, 68.0297 ], [ -176.6234, 68.0286 ], [ -176.6104, 68.0229 ], [ -176.6042, 68.0229 ], [ -176.5958, 68.0188 ], [ -176.5896, 68.0188 ], [ -176.5818, 68.0162 ], [ -176.5828, 68.013 ], [ -176.5813, 68.0125 ], [ -176.5688, 68.0125 ], [ -176.5672, 68.012 ], [ -176.5682, 68.0089 ], [ -176.5604, 68.0083 ], [ -176.5583, 68.0062 ], [ -176.5604, 68.0042 ], [ -176.5667, 68.0042 ], [ -176.5708, 68.0062 ], [ -176.5896, 68.0062 ], [ -176.5979, 68.0104 ], [ -176.6104, 68.0104 ], [ -176.6146, 68.0125 ], [ -176.6187, 68.0125 ], [ -176.6333, 68.0188 ], [ -176.6396, 68.0188 ], [ -176.6516, 68.0234 ], [ -176.6557, 68.0266 ], [ -176.6505, 68.0286 ], [ -176.6583, 68.0292 ], [ -176.6708, 68.0354 ], [ -176.6896, 68.0354 ], [ -176.6938, 68.0375 ], [ -176.7016, 68.038 ], [ -176.7005, 68.0411 ], [ -176.7083, 68.0417 ], [ -176.7161, 68.0443 ], [ -176.7203, 68.0463 ], [ -176.7229, 68.05 ], [ -176.7307, 68.0505 ], [ -176.7297, 68.0537 ], [ -176.7328, 68.0547 ], [ -176.7339, 68.0578 ], [ -176.7417, 68.0583 ], [ -176.7474, 68.062 ], [ -176.7333, 68.0625 ], [ -176.725, 68.0583 ], [ -176.7172, 68.0578 ], [ -176.712, 68.0547 ] ] ], [ [ [ -176.7708, 68.0708 ], [ -176.7724, 68.0724 ], [ -176.7672, 68.0724 ], [ -176.7672, 68.0714 ], [ -176.7708, 68.0708 ] ] ], [ [ [ -177.0443, 68.1422 ], [ -177.0536, 68.1443 ], [ -177.0526, 68.1474 ], [ -177.0578, 68.1495 ], [ -177.0437, 68.15 ], [ -177.0359, 68.1474 ], [ -177.0412, 68.1453 ], [ -177.0443, 68.1422 ] ] ], [ [ [ -177.1479, 68.175 ], [ -177.1578, 68.1797 ], [ -177.1578, 68.1807 ], [ -177.15, 68.1813 ], [ -177.138, 68.1766 ], [ -177.1396, 68.175 ], [ -177.1479, 68.175 ] ] ], [ [ [ -177.5078, 68.2068 ], [ -177.5063, 68.2083 ], [ -177.5026, 68.2078 ], [ -177.5042, 68.2063 ], [ -177.5078, 68.2068 ] ] ], [ [ [ -177.6104, 68.2167 ], [ -177.6026, 68.2161 ], [ -177.6026, 68.213 ], [ -177.6062, 68.2125 ], [ -177.6104, 68.2146 ], [ -177.6151, 68.2109 ], [ -177.6245, 68.2099 ], [ -177.6276, 68.2068 ], [ -177.6354, 68.2042 ], [ -177.6479, 68.2042 ], [ -177.6557, 68.2016 ], [ -177.6589, 68.1943 ], [ -177.6604, 68.1937 ], [ -177.6682, 68.1964 ], [ -177.6687, 68.2 ], [ -177.6641, 68.2099 ], [ -177.6542, 68.2125 ], [ -177.6526, 68.212 ], [ -177.6536, 68.2089 ], [ -177.6484, 68.2089 ], [ -177.6458, 68.2146 ], [ -177.6453, 68.2203 ], [ -177.6432, 68.2224 ], [ -177.638, 68.2234 ], [ -177.637, 68.2266 ], [ -177.6214, 68.2266 ], [ -177.6203, 68.2234 ], [ -177.6109, 68.2224 ], [ -177.612, 68.2172 ], [ -177.6104, 68.2167 ] ] ], [ [ [ -177.4797, 68.2266 ], [ -177.4833, 68.2229 ], [ -177.4911, 68.2234 ], [ -177.4833, 68.2271 ], [ -177.4797, 68.2266 ] ] ], [ [ [ -177.4964, 68.2286 ], [ -177.5042, 68.225 ], [ -177.5078, 68.2255 ], [ -177.5042, 68.2292 ], [ -177.4964, 68.2286 ] ] ], [ [ [ -177.7312, 68.2396 ], [ -177.7297, 68.238 ], [ -177.7328, 68.238 ], [ -177.7328, 68.2391 ], [ -177.7312, 68.2396 ] ] ], [ [ [ -177.4271, 68.2375 ], [ -177.4333, 68.2375 ], [ -177.4349, 68.2391 ], [ -177.4255, 68.2391 ], [ -177.4271, 68.2375 ] ] ], [ [ [ -177.7391, 68.2401 ], [ -177.7375, 68.2417 ], [ -177.7339, 68.2412 ], [ -177.7354, 68.2396 ], [ -177.7391, 68.2401 ] ] ], [ [ [ -177.362, 68.2422 ], [ -177.3661, 68.2453 ], [ -177.3583, 68.2458 ], [ -177.3505, 68.2432 ], [ -177.3521, 68.2417 ], [ -177.362, 68.2422 ] ] ], [ [ [ -177.3687, 68.2458 ], [ -177.375, 68.2458 ], [ -177.3766, 68.2474 ], [ -177.3672, 68.2474 ], [ -177.3687, 68.2458 ] ] ], [ [ [ -177.4005, 68.2484 ], [ -177.4083, 68.2458 ], [ -177.4104, 68.2479 ], [ -177.4083, 68.25 ], [ -177.4021, 68.25 ], [ -177.4005, 68.2484 ] ] ], [ [ [ -177.3792, 68.25 ], [ -177.3776, 68.2484 ], [ -177.3932, 68.2484 ], [ -177.3917, 68.25 ], [ -177.3792, 68.25 ] ] ], [ [ [ -177.4411, 68.2589 ], [ -177.4396, 68.2604 ], [ -177.4359, 68.2599 ], [ -177.4375, 68.2583 ], [ -177.4411, 68.2589 ] ] ], [ [ [ -177.4984, 68.2745 ], [ -177.5021, 68.2729 ], [ -177.5063, 68.275 ], [ -177.5125, 68.275 ], [ -177.5182, 68.2776 ], [ -177.5188, 68.2792 ], [ -177.5167, 68.2813 ], [ -177.5104, 68.2792 ], [ -177.5042, 68.2792 ], [ -177.5016, 68.2755 ], [ -177.4984, 68.2745 ] ] ], [ [ [ -177.525, 68.2813 ], [ -177.5286, 68.2818 ], [ -177.5286, 68.2828 ], [ -177.5234, 68.2828 ], [ -177.525, 68.2813 ] ] ], [ [ [ -177.7854, 68.3708 ], [ -177.7839, 68.3693 ], [ -177.7917, 68.3688 ], [ -177.7937, 68.3708 ], [ -177.7917, 68.3729 ], [ -177.7854, 68.3708 ] ] ], [ [ [ -177.8021, 68.3729 ], [ -177.8089, 68.3766 ], [ -177.8167, 68.3771 ], [ -177.8182, 68.3786 ], [ -177.8042, 68.3792 ], [ -177.7943, 68.3745 ], [ -177.7958, 68.3729 ], [ -177.8021, 68.3729 ] ] ], [ [ [ -177.8182, 68.3818 ], [ -177.8167, 68.3833 ], [ -177.813, 68.3828 ], [ -177.8146, 68.3812 ], [ -177.8182, 68.3818 ] ] ], [ [ [ -177.8797, 68.4047 ], [ -177.8875, 68.4042 ], [ -177.8917, 68.4083 ], [ -177.9042, 68.4083 ], [ -177.9057, 68.4099 ], [ -177.8958, 68.4125 ], [ -177.8917, 68.4104 ], [ -177.8838, 68.4099 ], [ -177.8828, 68.4068 ], [ -177.8797, 68.4047 ] ] ], [ [ [ -177.9005, 68.413 ], [ -177.9083, 68.4125 ], [ -177.9099, 68.4141 ], [ -177.9021, 68.4146 ], [ -177.9005, 68.413 ] ] ], [ [ [ -178.1391, 68.4818 ], [ -178.125, 68.4812 ], [ -178.1208, 68.4792 ], [ -178.1042, 68.4771 ], [ -178.1016, 68.4734 ], [ -178.0974, 68.4714 ], [ -178.0833, 68.4708 ], [ -178.0542, 68.4583 ], [ -178.0458, 68.4583 ], [ -178.0417, 68.4563 ], [ -178.0354, 68.4583 ], [ -178.0328, 68.4547 ], [ -178.0286, 68.4526 ], [ -178.0088, 68.4495 ], [ -178.0078, 68.4464 ], [ -178.0026, 68.4453 ], [ -177.9917, 68.4396 ], [ -177.9792, 68.4396 ], [ -177.9646, 68.4333 ], [ -177.9583, 68.4333 ], [ -177.9505, 68.4307 ], [ -177.9396, 68.425 ], [ -177.9312, 68.425 ], [ -177.9203, 68.4193 ], [ -177.9104, 68.4187 ], [ -177.9089, 68.4172 ], [ -177.9125, 68.4146 ], [ -177.9187, 68.4167 ], [ -177.9229, 68.4146 ], [ -177.9292, 68.4167 ], [ -177.9354, 68.4167 ], [ -177.9411, 68.4193 ], [ -177.9422, 68.4224 ], [ -177.9646, 68.4313 ], [ -177.9708, 68.4313 ], [ -177.975, 68.4333 ], [ -177.9875, 68.4333 ], [ -178.0021, 68.4396 ], [ -178.0146, 68.4396 ], [ -178.0271, 68.4458 ], [ -178.037, 68.4464 ], [ -178.0453, 68.4505 ], [ -178.0464, 68.4537 ], [ -178.0542, 68.4542 ], [ -178.062, 68.4568 ], [ -178.0661, 68.4589 ], [ -178.0688, 68.4625 ], [ -178.0813, 68.4625 ], [ -178.0875, 68.4604 ], [ -178.1021, 68.4667 ], [ -178.1099, 68.4672 ], [ -178.1089, 68.4703 ], [ -178.1104, 68.4708 ], [ -178.1396, 68.4729 ], [ -178.15, 68.4771 ], [ -178.1562, 68.4771 ], [ -178.1578, 68.4776 ], [ -178.1568, 68.4807 ], [ -178.1667, 68.4812 ], [ -178.1766, 68.487 ], [ -178.1687, 68.4896 ], [ -178.1625, 68.4896 ], [ -178.1495, 68.4838 ], [ -178.1417, 68.4833 ], [ -178.1391, 68.4818 ] ] ], [ [ [ -178.3286, 68.4755 ], [ -178.3271, 68.4771 ], [ -178.3234, 68.4766 ], [ -178.325, 68.475 ], [ -178.3286, 68.4755 ] ] ], [ [ [ -178.3313, 68.4771 ], [ -178.3375, 68.4771 ], [ -178.3391, 68.4786 ], [ -178.3297, 68.4786 ], [ -178.3313, 68.4771 ] ] ], [ [ [ -178.1875, 68.5 ], [ -178.1938, 68.5 ], [ -178.1953, 68.5016 ], [ -178.1859, 68.5016 ], [ -178.1875, 68.5 ] ] ], [ [ [ -178.3016, 68.5276 ], [ -178.2958, 68.525 ], [ -178.2896, 68.525 ], [ -178.2818, 68.5224 ], [ -178.275, 68.5188 ], [ -178.2563, 68.5167 ], [ -178.2547, 68.5162 ], [ -178.2557, 68.513 ], [ -178.2458, 68.5125 ], [ -178.2417, 68.5104 ], [ -178.2339, 68.5109 ], [ -178.2375, 68.5146 ], [ -178.237, 68.5162 ], [ -178.2333, 68.5167 ], [ -178.225, 68.5125 ], [ -178.2187, 68.5125 ], [ -178.2109, 68.5099 ], [ -178.1963, 68.5026 ], [ -178.2125, 68.5021 ], [ -178.2167, 68.5 ], [ -178.2229, 68.5 ], [ -178.2312, 68.5042 ], [ -178.2412, 68.5047 ], [ -178.2479, 68.5083 ], [ -178.2604, 68.5083 ], [ -178.275, 68.5125 ], [ -178.2917, 68.5146 ], [ -178.3255, 68.5286 ], [ -178.3333, 68.5292 ], [ -178.3411, 68.5318 ], [ -178.3417, 68.5333 ], [ -178.3396, 68.5354 ], [ -178.3208, 68.5354 ], [ -178.3182, 68.5318 ], [ -178.3104, 68.5313 ], [ -178.3016, 68.5276 ] ] ], [ [ [ -178.2396, 68.5167 ], [ -178.2458, 68.5167 ], [ -178.2474, 68.5182 ], [ -178.238, 68.5182 ], [ -178.2396, 68.5167 ] ] ], [ [ [ -178.2536, 68.5193 ], [ -178.2521, 68.5208 ], [ -178.2484, 68.5203 ], [ -178.25, 68.5188 ], [ -178.2536, 68.5193 ] ] ], [ [ [ -178.3458, 68.5479 ], [ -178.3443, 68.5474 ], [ -178.3443, 68.5463 ], [ -178.3474, 68.5463 ], [ -178.3458, 68.5479 ] ] ], [ [ [ -178.3849, 68.5568 ], [ -178.3833, 68.5583 ], [ -178.3797, 68.5578 ], [ -178.3813, 68.5563 ], [ -178.3849, 68.5568 ] ] ], [ [ [ -178.3979, 68.5604 ], [ -178.4016, 68.5609 ], [ -178.4057, 68.5641 ], [ -178.3901, 68.562 ], [ -178.3917, 68.5604 ], [ -178.3979, 68.5604 ] ] ], [ [ [ -178.4162, 68.5672 ], [ -178.4146, 68.5687 ], [ -178.4109, 68.5682 ], [ -178.4125, 68.5667 ], [ -178.4162, 68.5672 ] ] ], [ [ [ -178.4391, 68.5672 ], [ -178.4375, 68.5687 ], [ -178.4339, 68.5682 ], [ -178.4354, 68.5667 ], [ -178.4391, 68.5672 ] ] ], [ [ [ -178.4328, 68.5693 ], [ -178.4312, 68.5708 ], [ -178.4276, 68.5703 ], [ -178.4292, 68.5687 ], [ -178.4328, 68.5693 ] ] ], [ [ [ -178.4505, 68.5734 ], [ -178.4583, 68.5729 ], [ -178.4599, 68.5745 ], [ -178.4521, 68.575 ], [ -178.4505, 68.5734 ] ] ], [ [ [ -178.4849, 68.588 ], [ -178.4693, 68.5828 ], [ -178.4609, 68.5776 ], [ -178.4687, 68.5771 ], [ -178.4875, 68.5854 ], [ -178.5063, 68.5875 ], [ -178.5088, 68.5911 ], [ -178.5182, 68.5953 ], [ -178.5146, 68.5979 ], [ -178.5021, 68.5979 ], [ -178.4995, 68.5943 ], [ -178.4901, 68.5932 ], [ -178.4891, 68.5901 ], [ -178.4849, 68.588 ] ] ], [ [ [ -178.5276, 68.6057 ], [ -178.525, 68.6042 ], [ -178.5167, 68.6042 ], [ -178.5151, 68.6026 ], [ -178.5188, 68.5979 ], [ -178.525, 68.6 ], [ -178.5312, 68.6 ], [ -178.5339, 68.6036 ], [ -178.5536, 68.612 ], [ -178.5437, 68.6146 ], [ -178.5318, 68.6099 ], [ -178.5307, 68.6068 ], [ -178.5276, 68.6057 ] ] ], [ [ [ -178.6062, 68.6312 ], [ -178.6161, 68.6339 ], [ -178.6203, 68.637 ], [ -178.6104, 68.6396 ], [ -178.5984, 68.6349 ], [ -178.6005, 68.6318 ], [ -178.6062, 68.6312 ] ] ], [ [ [ -178.6313, 68.6438 ], [ -178.6411, 68.6464 ], [ -178.6401, 68.6495 ], [ -178.6453, 68.6516 ], [ -178.6354, 68.6542 ], [ -178.6234, 68.6495 ], [ -178.6286, 68.6474 ], [ -178.6313, 68.6438 ] ] ], [ [ [ -178.6458, 68.6604 ], [ -178.6443, 68.6589 ], [ -178.6521, 68.6583 ], [ -178.6542, 68.6604 ], [ -178.6521, 68.6625 ], [ -178.6458, 68.6604 ] ] ], [ [ [ -178.6562, 68.6667 ], [ -178.6547, 68.6651 ], [ -178.6578, 68.6651 ], [ -178.6578, 68.6661 ], [ -178.6562, 68.6667 ] ] ], [ [ [ -178.6693, 68.6703 ], [ -178.6651, 68.6672 ], [ -178.6687, 68.6667 ], [ -178.6745, 68.6693 ], [ -178.6729, 68.6708 ], [ -178.6693, 68.6703 ] ] ], [ [ [ -178.937, 68.7693 ], [ -178.9354, 68.7708 ], [ -178.9318, 68.7703 ], [ -178.9333, 68.7688 ], [ -178.937, 68.7693 ] ] ], [ [ [ -179.1292, 68.8125 ], [ -179.1328, 68.813 ], [ -179.1328, 68.8141 ], [ -179.1276, 68.8141 ], [ -179.1292, 68.8125 ] ] ], [ [ [ -179.3771, 68.8812 ], [ -179.3734, 68.8807 ], [ -179.3734, 68.8776 ], [ -179.3771, 68.8771 ], [ -179.3828, 68.8797 ], [ -179.3813, 68.8812 ], [ -179.3771, 68.8812 ] ] ], [ [ [ -179.3891, 68.8818 ], [ -179.3875, 68.8833 ], [ -179.3838, 68.8828 ], [ -179.3854, 68.8812 ], [ -179.3891, 68.8818 ] ] ], [ [ [ -179.3917, 68.8854 ], [ -179.3901, 68.8839 ], [ -179.3932, 68.8839 ], [ -179.3932, 68.8849 ], [ -179.3917, 68.8854 ] ] ], [ [ [ -179.3995, 68.8859 ], [ -179.3979, 68.8875 ], [ -179.3943, 68.887 ], [ -179.3958, 68.8854 ], [ -179.3995, 68.8859 ] ] ], [ [ [ -180.0, 68.9859 ], [ -179.9979, 68.9875 ], [ -179.9943, 68.987 ], [ -179.9958, 68.9854 ], [ -180.0, 68.9859 ] ] ], [ [ [ -180.0, 68.9922 ], [ -180.0, 68.9974 ], [ -179.9984, 68.9995 ], [ -179.9984, 68.9901 ], [ -180.0, 68.9922 ] ] ], [ [ [ -179.9979, 69.0083 ], [ -179.9964, 69.0068 ], [ -180.0, 69.0068 ], [ -180.0, 69.0078 ], [ -179.9979, 69.0083 ] ] ], [ [ [ -180.0, 69.0234 ], [ -180.0, 69.0286 ], [ -179.9984, 69.0307 ], [ -179.9984, 69.0214 ], [ -180.0, 69.0234 ] ] ], [ [ [ -179.9979, 69.0542 ], [ -179.9984, 69.0526 ], [ -180.0, 69.0531 ], [ -180.0, 69.0552 ], [ -179.9984, 69.0557 ], [ -179.9979, 69.0542 ] ] ], [ [ [ -180.0, 69.087 ], [ -179.9984, 69.087 ], [ -179.9984, 69.0859 ], [ -180.0, 69.0859 ], [ -180.0, 69.087 ] ] ], [ [ [ -179.9979, 69.0938 ], [ -179.9984, 69.0901 ], [ -180.0, 69.0927 ], [ -180.0, 69.099 ], [ -179.9979, 69.1021 ], [ -179.9958, 69.1 ], [ -179.9979, 69.0938 ] ] ], [ [ [ -180.0, 69.1073 ], [ -179.9964, 69.1078 ], [ -179.9964, 69.1047 ], [ -180.0, 69.1052 ], [ -180.0, 69.1073 ] ] ], [ [ [ -179.1396, 68.8187 ], [ -179.1339, 68.8182 ], [ -179.1339, 68.8151 ], [ -179.1495, 68.8151 ], [ -179.1396, 68.8187 ] ] ], [ [ [ -179.0125, 71.5833 ], [ -178.9833, 71.5833 ], [ -178.9792, 71.5813 ], [ -178.9708, 71.5833 ], [ -178.9667, 71.5813 ], [ -178.9583, 71.5813 ], [ -178.9542, 71.5792 ], [ -178.9479, 71.5813 ], [ -178.9375, 71.5813 ], [ -178.9208, 71.575 ], [ -178.8917, 71.575 ], [ -178.8854, 71.5771 ], [ -178.8813, 71.575 ], [ -178.8729, 71.575 ], [ -178.8687, 71.5729 ], [ -178.8604, 71.5729 ], [ -178.8562, 71.5708 ], [ -178.85, 71.5729 ], [ -178.8271, 71.5729 ], [ -178.8229, 71.5708 ], [ -178.8146, 71.5729 ], [ -178.8104, 71.5708 ], [ -178.8062, 71.5708 ], [ -178.8021, 71.5729 ], [ -178.7937, 71.5729 ], [ -178.7896, 71.575 ], [ -178.7813, 71.575 ], [ -178.7771, 71.5771 ], [ -178.7729, 71.5771 ], [ -178.7688, 71.575 ], [ -178.7271, 71.575 ], [ -178.7229, 71.5729 ], [ -178.7083, 71.5729 ], [ -178.7042, 71.5708 ], [ -178.6958, 71.5708 ], [ -178.6917, 71.5687 ], [ -178.6687, 71.5687 ], [ -178.6646, 71.5646 ], [ -178.6583, 71.5667 ], [ -178.6375, 71.5667 ], [ -178.6333, 71.5646 ], [ -178.6214, 71.5641 ], [ -178.6229, 71.5625 ], [ -178.6286, 71.562 ], [ -178.6286, 71.5589 ], [ -178.6271, 71.5583 ], [ -178.6104, 71.5583 ], [ -178.6062, 71.5563 ], [ -178.6, 71.5583 ], [ -178.5708, 71.5583 ], [ -178.5693, 71.5578 ], [ -178.5703, 71.5547 ], [ -178.5625, 71.5521 ], [ -178.5568, 71.5526 ], [ -178.5542, 71.5563 ], [ -178.5396, 71.5563 ], [ -178.5354, 71.5542 ], [ -178.5271, 71.5542 ], [ -178.5229, 71.5521 ], [ -178.5188, 71.5521 ], [ -178.5146, 71.5542 ], [ -178.5063, 71.5521 ], [ -178.5, 71.5542 ], [ -178.4974, 71.5505 ], [ -178.4875, 71.55 ], [ -178.4833, 71.5479 ], [ -178.4687, 71.5479 ], [ -178.4646, 71.5458 ], [ -178.4542, 71.5458 ], [ -178.45, 71.5437 ], [ -178.4292, 71.5417 ], [ -178.425, 71.5396 ], [ -178.4104, 71.5396 ], [ -178.4062, 71.5375 ], [ -178.3979, 71.5375 ], [ -178.3875, 71.5333 ], [ -178.3771, 71.5333 ], [ -178.3729, 71.5313 ], [ -178.3646, 71.5313 ], [ -178.3604, 71.5292 ], [ -178.3458, 71.5292 ], [ -178.3354, 71.525 ], [ -178.3208, 71.525 ], [ -178.3104, 71.5208 ], [ -178.3, 71.5229 ], [ -178.2958, 71.5188 ], [ -178.2812, 71.5188 ], [ -178.2583, 71.5104 ], [ -178.2375, 71.5083 ], [ -178.2297, 71.5057 ], [ -178.2292, 71.5042 ], [ -178.2297, 71.5026 ], [ -178.2333, 71.5021 ], [ -178.2437, 71.5062 ], [ -178.2667, 71.5062 ], [ -178.2682, 71.5068 ], [ -178.2672, 71.5099 ], [ -178.275, 71.5125 ], [ -178.2854, 71.5125 ], [ -178.2917, 71.5104 ], [ -178.2958, 71.5125 ], [ -178.3104, 71.5125 ], [ -178.3146, 71.5146 ], [ -178.3313, 71.5146 ], [ -178.3328, 71.513 ], [ -178.3229, 71.5125 ], [ -178.3089, 71.5078 ], [ -178.3141, 71.5057 ], [ -178.3089, 71.5026 ], [ -178.3141, 71.5005 ], [ -178.2901, 71.4974 ], [ -178.2953, 71.4953 ], [ -178.2953, 71.4922 ], [ -178.2875, 71.4896 ], [ -178.2667, 71.4875 ], [ -178.2625, 71.4833 ], [ -178.2417, 71.4833 ], [ -178.2375, 71.4812 ], [ -178.2292, 71.4812 ], [ -178.2214, 71.4786 ], [ -178.2266, 71.4766 ], [ -178.2276, 71.4734 ], [ -178.2328, 71.4724 ], [ -178.2276, 71.4693 ], [ -178.2349, 71.4682 ], [ -178.2359, 71.4651 ], [ -178.2536, 71.4641 ], [ -178.2417, 71.4583 ], [ -178.2312, 71.4583 ], [ -178.2234, 71.4609 ], [ -178.2208, 71.4646 ], [ -178.2104, 71.4646 ], [ -178.2063, 71.4625 ], [ -178.1917, 71.4625 ], [ -178.1875, 71.4604 ], [ -178.1812, 71.4625 ], [ -178.175, 71.4604 ], [ -178.1521, 71.4604 ], [ -178.1479, 71.4583 ], [ -178.1396, 71.4583 ], [ -178.1354, 71.4563 ], [ -178.1255, 71.4589 ], [ -178.1234, 71.4661 ], [ -178.1349, 71.4693 ], [ -178.1359, 71.4724 ], [ -178.1411, 71.4734 ], [ -178.1396, 71.475 ], [ -178.1313, 71.475 ], [ -178.1271, 71.4729 ], [ -178.1208, 71.475 ], [ -178.1172, 71.4745 ], [ -178.1161, 71.4714 ], [ -178.1109, 71.4703 ], [ -178.1099, 71.4651 ], [ -178.1083, 71.4646 ], [ -178.0984, 71.4651 ], [ -178.0953, 71.4703 ], [ -178.0917, 71.4708 ], [ -178.0688, 71.4625 ], [ -178.0604, 71.4625 ], [ -178.0578, 71.4589 ], [ -178.0479, 71.4583 ], [ -178.0464, 71.4568 ], [ -178.0708, 71.4563 ], [ -178.075, 71.4542 ], [ -178.0854, 71.4542 ], [ -178.0932, 71.4505 ], [ -178.0854, 71.4479 ], [ -178.0625, 71.45 ], [ -178.0547, 71.4474 ], [ -178.0521, 71.4437 ], [ -178.0437, 71.4437 ], [ -178.0422, 71.4443 ], [ -178.0412, 71.4495 ], [ -178.0312, 71.45 ], [ -178.0271, 71.4458 ], [ -178.0088, 71.4453 ], [ -178.0099, 71.4422 ], [ -178.0, 71.4417 ], [ -177.9974, 71.438 ], [ -177.9922, 71.437 ], [ -178.0, 71.4333 ], [ -178.0063, 71.4354 ], [ -178.0146, 71.4354 ], [ -178.0182, 71.4328 ], [ -178.0068, 71.4287 ], [ -178.0068, 71.4255 ], [ -178.0083, 71.425 ], [ -178.0437, 71.425 ], [ -178.05, 71.4271 ], [ -178.0578, 71.4245 ], [ -178.0526, 71.4224 ], [ -178.0516, 71.4193 ], [ -178.0375, 71.4146 ], [ -178.0167, 71.4146 ], [ -178.0026, 71.4099 ], [ -178.0026, 71.4068 ], [ -178.0146, 71.4063 ], [ -178.0172, 71.4026 ], [ -178.025, 71.4 ], [ -178.0354, 71.4021 ], [ -178.0432, 71.3995 ], [ -178.0432, 71.3964 ], [ -178.0318, 71.3953 ], [ -178.0292, 71.3917 ], [ -178.0193, 71.3912 ], [ -178.0245, 71.388 ], [ -178.0146, 71.3854 ], [ -178.0005, 71.3901 ], [ -177.9974, 71.3953 ], [ -177.9896, 71.3979 ], [ -177.9812, 71.3979 ], [ -177.9646, 71.3917 ], [ -177.95, 71.3917 ], [ -177.9458, 71.3896 ], [ -177.9229, 71.3896 ], [ -177.9203, 71.3859 ], [ -177.9151, 71.3849 ], [ -177.9162, 71.3818 ], [ -177.9026, 71.3807 ], [ -177.9036, 71.3776 ], [ -177.8984, 71.3766 ], [ -177.8974, 71.3734 ], [ -177.8922, 71.3724 ], [ -177.8911, 71.3693 ], [ -177.8672, 71.362 ], [ -177.8682, 71.3588 ], [ -177.8495, 71.3526 ], [ -177.8396, 71.3521 ], [ -177.8318, 71.3495 ], [ -177.8307, 71.3464 ], [ -177.8193, 71.3432 ], [ -177.8182, 71.3401 ], [ -177.813, 71.3391 ], [ -177.8146, 71.3375 ], [ -177.8203, 71.337 ], [ -177.8203, 71.3339 ], [ -177.8068, 71.3349 ], [ -177.8026, 71.3328 ], [ -177.8016, 71.3297 ], [ -177.7839, 71.3245 ], [ -177.7891, 71.3214 ], [ -177.7776, 71.3203 ], [ -177.7766, 71.3172 ], [ -177.7713, 71.3161 ], [ -177.7724, 71.313 ], [ -177.7333, 71.3 ], [ -177.7234, 71.2995 ], [ -177.7224, 71.2943 ], [ -177.7109, 71.2932 ], [ -177.712, 71.2901 ], [ -177.7, 71.2896 ], [ -177.6917, 71.2854 ], [ -177.6812, 71.2875 ], [ -177.6797, 71.287 ], [ -177.6807, 71.2839 ], [ -177.6693, 71.2807 ], [ -177.6682, 71.2776 ], [ -177.6354, 71.2667 ], [ -177.6255, 71.2662 ], [ -177.6229, 71.2625 ], [ -177.5958, 71.2625 ], [ -177.5917, 71.2604 ], [ -177.5833, 71.2604 ], [ -177.5646, 71.2667 ], [ -177.5583, 71.2646 ], [ -177.55, 71.2646 ], [ -177.5458, 71.2667 ], [ -177.5396, 71.2646 ], [ -177.5333, 71.2667 ], [ -177.5104, 71.2667 ], [ -177.5088, 71.2651 ], [ -177.5161, 71.2641 ], [ -177.5109, 71.262 ], [ -177.5109, 71.2589 ], [ -177.5224, 71.2557 ], [ -177.5255, 71.2526 ], [ -177.5307, 71.2516 ], [ -177.5068, 71.2432 ], [ -177.5083, 71.2417 ], [ -177.5188, 71.2417 ], [ -177.5203, 71.2401 ], [ -177.5125, 71.2375 ], [ -177.4708, 71.2375 ], [ -177.4646, 71.2396 ], [ -177.4542, 71.2354 ], [ -177.4443, 71.2349 ], [ -177.4432, 71.2318 ], [ -177.438, 71.2297 ], [ -177.4453, 71.2255 ], [ -177.4339, 71.2224 ], [ -177.4333, 71.2208 ], [ -177.4354, 71.2188 ], [ -177.4396, 71.2188 ], [ -177.4563, 71.2125 ], [ -177.462, 71.212 ], [ -177.4568, 71.2089 ], [ -177.462, 71.2078 ], [ -177.4651, 71.2005 ], [ -177.475, 71.2 ], [ -177.4792, 71.2021 ], [ -177.4828, 71.2016 ], [ -177.4828, 71.1964 ], [ -177.4797, 71.1943 ], [ -177.4833, 71.1937 ], [ -177.4875, 71.1958 ], [ -177.4974, 71.1953 ], [ -177.4896, 71.1917 ], [ -177.4734, 71.1911 ], [ -177.4729, 71.1875 ], [ -177.475, 71.1854 ], [ -177.4812, 71.1875 ], [ -177.4896, 71.1875 ], [ -177.4937, 71.1896 ], [ -177.5042, 71.1875 ], [ -177.5057, 71.188 ], [ -177.5047, 71.1911 ], [ -177.5099, 71.1922 ], [ -177.5109, 71.1953 ], [ -177.5188, 71.1979 ], [ -177.525, 71.1958 ], [ -177.5312, 71.1979 ], [ -177.5542, 71.1958 ], [ -177.5557, 71.1943 ], [ -177.5505, 71.1922 ], [ -177.5604, 71.1917 ], [ -177.5646, 71.1896 ], [ -177.5771, 71.1937 ], [ -177.5937, 71.1937 ], [ -177.5979, 71.1917 ], [ -177.6062, 71.1917 ], [ -177.6104, 71.1937 ], [ -177.6328, 71.1932 ], [ -177.625, 71.1896 ], [ -177.6151, 71.1891 ], [ -177.6172, 71.1859 ], [ -177.6224, 71.1849 ], [ -177.6208, 71.1833 ], [ -177.6125, 71.1833 ], [ -177.6083, 71.1813 ], [ -177.6, 71.1813 ], [ -177.5937, 71.1833 ], [ -177.5859, 71.1807 ], [ -177.5859, 71.1776 ], [ -177.5911, 71.1766 ], [ -177.5896, 71.175 ], [ -177.5688, 71.1771 ], [ -177.5646, 71.175 ], [ -177.5437, 71.175 ], [ -177.5359, 71.1724 ], [ -177.5437, 71.1687 ], [ -177.5521, 71.1708 ], [ -177.5625, 71.1667 ], [ -177.5745, 71.1661 ], [ -177.5729, 71.1646 ], [ -177.5646, 71.1646 ], [ -177.5604, 71.1625 ], [ -177.5396, 71.1625 ], [ -177.538, 71.1609 ], [ -177.5495, 71.1578 ], [ -177.5516, 71.1557 ], [ -177.5401, 71.1516 ], [ -177.5396, 71.1479 ], [ -177.5401, 71.1464 ], [ -177.5453, 71.1453 ], [ -177.5422, 71.1432 ], [ -177.5422, 71.1401 ], [ -177.5474, 71.1391 ], [ -177.5484, 71.1359 ], [ -177.5557, 71.1349 ], [ -177.5568, 71.1318 ], [ -177.562, 71.1307 ], [ -177.5641, 71.1255 ], [ -177.5589, 71.1234 ], [ -177.5703, 71.1224 ], [ -177.5651, 71.1203 ], [ -177.5667, 71.1188 ], [ -177.5849, 71.1182 ], [ -177.5849, 71.1151 ], [ -177.5797, 71.1141 ], [ -177.5792, 71.1104 ], [ -177.5797, 71.1088 ], [ -177.5854, 71.1083 ], [ -177.5875, 71.1062 ], [ -177.587, 71.1047 ], [ -177.5818, 71.1026 ], [ -177.5896, 71.1 ], [ -177.6125, 71.1 ], [ -177.6229, 71.0958 ], [ -177.6333, 71.0958 ], [ -177.6359, 71.0922 ], [ -177.6458, 71.0917 ], [ -177.6484, 71.088 ], [ -177.6682, 71.0828 ], [ -177.6714, 71.0776 ], [ -177.6812, 71.0771 ], [ -177.7, 71.0708 ], [ -177.7146, 71.075 ], [ -177.7167, 71.0729 ], [ -177.7172, 71.0651 ], [ -177.7187, 71.0646 ], [ -177.7292, 71.0646 ], [ -177.7333, 71.0625 ], [ -177.7479, 71.0625 ], [ -177.7521, 71.0604 ], [ -177.7583, 71.0625 ], [ -177.7667, 71.0625 ], [ -177.7708, 71.0604 ], [ -177.7937, 71.0583 ], [ -177.8016, 71.0557 ], [ -177.8062, 71.0521 ], [ -177.8146, 71.0521 ], [ -177.825, 71.0479 ], [ -177.8307, 71.0474 ], [ -177.8333, 71.0437 ], [ -177.8417, 71.0437 ], [ -177.8875, 71.0292 ], [ -177.9021, 71.0292 ], [ -177.9063, 71.025 ], [ -177.9167, 71.025 ], [ -177.9208, 71.0208 ], [ -177.9292, 71.0229 ], [ -177.9333, 71.0208 ], [ -177.9563, 71.0208 ], [ -177.9604, 71.0188 ], [ -177.975, 71.0188 ], [ -177.9792, 71.0167 ], [ -178.0208, 71.0167 ], [ -178.025, 71.0146 ], [ -178.0521, 71.0146 ], [ -178.0562, 71.0125 ], [ -178.0646, 71.0146 ], [ -178.0688, 71.0125 ], [ -178.0792, 71.0125 ], [ -178.0833, 71.0104 ], [ -178.0917, 71.0104 ], [ -178.0958, 71.0083 ], [ -178.1375, 71.0083 ], [ -178.1417, 71.0104 ], [ -178.1625, 71.0104 ], [ -178.1667, 71.0083 ], [ -178.175, 71.0083 ], [ -178.1938, 71.0021 ], [ -178.2021, 71.0021 ], [ -178.2063, 71.0 ], [ -178.2208, 71.0 ], [ -178.225, 70.9979 ], [ -178.2417, 70.9979 ], [ -178.2521, 70.9938 ], [ -178.275, 70.9917 ], [ -178.2792, 70.9896 ], [ -178.2875, 70.9896 ], [ -178.2917, 70.9875 ], [ -178.2979, 70.9896 ], [ -178.3125, 70.9896 ], [ -178.3167, 70.9875 ], [ -178.4021, 70.9875 ], [ -178.4062, 70.9854 ], [ -178.4417, 70.9833 ], [ -178.4458, 70.9812 ], [ -178.4563, 70.9812 ], [ -178.4604, 70.9792 ], [ -178.4812, 70.9792 ], [ -178.4854, 70.9771 ], [ -178.4937, 70.9771 ], [ -178.4979, 70.975 ], [ -178.5208, 70.975 ], [ -178.525, 70.9708 ], [ -178.5412, 70.9703 ], [ -178.5437, 70.9667 ], [ -178.5854, 70.9667 ], [ -178.5896, 70.9646 ], [ -178.6125, 70.9646 ], [ -178.6167, 70.9625 ], [ -178.6562, 70.9625 ], [ -178.6667, 70.9583 ], [ -178.6729, 70.9604 ], [ -178.6792, 70.9583 ], [ -178.6833, 70.9583 ], [ -178.6875, 70.9604 ], [ -178.7146, 70.9604 ], [ -178.7187, 70.9583 ], [ -178.7333, 70.9583 ], [ -178.7375, 70.9563 ], [ -178.7479, 70.9563 ], [ -178.7583, 70.9521 ], [ -178.7646, 70.9542 ], [ -178.7729, 70.9542 ], [ -178.7833, 70.95 ], [ -178.7979, 70.95 ], [ -178.8021, 70.9479 ], [ -178.8125, 70.9479 ], [ -178.8354, 70.9396 ], [ -178.8521, 70.9396 ], [ -178.8562, 70.9375 ], [ -178.8771, 70.9375 ], [ -178.8813, 70.9354 ], [ -178.9167, 70.9354 ], [ -178.9208, 70.9333 ], [ -178.9292, 70.9333 ], [ -178.9396, 70.9292 ], [ -178.9625, 70.9292 ], [ -178.9729, 70.925 ], [ -178.9812, 70.925 ], [ -178.9854, 70.9229 ], [ -178.9937, 70.925 ], [ -178.9979, 70.9208 ], [ -179.0146, 70.9208 ], [ -179.0312, 70.9146 ], [ -179.0667, 70.9146 ], [ -179.0693, 70.9109 ], [ -179.0807, 70.9078 ], [ -179.0833, 70.9042 ], [ -179.1062, 70.9042 ], [ -179.1229, 70.8979 ], [ -179.1313, 70.8979 ], [ -179.1375, 70.9 ], [ -179.1417, 70.8958 ], [ -179.1646, 70.8958 ], [ -179.1687, 70.8938 ], [ -179.1833, 70.8938 ], [ -179.1938, 70.8979 ], [ -179.2036, 70.8984 ], [ -179.2026, 70.9036 ], [ -179.2042, 70.9042 ], [ -179.2458, 70.9042 ], [ -179.25, 70.9 ], [ -179.2646, 70.9 ], [ -179.2688, 70.9021 ], [ -179.2771, 70.9021 ], [ -179.2812, 70.9 ], [ -179.3167, 70.9 ], [ -179.3208, 70.8979 ], [ -179.3292, 70.9 ], [ -179.3333, 70.8979 ], [ -179.3542, 70.8979 ], [ -179.3583, 70.8958 ], [ -179.3646, 70.8979 ], [ -179.3917, 70.8979 ], [ -179.3958, 70.8958 ], [ -179.4, 70.8958 ], [ -179.4016, 70.8964 ], [ -179.4005, 70.8995 ], [ -179.4021, 70.9 ], [ -179.4083, 70.8979 ], [ -179.4125, 70.8979 ], [ -179.4167, 70.9 ], [ -179.4229, 70.8979 ], [ -179.4312, 70.8979 ], [ -179.4354, 70.9 ], [ -179.4438, 70.9 ], [ -179.45, 70.9021 ], [ -179.4578, 70.8984 ], [ -179.4479, 70.8979 ], [ -179.4463, 70.8964 ], [ -179.4542, 70.8938 ], [ -179.4641, 70.8932 ], [ -179.4667, 70.8896 ], [ -179.4833, 70.8896 ], [ -179.4849, 70.8901 ], [ -179.4839, 70.8932 ], [ -179.4979, 70.8979 ], [ -179.5063, 70.8979 ], [ -179.5088, 70.9016 ], [ -179.5229, 70.9063 ], [ -179.5312, 70.9063 ], [ -179.5391, 70.9089 ], [ -179.5396, 70.9104 ], [ -179.5375, 70.9125 ], [ -179.5292, 70.9125 ], [ -179.525, 70.9104 ], [ -179.5167, 70.9104 ], [ -179.5125, 70.9083 ], [ -179.4755, 70.9089 ], [ -179.4807, 70.9109 ], [ -179.4812, 70.9125 ], [ -179.4807, 70.9161 ], [ -179.4755, 70.9182 ], [ -179.487, 70.9213 ], [ -179.4818, 70.9234 ], [ -179.4812, 70.925 ], [ -179.4818, 70.9266 ], [ -179.4875, 70.9292 ], [ -179.4958, 70.9292 ], [ -179.5, 70.9313 ], [ -179.5083, 70.9313 ], [ -179.5125, 70.9333 ], [ -179.5167, 70.9333 ], [ -179.5271, 70.9292 ], [ -179.5542, 70.9292 ], [ -179.5583, 70.9271 ], [ -179.5729, 70.9271 ], [ -179.5771, 70.9292 ], [ -179.5917, 70.9292 ], [ -179.5958, 70.9313 ], [ -179.6042, 70.9313 ], [ -179.6083, 70.9333 ], [ -179.6229, 70.9333 ], [ -179.6271, 70.9375 ], [ -179.6354, 70.9375 ], [ -179.6646, 70.9479 ], [ -179.6729, 70.9479 ], [ -179.6771, 70.95 ], [ -179.6917, 70.95 ], [ -179.6958, 70.9521 ], [ -179.725, 70.9521 ], [ -179.7292, 70.9542 ], [ -179.7437, 70.9542 ], [ -179.7479, 70.9563 ], [ -179.7688, 70.9563 ], [ -179.7729, 70.9583 ], [ -179.7875, 70.9583 ], [ -179.7917, 70.9604 ], [ -179.8, 70.9604 ], [ -179.8042, 70.9625 ], [ -179.8125, 70.9625 ], [ -179.8167, 70.9646 ], [ -179.8313, 70.9646 ], [ -179.8354, 70.9667 ], [ -179.8437, 70.9667 ], [ -179.8479, 70.9688 ], [ -179.8687, 70.9708 ], [ -179.8729, 70.9729 ], [ -179.8938, 70.975 ], [ -179.8979, 70.9771 ], [ -179.9187, 70.9771 ], [ -179.9229, 70.9792 ], [ -179.9292, 70.9771 ], [ -179.9396, 70.9771 ], [ -179.9438, 70.9792 ], [ -179.95, 70.9771 ], [ -179.9771, 70.9771 ], [ -179.9812, 70.9792 ], [ -179.9979, 70.9792 ], [ -180.0, 71.1167 ], [ -180.0, 71.3917 ], [ -179.9979, 71.5292 ], [ -179.9917, 71.5271 ], [ -179.9833, 71.5271 ], [ -179.9792, 71.525 ], [ -179.9167, 71.525 ], [ -179.9125, 71.5229 ], [ -179.9026, 71.5224 ], [ -179.9016, 71.5193 ], [ -179.8938, 71.5167 ], [ -179.8838, 71.5172 ], [ -179.8813, 71.5208 ], [ -179.8646, 71.5208 ], [ -179.8604, 71.5229 ], [ -179.8458, 71.5229 ], [ -179.8417, 71.5208 ], [ -179.8188, 71.5208 ], [ -179.8146, 71.5188 ], [ -179.7937, 71.5188 ], [ -179.7922, 71.5203 ], [ -179.8083, 71.5208 ], [ -179.8125, 71.5229 ], [ -179.8224, 71.5234 ], [ -179.8234, 71.5286 ], [ -179.8313, 71.5313 ], [ -179.8396, 71.5313 ], [ -179.862, 71.538 ], [ -179.8604, 71.5396 ], [ -179.8547, 71.5401 ], [ -179.862, 71.5432 ], [ -179.8583, 71.5458 ], [ -179.8484, 71.5463 ], [ -179.8458, 71.55 ], [ -179.8396, 71.5479 ], [ -179.8229, 71.5479 ], [ -179.8213, 71.5474 ], [ -179.8224, 71.5443 ], [ -179.8208, 71.5437 ], [ -179.8167, 71.5437 ], [ -179.8125, 71.5458 ], [ -179.8062, 71.5437 ], [ -179.7583, 71.5437 ], [ -179.7505, 71.5463 ], [ -179.7495, 71.5516 ], [ -179.7359, 71.5526 ], [ -179.7411, 71.5557 ], [ -179.7359, 71.5568 ], [ -179.7349, 71.5599 ], [ -179.7297, 71.5609 ], [ -179.7292, 71.5625 ], [ -179.7297, 71.5641 ], [ -179.7349, 71.5661 ], [ -179.725, 71.5687 ], [ -179.7167, 71.5687 ], [ -179.7125, 71.5708 ], [ -179.6979, 71.5708 ], [ -179.6963, 71.5703 ], [ -179.6963, 71.5672 ], [ -179.7016, 71.5661 ], [ -179.7026, 71.5589 ], [ -179.7078, 71.5568 ], [ -179.7026, 71.5557 ], [ -179.7016, 71.5526 ], [ -179.6963, 71.5516 ], [ -179.6953, 71.5484 ], [ -179.6901, 71.5474 ], [ -179.6875, 71.5437 ], [ -179.6604, 71.5437 ], [ -179.6562, 71.5458 ], [ -179.6458, 71.5458 ], [ -179.6443, 71.5474 ], [ -179.6495, 71.5484 ], [ -179.6495, 71.5537 ], [ -179.6417, 71.5563 ], [ -179.625, 71.5563 ], [ -179.6208, 71.5583 ], [ -179.6125, 71.5563 ], [ -179.6099, 71.5599 ], [ -179.5854, 71.5667 ], [ -179.5776, 71.5641 ], [ -179.5766, 71.5609 ], [ -179.5667, 71.5604 ], [ -179.5589, 71.5578 ], [ -179.5578, 71.5547 ], [ -179.5437, 71.55 ], [ -179.5396, 71.55 ], [ -179.5354, 71.5521 ], [ -179.5146, 71.5521 ], [ -179.5083, 71.5542 ], [ -179.5057, 71.5505 ], [ -179.5005, 71.5495 ], [ -179.5005, 71.5463 ], [ -179.5057, 71.5453 ], [ -179.5042, 71.5437 ], [ -179.4958, 71.5437 ], [ -179.4917, 71.5458 ], [ -179.4688, 71.5437 ], [ -179.4661, 71.5474 ], [ -179.4479, 71.5479 ], [ -179.4401, 71.5505 ], [ -179.4375, 71.5542 ], [ -179.4021, 71.5521 ], [ -179.3995, 71.5484 ], [ -179.3896, 71.5479 ], [ -179.3854, 71.5458 ], [ -179.3687, 71.5458 ], [ -179.3583, 71.55 ], [ -179.35, 71.55 ], [ -179.3474, 71.5463 ], [ -179.3359, 71.5453 ], [ -179.3349, 71.5401 ], [ -179.325, 71.5396 ], [ -179.3224, 71.5359 ], [ -179.3047, 71.5307 ], [ -179.3099, 71.5286 ], [ -179.3099, 71.5255 ], [ -179.2937, 71.525 ], [ -179.2875, 71.5292 ], [ -179.2667, 71.5313 ], [ -179.2641, 71.5349 ], [ -179.2583, 71.5354 ], [ -179.2479, 71.5396 ], [ -179.2333, 71.5396 ], [ -179.2292, 71.5354 ], [ -179.2187, 71.5354 ], [ -179.2146, 71.5375 ], [ -179.2063, 71.5375 ], [ -179.2, 71.5354 ], [ -179.1687, 71.5458 ], [ -179.1583, 71.5458 ], [ -179.1521, 71.5437 ], [ -179.1505, 71.5443 ], [ -179.1495, 71.5495 ], [ -179.1443, 71.5505 ], [ -179.1432, 71.5537 ], [ -179.1359, 71.5547 ], [ -179.1349, 71.5578 ], [ -179.1297, 71.5589 ], [ -179.1286, 71.5641 ], [ -179.1208, 71.5667 ], [ -179.0854, 71.5646 ], [ -179.0688, 71.5583 ], [ -179.0583, 71.5583 ], [ -179.0542, 71.5604 ], [ -179.0443, 71.5599 ], [ -179.0417, 71.5563 ], [ -179.0354, 71.5583 ], [ -179.0188, 71.5583 ], [ -179.0167, 71.5604 ], [ -179.0188, 71.5625 ], [ -179.0354, 71.5625 ], [ -179.037, 71.5641 ], [ -179.0333, 71.5667 ], [ -179.0125, 71.5667 ], [ -179.0083, 71.5687 ], [ -178.9979, 71.5687 ], [ -178.9964, 71.5693 ], [ -178.9964, 71.5724 ], [ -179.0104, 71.5771 ], [ -179.0188, 71.5771 ], [ -179.0312, 71.5813 ], [ -179.0396, 71.5813 ], [ -179.0474, 71.5849 ], [ -179.0354, 71.5854 ], [ -179.0312, 71.5833 ], [ -179.0125, 71.5833 ] ] ], [ [ [ -179.0729, 71.5625 ], [ -179.0766, 71.5641 ], [ -179.0693, 71.5641 ], [ -179.0693, 71.563 ], [ -179.0729, 71.5625 ] ] ], [ [ [ -178.8042, 71.5771 ], [ -178.8, 71.5771 ], [ -178.7984, 71.5755 ], [ -178.8182, 71.5755 ], [ -178.8167, 71.5771 ], [ -178.8042, 71.5771 ] ] ], [ [ [ -177.4833, 71.1563 ], [ -177.4917, 71.1563 ], [ -177.4979, 71.1542 ], [ -177.5021, 71.1563 ], [ -177.5104, 71.1563 ], [ -177.512, 71.1578 ], [ -177.5047, 71.1589 ], [ -177.5021, 71.1625 ], [ -177.4958, 71.1604 ], [ -177.4875, 71.1625 ], [ -177.4849, 71.1589 ], [ -177.4797, 71.1578 ], [ -177.4833, 71.1563 ] ] ], [ [ [ -177.513, 71.1589 ], [ -177.5167, 71.1583 ], [ -177.5271, 71.1625 ], [ -177.5333, 71.1625 ], [ -177.5349, 71.1641 ], [ -177.525, 71.1646 ], [ -177.5172, 71.162 ], [ -177.513, 71.1589 ] ] ], [ [ [ -177.4797, 71.163 ], [ -177.4833, 71.1625 ], [ -177.4849, 71.1641 ], [ -177.4812, 71.1646 ], [ -177.4797, 71.163 ] ] ], [ [ [ -177.5099, 71.1641 ], [ -177.5146, 71.1625 ], [ -177.5172, 71.1661 ], [ -177.5271, 71.1667 ], [ -177.5286, 71.1682 ], [ -177.5104, 71.1708 ], [ -177.5088, 71.1703 ], [ -177.5099, 71.1672 ], [ -177.5047, 71.1661 ], [ -177.5099, 71.1641 ] ] ], [ [ [ -177.4766, 71.1734 ], [ -177.4714, 71.1724 ], [ -177.4766, 71.1703 ], [ -177.4714, 71.1672 ], [ -177.5036, 71.1672 ], [ -177.5021, 71.1687 ], [ -177.4964, 71.1693 ], [ -177.4958, 71.1708 ], [ -177.4964, 71.1724 ], [ -177.5016, 71.1734 ], [ -177.5016, 71.1766 ], [ -177.4979, 71.1771 ], [ -177.4917, 71.175 ], [ -177.4901, 71.1755 ], [ -177.4932, 71.1776 ], [ -177.4917, 71.1792 ], [ -177.4818, 71.1766 ], [ -177.4766, 71.1734 ] ] ], [ [ [ -177.5271, 71.1771 ], [ -177.5437, 71.1771 ], [ -177.5458, 71.1792 ], [ -177.5437, 71.1813 ], [ -177.5292, 71.1813 ], [ -177.5213, 71.1787 ], [ -177.5229, 71.1771 ], [ -177.5271, 71.1771 ] ] ], [ [ [ -177.4755, 71.1797 ], [ -177.4792, 71.1792 ], [ -177.4807, 71.1807 ], [ -177.4771, 71.1813 ], [ -177.4755, 71.1797 ] ] ], [ [ [ -177.4818, 71.1818 ], [ -177.4854, 71.1813 ], [ -177.487, 71.1828 ], [ -177.4833, 71.1833 ], [ -177.4818, 71.1818 ] ] ], [ [ [ -177.5104, 71.1875 ], [ -177.5042, 71.1854 ], [ -177.4958, 71.1854 ], [ -177.4937, 71.1833 ], [ -177.4943, 71.1818 ], [ -177.4995, 71.1807 ], [ -177.5042, 71.1771 ], [ -177.5125, 71.1771 ], [ -177.5141, 71.1776 ], [ -177.5141, 71.1807 ], [ -177.5083, 71.1813 ], [ -177.5068, 71.1828 ], [ -177.5292, 71.1833 ], [ -177.5312, 71.1854 ], [ -177.5307, 71.1891 ], [ -177.5292, 71.1896 ], [ -177.5229, 71.1875 ], [ -177.5104, 71.1875 ] ] ], [ [ [ -177.5542, 71.1854 ], [ -177.5578, 71.187 ], [ -177.538, 71.187 ], [ -177.5479, 71.1833 ], [ -177.5542, 71.1854 ] ] ], [ [ [ -177.5854, 71.1875 ], [ -177.5891, 71.1891 ], [ -177.5714, 71.1891 ], [ -177.5792, 71.1854 ], [ -177.5854, 71.1875 ] ] ], [ [ [ -177.538, 71.1901 ], [ -177.5417, 71.1896 ], [ -177.5432, 71.1911 ], [ -177.5396, 71.1917 ], [ -177.538, 71.1901 ] ] ], [ [ [ -177.4588, 71.1974 ], [ -177.4604, 71.1958 ], [ -177.4641, 71.1964 ], [ -177.4625, 71.1979 ], [ -177.4588, 71.1974 ] ] ], [ [ [ -177.4359, 71.2161 ], [ -177.4375, 71.2146 ], [ -177.4411, 71.2151 ], [ -177.4396, 71.2167 ], [ -177.4359, 71.2161 ] ] ], [ [ [ -179.9984, 69.1203 ], [ -179.9984, 69.1193 ], [ -180.0, 69.1193 ], [ -180.0, 69.1203 ], [ -179.9984, 69.1203 ] ] ], [ [ [ -180.0, 69.124 ], [ -180.0, 69.126 ], [ -179.9984, 69.1266 ], [ -179.9984, 69.1234 ], [ -180.0, 69.124 ] ] ], [ [ [ -179.2359, 71.5432 ], [ -179.2375, 71.5417 ], [ -179.2432, 71.5422 ], [ -179.2417, 71.5437 ], [ -179.2359, 71.5432 ] ] ], [ [ [ -179.4984, 71.5547 ], [ -179.5021, 71.5542 ], [ -179.5036, 71.5557 ], [ -179.5, 71.5563 ], [ -179.4984, 71.5547 ] ] ], [ [ [ -179.5047, 71.5578 ], [ -179.5063, 71.5563 ], [ -179.512, 71.5568 ], [ -179.5104, 71.5583 ], [ -179.5047, 71.5578 ] ] ], [ [ [ -179.4958, 71.5604 ], [ -179.4922, 71.5589 ], [ -179.5036, 71.5589 ], [ -179.5021, 71.5604 ], [ -179.4958, 71.5604 ] ] ], [ [ [ -179.5838, 71.5693 ], [ -179.5875, 71.5687 ], [ -179.5891, 71.5703 ], [ -179.5854, 71.5708 ], [ -179.5838, 71.5693 ] ] ], [ [ [ -179.5901, 71.5714 ], [ -179.5937, 71.5708 ], [ -179.5953, 71.5724 ], [ -179.5917, 71.5729 ], [ -179.5901, 71.5714 ] ] ], [ [ [ -179.3021, 71.5917 ], [ -179.3083, 71.5917 ], [ -179.3146, 71.5896 ], [ -179.3188, 71.5917 ], [ -179.3292, 71.5917 ], [ -179.3307, 71.5932 ], [ -179.3271, 71.5958 ], [ -179.3062, 71.5958 ], [ -179.2984, 71.5932 ], [ -179.3021, 71.5917 ] ] ], [ [ [ -179.2187, 71.5938 ], [ -179.2063, 71.5938 ], [ -179.2047, 71.5922 ], [ -179.2167, 71.5917 ], [ -179.2208, 71.5896 ], [ -179.2563, 71.5896 ], [ -179.2604, 71.5917 ], [ -179.2688, 71.5917 ], [ -179.2729, 71.5938 ], [ -179.2958, 71.5938 ], [ -179.2974, 71.5953 ], [ -179.2458, 71.5958 ], [ -179.2396, 71.5938 ], [ -179.2333, 71.5958 ], [ -179.2292, 71.5938 ], [ -179.2187, 71.5938 ] ] ], [ [ [ -178.8255, 71.5766 ], [ -178.8271, 71.575 ], [ -178.8307, 71.5755 ], [ -178.8292, 71.5771 ], [ -178.8255, 71.5766 ] ] ], [ [ [ -179.0917, 76.1667 ], [ -179.088, 76.1651 ], [ -179.0974, 76.1651 ], [ -179.0958, 76.1667 ], [ -179.0917, 76.1667 ] ] ] ] } diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index a446fa91..cb86f3a8 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -10,10 +10,27 @@ from ckanext.spatial.tests.base import SpatialTestBase extents = { - "nz": '{"type":"Polygon","coordinates":[[[174,-38],[176,-38],[176,-40],[174,-40],[174,-38]]]}', - "ohio": '{"type": "Polygon","coordinates": [[[-84,38],[-84,40],[-80,42],[-80,38],[-84,38]]]}', - "dateline": '{"type":"Polygon","coordinates":[[[169,70],[169,60],[192,60],[192,70],[169,70]]]}', - "dateline2": '{"type":"Polygon","coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}', + "nz": """ + {"type":"Polygon", + "coordinates":[[[174,-38],[176,-38],[176,-40],[174,-40],[174,-38]]]}""", + "ohio": """ + {"type": "Polygon", + "coordinates": [[[-84,38],[-84,40],[-80,42],[-80,38],[-84,38]]]}""", + "antimeridian_bbox": """ + {"type":"Polygon", + "coordinates":[[[169,70],[169,60],[192,60],[192,70],[169,70]]]}""", + "antimeridian_bbox_2": """ + {"type":"Polygon", + "coordinates":[[[170,60],[-170,60],[-170,70],[170,70],[170,60]]]}""", + "antimeridian_polygon": """ + { "type": "MultiPolygon", + "coordinates": [ [ [ + [ 181.2, 61.7 ], [ 178.1, 61.4 ], + [ 176.9, 59.2 ], [ 178.4, 55.2 ], + [ 188.6, 54.9 ], [ 188.8, 60.2 ], + [ 181.2, 61.7 ] ] ] ] + } + """ } @@ -175,10 +192,10 @@ def test_spatial_query_ohio_wrap(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_1(self): + def test_spatial_query_antimeridian_1(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox"]}] ) result = helpers.call_action( @@ -188,10 +205,10 @@ def test_spatial_query_dateline_1(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_2(self): + def test_spatial_query_antimeridian_2(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox"]}] ) result = helpers.call_action( @@ -201,10 +218,10 @@ def test_spatial_query_dateline_2(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_3(self): + def test_spatial_query_antimeridian_3(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline2"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox_2"]}] ) result = helpers.call_action( @@ -214,10 +231,10 @@ def test_spatial_query_dateline_3(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_4(self): + def test_spatial_query_antimeridian_4(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline2"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox_2"]}] ) result = helpers.call_action( @@ -413,10 +430,10 @@ def test_spatial_query_ohio_wrap(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_1(self): + def test_spatial_query_antimeridian_1(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox"]}] ) result = helpers.call_action( @@ -426,10 +443,10 @@ def test_spatial_query_dateline_1(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_2(self): + def test_spatial_query_antimeridian_2(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox"]}] ) result = helpers.call_action( @@ -439,10 +456,10 @@ def test_spatial_query_dateline_2(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_3(self): + def test_spatial_query_antimeridian_3(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline2"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox_2"]}] ) result = helpers.call_action( @@ -452,10 +469,10 @@ def test_spatial_query_dateline_3(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_spatial_query_dateline_4(self): + def test_spatial_query_antimeridian_4(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": extents["dateline2"]}] + extras=[{"key": "spatial", "value": extents["antimeridian_bbox_2"]}] ) result = helpers.call_action( @@ -497,3 +514,19 @@ def test_spatial_polygon_split_across_antimeridian_outside_bbox(self): ) assert result["count"] == 0 + + def test_spatial_polygon_across_antimeridian_not_indexed(self): + factories.Dataset( + extras=[ + { + "key": "spatial", + "value": extents["antimeridian_polygon"] + } + ] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "177.9,55,178.0,59"}, + ) + + assert result["count"] == 0 From a358afce5c09b0a104b25bb23aaaa2a9506333f4 Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 5 Sep 2022 15:40:32 +0200 Subject: [PATCH 33/66] Clean up and consolidate solr based searches --- ckanext/spatial/plugin/__init__.py | 63 +++++++++++++----------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 081ad597..9ede1c4d 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -264,7 +264,6 @@ def before_dataset_index(self, pkg_dict): log.error('Wrong geometry, not indexing') return pkg_dict if shape.bounds[0] < -180 or shape.bounds[2] > 180: - import ipdb; ipdb.set_trace() log.error(""" Geometries outside the -180, -90, 180, 90 boundaries are not supported, you need to split the geometry in order to fit the parts. Not indexing""") @@ -290,47 +289,36 @@ def before_dataset_search(self, search_params): search_backend = self._get_search_backend() - if search_params.get('extras', None) and search_params['extras'].get('ext_bbox', None): + input_bbox = search_params.get('extras', {}).get('ext_bbox', None) - bbox = normalize_bbox(search_params['extras']['ext_bbox']) + if input_bbox: + bbox = normalize_bbox(input_bbox) if not bbox: raise SearchError('Wrong bounding box provided') - if search_backend == 'solr': + if search_backend in ('solr', 'solr-spatial-field'): bbox = fit_bbox(bbox) if not search_params.get("fq_list"): search_params["fq_list"] = [] + spatial_field = "spatial_bbox" if search_backend == "solr" else "spatial_geom" + search_params["fq_list"].append( - "{{!field f=spatial_bbox}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))".format( - **bbox) + "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))".format( + spatial_field=spatial_field, **bbox) ) - elif search_backend == 'solr-spatial-field': - - bbox = fit_bbox(bbox) - - search_params = self._params_for_solr_spatial_field_search(bbox, search_params) elif search_backend == 'postgis': search_params = self._params_for_postgis_search(bbox, search_params) - return search_params - - def _params_for_solr_spatial_field_search(self, bbox, search_params): - ''' - This will add an fq filter with the form: - - +spatial_geom:"Intersects(ENVELOPE({minx}, {miny}, {maxx}, {maxy})) - - ''' - search_params['fq_list'] = search_params.get('fq_list', []) - search_params['fq_list'].append('+spatial_geom:"Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))"' - .format(minx=bbox['minx'], miny=bbox['miny'], maxx=bbox['maxx'], maxy=bbox['maxy'])) return search_params def _params_for_postgis_search(self, bbox, search_params): + """ + Note: The PostGIS search functionality will be removed in future versions + """ from ckanext.spatial.postgis.model import bbox_query, bbox_query_ordered from ckan.lib.search import SearchError @@ -389,20 +377,25 @@ def _params_for_postgis_search(self, bbox, search_params): return search_params def after_dataset_search(self, search_results, search_params): + """ + Note: The PostGIS search functionality will be removed in future versions + """ from ckan.lib.search import PackageSearchQuery - # Note: This will be deprecated at some point in favour of the - # Solr 4 spatial sorting capabilities - if search_params.get('extras', {}).get('ext_spatial') and \ - tk.asbool(config.get('ckanext.spatial.use_postgis_sorting', 'False')): - # Apply the spatial sort - querier = PackageSearchQuery() - pkgs = [] - for package_id, spatial_ranking in search_params['extras']['ext_spatial']: - # get package from SOLR - pkg = querier.get_index(package_id)['data_dict'] - pkgs.append(json.loads(pkg)) - search_results['results'] = pkgs + search_backend = self._get_search_backend() + if search_backend == "postgis": + # Note: This will be deprecated at some point in favour of the + # Solr 4 spatial sorting capabilities + if search_params.get('extras', {}).get('ext_spatial') and \ + tk.asbool(config.get('ckanext.spatial.use_postgis_sorting', 'False')): + # Apply the spatial sort + querier = PackageSearchQuery() + pkgs = [] + for package_id, spatial_ranking in search_params['extras']['ext_spatial']: + # get package from SOLR + pkg = querier.get_index(package_id)['data_dict'] + pkgs.append(json.loads(pkg)) + search_results['results'] = pkgs return search_results From 89070be10c23553667f72e9765a129c2352e7a6a Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 6 Sep 2022 10:31:51 +0200 Subject: [PATCH 34/66] Allow to configure the Solr spatial query sent --- ckanext/spatial/plugin/__init__.py | 11 +++- ckanext/spatial/tests/test_spatial_search.py | 67 ++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 9ede1c4d..411be57e 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -303,10 +303,17 @@ def before_dataset_search(self, search_params): if not search_params.get("fq_list"): search_params["fq_list"] = [] - spatial_field = "spatial_bbox" if search_backend == "solr" else "spatial_geom" + spatial_field = ( + "spatial_bbox" if search_backend == "solr" else "spatial_geom" + ) + + default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" + + spatial_query = config.get( + "ckanext.spatial.solr_query", default_spatial_query) search_params["fq_list"].append( - "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))".format( + spatial_query.format( spatial_field=spatial_field, **bbox) ) diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index cb86f3a8..3a796708 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -244,6 +244,39 @@ def test_spatial_query_antimeridian_4(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] + def test_custom_spatial_query(self, monkeypatch, ckan_config): + """ + ┌────────────────┐ xxxxxx + │ xxxxx│xx xxx + │ xxx │ xxx + │ xx │ xx + │ xxxx │ xxx + │ x x│xxxxx + │ xxx xxxx│ + │ xxxx │ + │ │ + └────────────────┘ + """ + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "175,-39.5,176.5,-39"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + monkeypatch.setitem( + ckan_config, + "ckanext.spatial.solr_query", + "{{!field f={spatial_field}}}Contains(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))") + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "175,-39.5,176.5,-39"} + ) + + assert result["count"] == 0 + @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") @pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr-spatial-field") @@ -530,3 +563,37 @@ def test_spatial_polygon_across_antimeridian_not_indexed(self): ) assert result["count"] == 0 + + def test_custom_spatial_query(self, monkeypatch, ckan_config): + """ + ┌────────────────┐ xxxxxx + │ xxxxx│xx xxx + │ xxx │ xxx + │ xx │ xx + │ xxxx │ xxx + │ x x│xxxxx + │ xxx xxxx│ + │ xxxx │ + │ │ + └────────────────┘ + """ + dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "175,-39.5,176.5,-39"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + monkeypatch.setitem( + ckan_config, + "ckanext.spatial.solr_query", + "{{!field f={spatial_field}}}Contains(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))") + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "175,-39.5,176.5,-39"} + ) + + assert result["count"] == 0 + From cf2b2a908958fd1ca0296bb02a5716bab29412c9 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 6 Sep 2022 11:25:40 +0200 Subject: [PATCH 35/66] Drop big fields from indexed dict before --- ckanext/spatial/plugin/__init__.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 411be57e..35c2b4e9 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -214,8 +214,17 @@ def before_dataset_index(self, pkg_dict): if not pkg_dict.get('extras_spatial'): return pkg_dict + # Coupled resources are URL -> uuid links, they are not needed in SOLR + # and might be huge if there are lot of coupled resources + pkg_dict.pop('coupled-resource', None) + pkg_dict.pop('extras_coupled-resource', None) + + # spatial field is geojson coordinate data, not needed in SOLR either + pkg_dict.pop('spatial', None) + geom_from_metadata = pkg_dict.pop('extras_spatial', None) + try: - geometry = json.loads(pkg_dict['extras_spatial']) + geometry = json.loads(geom_from_metadata) shape = shapely.geometry.shape(geometry) except (AttributeError, ValueError): @@ -272,16 +281,6 @@ def before_dataset_index(self, pkg_dict): pkg_dict['spatial_geom'] = wkt - # Coupled resources are URL -> uuid links, they are not needed in SOLR - # and might be huge if there are lot of coupled resources - if pkg_dict.get('coupled-resource'): - pkg_dict.pop('coupled-resource', None) - pkg_dict.pop('extras_coupled-resource', None) - - # spatial field is geojson coordinate data, not needed in SOLR either - if pkg_dict.get('spatial'): - pkg_dict.pop('spatial', None) - pkg_dict.pop('extras_spatial', None) return pkg_dict From f28b42a8aa598990d742cfc52d76e37a07bfa634 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 6 Sep 2022 11:58:09 +0200 Subject: [PATCH 36/66] Load plugins in test classes --- ckanext/spatial/plugin/__init__.py | 1 - ckanext/spatial/tests/test_spatial_search.py | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 35c2b4e9..66502218 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -281,7 +281,6 @@ def before_dataset_index(self, pkg_dict): pkg_dict['spatial_geom'] = wkt - return pkg_dict def before_dataset_search(self, search_params): diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index 3a796708..f47c5f21 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -34,7 +34,7 @@ } -@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") +@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") @pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr") class TestBBoxSearch(SpatialTestBase): def test_spatial_query(self): @@ -50,7 +50,6 @@ def test_spatial_query(self): assert result["results"][0]["id"] == dataset["id"] def test_spatial_query_outside_bbox(self): - factories.Dataset( extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] ) @@ -278,7 +277,7 @@ def test_custom_spatial_query(self, monkeypatch, ckan_config): assert result["count"] == 0 -@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup") +@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") @pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr-spatial-field") class TestSpatialFieldSearch(SpatialTestBase): def test_spatial_query_point(self): From efc244bff41d4e2ff818c89c1ba88e7778c83a19 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 6 Sep 2022 13:44:56 +0200 Subject: [PATCH 37/66] Update spatial search documentation --- ckanext/spatial/plugin/__init__.py | 4 +- doc/conf.py | 6 +- doc/install.rst | 7 +- doc/spatial-search.rst | 159 +++++++++++++++++------------ 4 files changed, 101 insertions(+), 75 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 66502218..4e5e36aa 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -220,8 +220,8 @@ def before_dataset_index(self, pkg_dict): pkg_dict.pop('extras_coupled-resource', None) # spatial field is geojson coordinate data, not needed in SOLR either - pkg_dict.pop('spatial', None) - geom_from_metadata = pkg_dict.pop('extras_spatial', None) + geom_from_metadata = pkg_dict.pop('spatial', None) + pkg_dict.pop('extras_spatial', None) try: geometry = json.loads(geom_from_metadata) diff --git a/doc/conf.py b/doc/conf.py index 142a13d9..7c04c2b2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +import os +import datetime + # # ckanext-spatial documentation build configuration file, created by # sphinx-quickstart on Wed Apr 10 17:17:12 2013. @@ -11,7 +14,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -41,7 +43,7 @@ # General information about the project. project = u'ckanext-spatial' -copyright = u'© 2011-2021 Open Knowledge Foundation and contributors.' +copyright = u'© 2011-{} Open Knowledge Foundation and contributors.'.format(datetime.datetime.utcnow().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/install.rst b/doc/install.rst index 7f239ccd..d9e70b6a 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -8,10 +8,6 @@ Check the Troubleshooting_ section if you get errors at any stage. or WMS services), these are now located in ckanext-geoview_. They have a much simpler installation, so you can skip all the following steps if you just want the previews. - -.. note:: The package names and paths shown are the defaults on Ubuntu installs. - Adjust the package names and the paths if you are using a different platform. - .. note:: Starting from ckanext-spatial 2.0.0 **PostGIS is no longer required** to use the extension, and its use has been deprected. If for some reason you still need to use the old PostGIS backend see :ref:`legacy_postgis`. @@ -22,6 +18,9 @@ All commands assume an existing CKAN database named ``ckan_default``. Install the extension --------------------- +.. note:: The package names and paths shown are the defaults on Ubuntu installs. + Adjust the package names and the paths if you are using a different platform. + #. Install some packages needed by the extension dependencies:: sudo apt-get install python-dev libxml2-dev libxslt1-dev libgeos-c1 diff --git a/doc/spatial-search.rst b/doc/spatial-search.rst index 50b3d406..c34372b9 100644 --- a/doc/spatial-search.rst +++ b/doc/spatial-search.rst @@ -6,28 +6,18 @@ The spatial extension allows to index datasets with spatial information so they can be filtered via a spatial search query. This includes both via the web interface (see the `Spatial Search Widget`_) or via the `action API`_, e.g.:: - POST http://localhost:5000/api/action/package_search - { "q": "Pollution", - "facet": "true", - "facet.field": "country", - "extras": { - "ext_bbox": "-7.535093,49.208494,3.890688,57.372349" } - } - -.. versionchanged:: 2.0.1 - Starting from this version the spatial filter it is also supported on GET - requests: - http://localhost:5000/api/action/package_search?q=Pollution&ext_bbox=-7.535093,49.208494,3.890688,57.372349 +The ``ext_bbox`` parameter must be provided in the form ``ext_bbox={minx},{miny},{maxx},{maxy}`` + Setup ----- To enable the spatial search you need to add the ``spatial_query`` plugin to -your ini file. This plugin requires the ``spatial_metadata`` plugin, eg:: +your ini file. This plugin in turn requires the ``spatial_metadata`` plugin, eg:: - ckan.plugins = [other plugins] spatial_metadata spatial_query + ckan.plugins = ... spatial_metadata spatial_query To define which backend to use for the spatial search use the following configuration option (see `Choosing a backend for the spatial search`_):: @@ -39,8 +29,14 @@ Geo-Indexing your datasets -------------------------- Regardless of the backend that you are using, in order to make a dataset -searchable by location, it must have a special extra, with its key named -'spatial'. The value must be a valid GeoJSON_ geometry, for example:: +searchable by location, it must have a the location information (a geometry), indexed in +Solr. You can provide this information in two ways. + +The ``spatial`` extra field ++++++++++++++++++++++++++++ + +The easiest way to get your geometries indexed is to use an extra field named ``spatial``. +The value of this extra should be a valid GeoJSON_ geometry, for example:: { "type":"Polygon", @@ -56,90 +52,122 @@ or:: Every time a dataset is created, updated or deleted, the extension will -synchronize the information stored in the extra with the geometry table. +index the information stored in the ``spatial`` in Solr, so it can be reflected on spatial searches. If you already have datasets when you enable Spatial Search then you'll need to -reindex them: +`rebuild the search index `_. + + +Custom indexing logic ++++++++++++++++++++++ + +You might not want to use the ``spatial`` extra field. Perhaps you don't want to store the geometries +in the dataset metadata but prefer to do so in a separate table, or you simply want to perform a different +processing on the geometries before indexing. + +In this case you need to implement the ``before_dataset_index()`` method of the `IPackageController `_ interface:: + + def before_dataset_search(self, dataset_dict): + + # When using the default `solr` backend (based on bounding boxes), you need to + # include the `spatial_bbox` field in the returned dataset_dict. Make sure to use + # the correct syntax expected by Solr: + + dataset_dict["spatial_bbox"] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})" + + # When using the `solr-spatial-field` backend, you need to include the `spatial_geom` + # field in the returned dataset_dict. This should be a valid geometry in WKT format. + # Shapely can help you get the WKT representation of your gemetry if you have it in GeoJSON: + + shape = shapely.geometry.shape(geometry) + wkt = shape.wkt + + dataset_dict["spatial_geom"] = wkt - ckan --config=/etc/ckan/default/development.ini search-index rebuild + # Don't forget to actually return the dict! -..note:: For CKAN 2.8 and below use: + return dataset_dict - paster --plugin=ckan search-index rebuild --config=/etc/ckan/default/development.ini +Some things to keep in mind: +* Remember, you only need to provide one field, either ``spatial_bbox`` or ``spatial_geom``, depending on + the backend chosen. +* All indexed geometries should fall within the -180, -90, 180, 90 bounds. If you have polygons crossing the antimeridian (i.e. with longituded lower than -180 or bigger than 180) you'll have to split them across the antimeridian. +* Check the default implementation of ``before_dataset_index()`` in `ckanext/spatial/plugins/__init__.py `_ for extra useful checks and validations. +* If you want to store the geometry in the ``spatial`` field but don't want to apply the default automatic indexing logic applied by ckanext-spatial just remove the field from the dict (this won't remove it from the dataset metadata, just from the indexed data):: + + def before_dataset_search(self, dataset_dict): + + dataset_dict.pop("spatial", None) + + return dataset_dict Choosing a backend for the spatial search +++++++++++++++++++++++++++++++++++++++++ +Ckanext-spatial uses Solr to power the spatial search. The current implementation is tested on Solr 8, which is the supported version, although it might work on previous Solr versions. + +The are official `Docker images for Solr `_ that have all the configuration needed to perform spatial searches. This is the easiest way to get started but if you need to customize Solr yourself see below for the modifications needed. + There are different backends supported for the spatial search, it is important to understand their differences and the necessary setup required when choosing -which one to use. +which one to use. To configure the search backend use the following configuration option:: -The following table summarizes the different spatial search backends: + ckanext.spatial.search_backend = solr | solr-spatial-field -+------------------------+---------------+-------------------------------------+-----------------------------------------------------------+-------------------------------------------+ -| Backend | Solr Versions | Supported geometries | Sorting and relevance | Performance with large number of datasets | -+========================+===============+=====================================+===========================================================+===========================================+ -| ``solr`` | >= 3.1 | Bounding Box | Yes, spatial sorting combined with other query parameters | Good | -+------------------------+---------------+-------------------------------------+-----------------------------------------------------------+-------------------------------------------+ -| ``solr-spatial-field`` | >= 4.x | Bounding Box, Point and Polygon [1] | Not implemented | Good | -+------------------------+---------------+-------------------------------------+-----------------------------------------------------------+-------------------------------------------+ +The following table summarizes the different spatial search backends: ++------------------------+--------------------------------------+--------------------+ +| Backend | Supported geometries indexed in Solr | Solr setup needed | ++========================+======================================+====================+ +| ``solr`` | Bounding Box | Custom field | ++------------------------+--------------------------------------+--------------------+ +| ``solr-spatial-field`` | Bounding Box, Point and Polygon | Custom field + JTS | ++------------------------+--------------------------------------+--------------------+ -[1] Requires JTS -We recommend to use the ``solr`` backend whenever possible. Here are more -details about the available options: +The ``solr`` backend is probably a good starting point. Here are more +details about the available options (again, you don't need to modify Solr if you are using one of the spatial enabled official Docker images): -* ``solr`` (Recommended) - This option uses normal Solr fields to index the relevant bits of - information about the geometry and uses an algorithm function to sort - results by relevance, keeping any other non-spatial filtering. It only - supports bounding boxes both for the geometries to be indexed and the - input query shape. It requires `EDisMax`_ query parser, so it will only - work on versions of Solr greater than 3.1 (We recommend using Solr 4.x). +* ``solr`` + This option always indexes just the extent of the provided geometries, whether if it's an + actual bounding box or not. It uses Solr's `BBoxField `_ so you need to add the following to your Solr schema:: - You will need to add the following fields to your Solr schema file to - enable it:: + + + + - - - - - + - The solr schema file is typically located at: (..)/src/ckan/ckan/config/solr/schema.xml - * ``solr-spatial-field`` - This option uses the `spatial field`_ introduced in Solr 4, which allows - to index points, rectangles and more complex geometries (complex geometries - will require `JTS`_, check the documentation). - Sorting has not yet been implemented, users willing to do so will need to - modify the query using the ``before_search`` extension point. - + This option uses the `RPT `_ Solr field, which allows + to index points, rectangles and more complex geometries like polygons. This requires the install of the `JTS`_ library. See the linked Solr documentation for details on this. You will need to add the following field type and field to your Solr - schema file to enable it (Check the `Solr documentation`__ for more - information on the different parameters, note that you don't need - ``spatialContextFactory`` if you are not using JTS):: + schema file to enable it :: - + maxDistErr="0.001" + distanceUnits="kilometers" /> + - + + .. note:: The old ``postgis`` search backend is deprecated and will be removed in future versions of the extension. You should migrate to one of the other backends instead but if you need to keep using it for a while see :ref:`legacy_postgis`. @@ -229,8 +257,5 @@ For adding the map to the main body, add this to the main dataset page template You need to load the ``spatial_metadata`` plugin to use these snippets. .. _action API: http://docs.ckan.org/en/latest/apiv3.html -.. _edismax: http://wiki.apache.org/solr/ExtendedDisMax -.. _JTS: http://www.vividsolutions.com/jts/JTSHome.htm -.. _spatial field: http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4 -__ `spatial field`_ +.. _JTS: https://github.com/locationtech/jts .. _GeoJSON: http://geojson.org From 3fce3934bff61c7b6561437267691ff50de5914b Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 6 Sep 2022 14:37:40 +0200 Subject: [PATCH 38/66] Add tests for custom indexing and multivalued geometries --- .../spatial/tests/functional/test_widgets.py | 4 +- ckanext/spatial/tests/test_plugin/plugin.py | 24 +++++++++ ckanext/spatial/tests/test_spatial_search.py | 52 +++++++++++++++++++ test.ini | 2 +- 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/ckanext/spatial/tests/functional/test_widgets.py b/ckanext/spatial/tests/functional/test_widgets.py index fdc9d865..e112ce4b 100644 --- a/ckanext/spatial/tests/functional/test_widgets.py +++ b/ckanext/spatial/tests/functional/test_widgets.py @@ -7,8 +7,10 @@ import ckan.plugins.toolkit as tk +@pytest.mark.usefixtures("with_plugins", "clean_db", "clean_index", "harvest_setup") +@pytest.mark.ckan_config( + "ckan.plugins", "test_spatial_plugin spatial_metadata spatial_query") class TestSpatialWidgets(SpatialTestBase): - @pytest.mark.usefixtures("with_plugins", "clean_db", "clean_index", "harvest_setup") def test_dataset_map(self, app): dataset = factories.Dataset( extras=[{"key": "spatial", "value": self.geojson_examples["point"]}], diff --git a/ckanext/spatial/tests/test_plugin/plugin.py b/ckanext/spatial/tests/test_plugin/plugin.py index 17cab1c4..9e79853c 100644 --- a/ckanext/spatial/tests/test_plugin/plugin.py +++ b/ckanext/spatial/tests/test_plugin/plugin.py @@ -1,3 +1,5 @@ +import json +import shapely from ckan import plugins as p @@ -5,5 +7,27 @@ class TestSpatialPlugin(p.SingletonPlugin): p.implements(p.IConfigurer, inherit=True) + p.implements(p.IPackageController, inherit=True) + def update_config(self, config): p.toolkit.add_template_directory(config, "templates") + + def before_dataset_index(self, pkg_dict): + + if not pkg_dict.get("my_geoms"): + return pkg_dict + + pkg_dict["spatial_geom"] = [] + + my_geoms = json.loads(pkg_dict["my_geoms"]) + + if not isinstance(my_geoms, list): + my_geoms = [my_geoms] + + for geom in my_geoms: + + shape = shapely.geometry.shape(geom) + + pkg_dict["spatial_geom"].append(shape.wkt) + + return pkg_dict diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index f47c5f21..218fb054 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -596,3 +596,55 @@ def test_custom_spatial_query(self, monkeypatch, ckan_config): assert result["count"] == 0 + +@pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") +@pytest.mark.ckan_config( + "ckan.plugins", "test_spatial_plugin spatial_metadata spatial_query") +@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr-spatial-field") +class TestCustomIndexing(SpatialTestBase): + """ + These tests ensure both that + 1. You can use your own custom logic to index geometries + 2. The spatial fields are multivaule, ie you can index more than one geometry against + the same dataset + """ + def test_single_geom(self): + dataset = factories.Dataset( + extras=[{"key": "my_geoms", "value": self.geojson_examples["polygon"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + def test_multiple_geoms(self): + dataset = factories.Dataset( + extras=[ + { + "key": "my_geoms", + "value": "[{}, {}]".format( + extents["nz"], extents["ohio"]) + } + ] + ) + + # Test that we get the same dataset using two different extents + + # New Zealand + result = helpers.call_action( + "package_search", extras={"ext_bbox": "56,-54,189,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + # Ohio + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-110,37,-78,53"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] diff --git a/test.ini b/test.ini index b1786434..9f60f15b 100644 --- a/test.ini +++ b/test.ini @@ -14,7 +14,7 @@ port = 5000 [app:main] use = config:../ckan/test-core.ini ckan.legacy_templates = false -ckan.plugins = test_spatial_plugin harvest spatial_metadata spatial_query spatial_harvest_metadata_api gemini_csw_harvester gemini_doc_harvester gemini_waf_harvester +ckan.plugins = harvest spatial_metadata spatial_query spatial_harvest_metadata_api gemini_csw_harvester gemini_doc_harvester gemini_waf_harvester ckan.spatial.srid = 4326 ckan.spatial.default_map_extent=-6.88,49.74,0.50,59.2 ckan.spatial.testing = true From 44e3ce1a420c564a215db7b145930e43d3f48ac6 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 7 Sep 2022 10:24:35 +0200 Subject: [PATCH 39/66] Fix, add 2.9 hook variant in test plugin --- ckanext/spatial/tests/test_plugin/plugin.py | 3 +++ ckanext/spatial/tests/test_spatial_search.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ckanext/spatial/tests/test_plugin/plugin.py b/ckanext/spatial/tests/test_plugin/plugin.py index 9e79853c..4312bfde 100644 --- a/ckanext/spatial/tests/test_plugin/plugin.py +++ b/ckanext/spatial/tests/test_plugin/plugin.py @@ -12,6 +12,9 @@ class TestSpatialPlugin(p.SingletonPlugin): def update_config(self, config): p.toolkit.add_template_directory(config, "templates") + def before_index(self, pkg_dict): + return self.before_dataset_index(pkg_dict) + def before_dataset_index(self, pkg_dict): if not pkg_dict.get("my_geoms"): diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index 218fb054..d19c3ae6 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -605,7 +605,7 @@ class TestCustomIndexing(SpatialTestBase): """ These tests ensure both that 1. You can use your own custom logic to index geometries - 2. The spatial fields are multivaule, ie you can index more than one geometry against + 2. The spatial fields are multivalued, ie you can index more than one geometry against the same dataset """ def test_single_geom(self): From 9bc5b4f00083b72735383417065ca9cdda6f246b Mon Sep 17 00:00:00 2001 From: Andres Vazquez Date: Tue, 13 Sep 2022 16:36:57 -0300 Subject: [PATCH 40/66] Allow Multiple Geometries --- ckanext/spatial/plugin/__init__.py | 61 ++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 4e5e36aa..a3485e5f 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -6,6 +6,7 @@ import geojson import shapely.geometry +from shapely.errors import GeometryTypeError import ckantoolkit as tk @@ -225,10 +226,14 @@ def before_dataset_index(self, pkg_dict): try: geometry = json.loads(geom_from_metadata) + except (AttributeError, ValueError) as e: + log.error(f'Geometry not valid JSON {e}, not indexing :: {geom_from_metadata[:100]}') + return pkg_dict + try: shape = shapely.geometry.shape(geometry) - except (AttributeError, ValueError): - log.error('Geometry not valid GeoJSON, not indexing') + except GeometryTypeError as e: + log.error(f'{e}, not indexing :: {geom_from_metadata[:100]}') return pkg_dict if search_backend == 'solr': @@ -244,28 +249,36 @@ def before_dataset_index(self, pkg_dict): elif search_backend == 'solr-spatial-field': wkt = None - # Check potential problems with bboxes - if geometry['type'] == 'Polygon' \ - and len(geometry['coordinates']) == 1 \ - and len(geometry['coordinates'][0]) == 5: - - # Check wrong bboxes (4 same points) - xs = [p[0] for p in geometry['coordinates'][0]] - ys = [p[1] for p in geometry['coordinates'][0]] - - if xs.count(xs[0]) == 5 and ys.count(ys[0]) == 5: - wkt = 'POINT({x} {y})'.format(x=xs[0], y=ys[0]) - else: - # Check if coordinates are defined counter-clockwise, - # otherwise we'll get wrong results from Solr - lr = shapely.geometry.polygon.LinearRing(geometry['coordinates'][0]) - lr_coords = ( - list(lr.coords) if lr.is_ccw - else list(reversed(list(lr.coords))) - ) - polygon = shapely.geometry.polygon.Polygon( - fit_linear_ring(lr_coords)) - wkt = polygon.wkt + # We allow multiple geometries as GeometryCollections + if geometry['type'] == 'GeometryCollection': + geometries = geometry['geometries'] + else: + geometries = [geometry] + + # Check potential problems with bboxes in each geometry + wkt = [] + for geom in geometries: + if geom['type'] == 'Polygon' \ + and len(geom['coordinates']) == 1 \ + and len(geom['coordinates'][0]) == 5: + + # Check wrong bboxes (4 same points) + xs = [p[0] for p in geom['coordinates'][0]] + ys = [p[1] for p in geom['coordinates'][0]] + + if xs.count(xs[0]) == 5 and ys.count(ys[0]) == 5: + wkt.append('POINT({x} {y})'.format(x=xs[0], y=ys[0])) + else: + # Check if coordinates are defined counter-clockwise, + # otherwise we'll get wrong results from Solr + lr = shapely.geometry.polygon.LinearRing(geom['coordinates'][0]) + lr_coords = ( + list(lr.coords) if lr.is_ccw + else list(reversed(list(lr.coords))) + ) + polygon = shapely.geometry.polygon.Polygon( + fit_linear_ring(lr_coords)) + wkt.append(polygon.wkt) if not wkt: shape = shapely.geometry.shape(geometry) From ca7a054a395c5e9d8e125c98b666c8ccd711c5e0 Mon Sep 17 00:00:00 2001 From: Andres Vazquez Date: Tue, 13 Sep 2022 17:02:01 -0300 Subject: [PATCH 41/66] Py2 compatibility --- ckanext/spatial/plugin/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index a3485e5f..8b45e018 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -227,13 +227,13 @@ def before_dataset_index(self, pkg_dict): try: geometry = json.loads(geom_from_metadata) except (AttributeError, ValueError) as e: - log.error(f'Geometry not valid JSON {e}, not indexing :: {geom_from_metadata[:100]}') + log.error('Geometry not valid JSON {}, not indexing :: {}'.format(e, geom_from_metadata[:100])) return pkg_dict try: shape = shapely.geometry.shape(geometry) except GeometryTypeError as e: - log.error(f'{e}, not indexing :: {geom_from_metadata[:100]}') + log.error('{}, not indexing :: {}'.format(e, geom_from_metadata[:100])) return pkg_dict if search_backend == 'solr': From 31d73b2b4835fc50aa7f1825fc5cd177ff347abb Mon Sep 17 00:00:00 2001 From: Andres Vazquez Date: Wed, 14 Sep 2022 12:45:00 -0300 Subject: [PATCH 42/66] Fix error for older Shapely versions --- ckanext/spatial/plugin/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index 8b45e018..c03d2ac9 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -6,7 +6,11 @@ import geojson import shapely.geometry -from shapely.errors import GeometryTypeError +try: + from shapely.errors import GeometryTypeError +except ImportError: + # Previous version of shapely uses ValueError and TypeError + GeometryTypeError = (ValueError, TypeError) import ckantoolkit as tk From e5628f088a1f76237600694e6f2292f4e91b2616 Mon Sep 17 00:00:00 2001 From: Andres Vazquez Date: Wed, 14 Sep 2022 12:55:57 -0300 Subject: [PATCH 43/66] Test GeometryCollection --- ckanext/spatial/tests/test_spatial_search.py | 39 +++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index d19c3ae6..f30b2874 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +import json import pytest from ckan.lib.search import SearchError @@ -275,6 +275,43 @@ def test_custom_spatial_query(self, monkeypatch, ckan_config): ) assert result["count"] == 0 + + def test_geometry_collection(self): + """ Test a geometry collection """ + + # Build a GeometryCollection with all ohio and nz extents + geometry_collection = { + 'type': 'GeometryCollection', + 'geometries': [ + json.loads(geom) for zone, geom in extents.items() if zone in ['nz', 'ohio']] + } + + dataset = factories.Dataset( + extras=[ + { + "key": "spatial", + "value": json.dumps(geometry_collection) + } + ] + ) + + # Test that we get the same dataset using two different extents + + # New Zealand + result = helpers.call_action( + "package_search", extras={"ext_bbox": "56,-54,189,-28"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] + + # Ohio + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-110,37,-78,53"} + ) + + assert result["count"] == 1 + assert result["results"][0]["id"] == dataset["id"] @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") From ef8c3c113422ad6ada00d6e7aed02a5ccb07d11d Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 6 Oct 2022 11:19:38 +0200 Subject: [PATCH 44/66] Rename solr backend to solr-bbox for clarity --- ckanext/spatial/plugin/__init__.py | 22 +++++++++++++------- ckanext/spatial/tests/test_spatial_search.py | 4 ++-- doc/spatial-search.rst | 22 +++++++++++--------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index c03d2ac9..a2ccb480 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -33,9 +33,10 @@ log = getLogger(__name__) -DEFAULT_SEARCH_BACKEND = "solr" +DEFAULT_SEARCH_BACKEND = "solr-bbox" ALLOWED_SEARCH_BACKENDS = [ - "solr", + "solr", # Deprecated, please update to "solr-bbox" + "solr-bbox", "solr-spatial-field", "postgis", # Deprecated: will be removed in the next version ] @@ -184,9 +185,14 @@ def _get_search_backend(self): search_backend, ALLOWED_SEARCH_BACKENDS) ) - if search_backend == "postgis": + if search_backend == "solr": + log.warning( + "The `solr` spatial search backend has been renamed to `solr-bbox`, " + "please update your configuration" + ) + elif search_backend == "postgis": log.warning( - "The `postgis` spatial search backend is deprecated" + "The `postgis` spatial search backend is deprecated " "and will be removed in future versions" ) @@ -213,7 +219,7 @@ def before_dataset_index(self, pkg_dict): search_backend = self._get_search_backend() - if search_backend not in ('solr', 'solr-spatial-field'): + if search_backend not in ("solr-bbox", "solr-spatial-field"): return pkg_dict if not pkg_dict.get('extras_spatial'): @@ -240,7 +246,7 @@ def before_dataset_index(self, pkg_dict): log.error('{}, not indexing :: {}'.format(e, geom_from_metadata[:100])) return pkg_dict - if search_backend == 'solr': + if search_backend == "solr-bbox": # We always index the envelope of the geometry regardless of # if it's an actual bounding box (polygon) @@ -311,7 +317,7 @@ def before_dataset_search(self, search_params): if not bbox: raise SearchError('Wrong bounding box provided') - if search_backend in ('solr', 'solr-spatial-field'): + if search_backend in ("solr-bbox", "solr-spatial-field"): bbox = fit_bbox(bbox) @@ -319,7 +325,7 @@ def before_dataset_search(self, search_params): search_params["fq_list"] = [] spatial_field = ( - "spatial_bbox" if search_backend == "solr" else "spatial_geom" + "spatial_bbox" if search_backend == "solr-bbox" else "spatial_geom" ) default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index f30b2874..93d5eb40 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -35,7 +35,7 @@ @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") -@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr") +@pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr-bbox") class TestBBoxSearch(SpatialTestBase): def test_spatial_query(self): dataset = factories.Dataset( @@ -275,7 +275,7 @@ def test_custom_spatial_query(self, monkeypatch, ckan_config): ) assert result["count"] == 0 - + def test_geometry_collection(self): """ Test a geometry collection """ diff --git a/doc/spatial-search.rst b/doc/spatial-search.rst index c34372b9..46ba82dd 100644 --- a/doc/spatial-search.rst +++ b/doc/spatial-search.rst @@ -113,24 +113,26 @@ There are different backends supported for the spatial search, it is important to understand their differences and the necessary setup required when choosing which one to use. To configure the search backend use the following configuration option:: - ckanext.spatial.search_backend = solr | solr-spatial-field + ckanext.spatial.search_backend = solr-bbox | solr-spatial-field The following table summarizes the different spatial search backends: -+------------------------+--------------------------------------+--------------------+ -| Backend | Supported geometries indexed in Solr | Solr setup needed | -+========================+======================================+====================+ -| ``solr`` | Bounding Box | Custom field | -+------------------------+--------------------------------------+--------------------+ -| ``solr-spatial-field`` | Bounding Box, Point and Polygon | Custom field + JTS | -+------------------------+--------------------------------------+--------------------+ ++-------------------------+--------------------------------------+--------------------+ +| Backend | Supported geometries indexed in Solr | Solr setup needed | ++=========================+======================================+====================+ +| ``solr-bbox`` (default) | Bounding Box | Custom field | ++-------------------------+--------------------------------------+--------------------+ +| ``solr-spatial-field`` | Bounding Box, Point and Polygon | Custom field + JTS | ++-------------------------+--------------------------------------+--------------------+ +.. note:: The default ``solr-bbox`` search backend was previously known as ``solr``. Please update + your configuration if using this version as it will be removed in the future. -The ``solr`` backend is probably a good starting point. Here are more +The ``solr-bbox`` backend is probably a good starting point. Here are more details about the available options (again, you don't need to modify Solr if you are using one of the spatial enabled official Docker images): -* ``solr`` +* ``solr-bbox`` This option always indexes just the extent of the provided geometries, whether if it's an actual bounding box or not. It uses Solr's `BBoxField `_ so you need to add the following to your Solr schema:: From f64121ab049b20f6a309bbfef53cb5fc22e1dd36 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 6 Oct 2022 11:49:03 +0200 Subject: [PATCH 45/66] Fix CSRF issus in functional tests --- ckanext/spatial/tests/functional/test_package.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ckanext/spatial/tests/functional/test_package.py b/ckanext/spatial/tests/functional/test_package.py index 5da5f68e..542618bf 100644 --- a/ckanext/spatial/tests/functional/test_package.py +++ b/ckanext/spatial/tests/functional/test_package.py @@ -6,7 +6,7 @@ import ckan.tests.helpers as helpers -@pytest.mark.usefixtures("with_plugins", "clean_db", "clean_index", "harvest_setup") +@pytest.mark.usefixtures("with_plugins", "with_request_context", "clean_db", "clean_index", "harvest_setup") class TestSpatialExtra(SpatialTestBase): def test_spatial_extra_base(self, app): @@ -18,7 +18,6 @@ def test_spatial_extra_base(self, app): offset = tk.url_for("dataset.edit", id=dataset["id"]) else: offset = tk.url_for(controller="package", action="edit", id=dataset["id"]) - res = app.get(offset, extra_environ=env) if tk.check_ckan_version(min_version="2.9"): data = { @@ -31,6 +30,8 @@ def test_spatial_extra_base(self, app): form = res.forms[1] form["extras__0__key"] = u"spatial" form["extras__0__value"] = self.geojson_examples["point"] + + res = app.get(offset, extra_environ=env) res = helpers.submit_and_follow(app, form, env, "save") assert "Error" not in res, res From 2945795b5f4817e0e0f7b6e2a3490765ba2ca545 Mon Sep 17 00:00:00 2001 From: amercader Date: Fri, 7 Oct 2022 15:23:44 +0200 Subject: [PATCH 46/66] Update backend internally whenusing deprecated "solr" one --- ckanext/spatial/plugin/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index a2ccb480..a28aab9f 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -190,6 +190,8 @@ def _get_search_backend(self): "The `solr` spatial search backend has been renamed to `solr-bbox`, " "please update your configuration" ) + search_backend = "solr-bbox" + elif search_backend == "postgis": log.warning( "The `postgis` spatial search backend is deprecated " From b89da29192cdf2f62f1561b024ec28a8921b9126 Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 10 Oct 2022 11:26:25 +0200 Subject: [PATCH 47/66] Add tests for ordering and other queries --- ckanext/spatial/tests/test_spatial_search.py | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index 93d5eb40..ab4e5ad0 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -313,6 +313,87 @@ def test_geometry_collection(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] + def test_sorting_of_bbox_results(self): + """ + Bounding box based searches support ordering the results spatially. The default + method used is overlap ratio, ie datasets with geometries that overlap more with + the input bounding box are returned first. In this case for instance, we would + expect the results to be ordered as B, D, A, C + + 2 ┌────────────┬────────────┐ + │ │ │ + │ xxxxxxxxxxxxx │ + │ A x │ x B │ + │ x │ x │ + │ x │ x │ + 1 ├─────────x──┼────────x───┤ + │ x │ x │ + │ xxxxxxxxxxxxx │ + │ │ │ + │ C │ D │ + │ │ │ + └────────────┴────────────┘ + 0,0 1 2 + """ + + dataset_extents = [ + ("Dataset A", (0, 1, 1, 2)), + ("Dataset B", (1, 1, 2, 2)), + ("Dataset C", (0, 0, 1, 1)), + ("Dataset D", (1, 0, 2, 1)), + ] + + for item in dataset_extents: + geom = """ + {{"type":"Polygon", + "coordinates":[[[{xmin},{ymax}],[{xmin},{ymin}],[{xmax},{ymin}],[{xmax},{ymax}],[{xmin},{ymax}]]]}} + """.format( + xmin=item[1][0], ymin=item[1][1], xmax=item[1][2], ymax=item[1][3]) + factories.Dataset( + title=item[0], + extras=[{"key": "spatial", "value": geom}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "0.75,0.75,1.75,1.75"} + ) + + assert result["count"] == 4 + + assert ( + [d["title"] for d in result["results"]] == [ + "Dataset B", "Dataset D", "Dataset A", "Dataset C"] + ) + + def test_spatial_search_combined_with_other_q(self): + dataset_extents = [ + ("Dataset A", (0, 1, 1, 2), "rabbit"), + ("Dataset B", (1, 1, 2, 2), "rabbit"), + ("Dataset C", (0, 0, 1, 1), "mole"), + ("Dataset D", (1, 0, 2, 1), "badger"), + ] + + for item in dataset_extents: + geom = """ + {{"type":"Polygon", + "coordinates":[[[{xmin},{ymax}],[{xmin},{ymin}],[{xmax},{ymin}],[{xmax},{ymax}],[{xmin},{ymax}]]]}} + """.format( + xmin=item[1][0], ymin=item[1][1], xmax=item[1][2], ymax=item[1][3]) + factories.Dataset( + title=item[0], + notes=item[2], + extras=[{"key": "spatial", "value": geom}] + + ) + + result = helpers.call_action( + "package_search", q="rabbit", extras={"ext_bbox": "0.75,0.75,1.75,1.75"} + ) + + assert result["count"] == 2 + + + @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") @pytest.mark.ckan_config("ckanext.spatial.search_backend", "solr-spatial-field") From 15caaad9f16043c336fa3c6a349d6c171cde2641 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 11 Oct 2022 10:20:45 +0200 Subject: [PATCH 48/66] Refactor the search logic to move it to its own module --- ckanext/spatial/plugin/__init__.py | 165 ++------------------ ckanext/spatial/search/__init__.py | 242 +++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 156 deletions(-) create mode 100644 ckanext/spatial/search/__init__.py diff --git a/ckanext/spatial/plugin/__init__.py b/ckanext/spatial/plugin/__init__.py index a28aab9f..a3c7dd74 100644 --- a/ckanext/spatial/plugin/__init__.py +++ b/ckanext/spatial/plugin/__init__.py @@ -18,7 +18,6 @@ from ckan.lib.search import SearchError from ckan.lib.helpers import json -from ckanext.spatial.lib import normalize_bbox, fit_bbox, fit_linear_ring if tk.check_ckan_version(min_version="2.9.0"): from ckanext.spatial.plugin.flask_plugin import ( @@ -29,6 +28,9 @@ SpatialQueryMixin, HarvestMetadataApiMixin ) +from ckanext.spatial.lib import normalize_bbox +from ckanext.spatial.search import search_backends + config = tk.config log = getLogger(__name__) @@ -227,85 +229,17 @@ def before_dataset_index(self, pkg_dict): if not pkg_dict.get('extras_spatial'): return pkg_dict + pkg_dict = search_backends[search_backend]().index_dataset(pkg_dict) + # Coupled resources are URL -> uuid links, they are not needed in SOLR # and might be huge if there are lot of coupled resources pkg_dict.pop('coupled-resource', None) pkg_dict.pop('extras_coupled-resource', None) # spatial field is geojson coordinate data, not needed in SOLR either - geom_from_metadata = pkg_dict.pop('spatial', None) + pkg_dict.pop('spatial', None) pkg_dict.pop('extras_spatial', None) - try: - geometry = json.loads(geom_from_metadata) - except (AttributeError, ValueError) as e: - log.error('Geometry not valid JSON {}, not indexing :: {}'.format(e, geom_from_metadata[:100])) - return pkg_dict - - try: - shape = shapely.geometry.shape(geometry) - except GeometryTypeError as e: - log.error('{}, not indexing :: {}'.format(e, geom_from_metadata[:100])) - return pkg_dict - - if search_backend == "solr-bbox": - # We always index the envelope of the geometry regardless of - # if it's an actual bounding box (polygon) - - bounds = shape.bounds - bbox = fit_bbox(normalize_bbox(list(bounds))) - - pkg_dict["spatial_bbox"] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})".format( - **bbox) - - elif search_backend == 'solr-spatial-field': - wkt = None - - # We allow multiple geometries as GeometryCollections - if geometry['type'] == 'GeometryCollection': - geometries = geometry['geometries'] - else: - geometries = [geometry] - - # Check potential problems with bboxes in each geometry - wkt = [] - for geom in geometries: - if geom['type'] == 'Polygon' \ - and len(geom['coordinates']) == 1 \ - and len(geom['coordinates'][0]) == 5: - - # Check wrong bboxes (4 same points) - xs = [p[0] for p in geom['coordinates'][0]] - ys = [p[1] for p in geom['coordinates'][0]] - - if xs.count(xs[0]) == 5 and ys.count(ys[0]) == 5: - wkt.append('POINT({x} {y})'.format(x=xs[0], y=ys[0])) - else: - # Check if coordinates are defined counter-clockwise, - # otherwise we'll get wrong results from Solr - lr = shapely.geometry.polygon.LinearRing(geom['coordinates'][0]) - lr_coords = ( - list(lr.coords) if lr.is_ccw - else list(reversed(list(lr.coords))) - ) - polygon = shapely.geometry.polygon.Polygon( - fit_linear_ring(lr_coords)) - wkt.append(polygon.wkt) - - if not wkt: - shape = shapely.geometry.shape(geometry) - if not shape.is_valid: - log.error('Wrong geometry, not indexing') - return pkg_dict - if shape.bounds[0] < -180 or shape.bounds[2] > 180: - log.error(""" -Geometries outside the -180, -90, 180, 90 boundaries are not supported, -you need to split the geometry in order to fit the parts. Not indexing""") - return pkg_dict - wkt = shape.wkt - - pkg_dict['spatial_geom'] = wkt - return pkg_dict def before_dataset_search(self, search_params): @@ -316,93 +250,12 @@ def before_dataset_search(self, search_params): if input_bbox: bbox = normalize_bbox(input_bbox) + if not bbox: raise SearchError('Wrong bounding box provided') - if search_backend in ("solr-bbox", "solr-spatial-field"): - - bbox = fit_bbox(bbox) - - if not search_params.get("fq_list"): - search_params["fq_list"] = [] - - spatial_field = ( - "spatial_bbox" if search_backend == "solr-bbox" else "spatial_geom" - ) - - default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" - - spatial_query = config.get( - "ckanext.spatial.solr_query", default_spatial_query) - - search_params["fq_list"].append( - spatial_query.format( - spatial_field=spatial_field, **bbox) - ) - - elif search_backend == 'postgis': - search_params = self._params_for_postgis_search(bbox, search_params) - - return search_params - - def _params_for_postgis_search(self, bbox, search_params): - """ - Note: The PostGIS search functionality will be removed in future versions - """ - from ckanext.spatial.postgis.model import bbox_query, bbox_query_ordered - from ckan.lib.search import SearchError - - # Adjust easting values - while (bbox['minx'] < -180): - bbox['minx'] += 360 - bbox['maxx'] += 360 - while (bbox['minx'] > 180): - bbox['minx'] -= 360 - bbox['maxx'] -= 360 - - # Note: This will be deprecated at some point in favour of the - # Solr 4 spatial sorting capabilities - if search_params.get('sort') == 'spatial desc' and \ - tk.asbool(config.get('ckanext.spatial.use_postgis_sorting', 'False')): - if search_params['q'] or search_params['fq']: - raise SearchError('Spatial ranking cannot be mixed with other search parameters') - # ...because it is too inefficient to use SOLR to filter - # results and return the entire set to this class and - # after_search do the sorting and paging. - extents = bbox_query_ordered(bbox) - are_no_results = not extents - search_params['extras']['ext_rows'] = search_params['rows'] - search_params['extras']['ext_start'] = search_params['start'] - # this SOLR query needs to return no actual results since - # they are in the wrong order anyway. We just need this SOLR - # query to get the count and facet counts. - rows = 0 - search_params['sort'] = None # SOLR should not sort. - # Store the rankings of the results for this page, so for - # after_search to construct the correctly sorted results - rows = search_params['extras']['ext_rows'] = search_params['rows'] - start = search_params['extras']['ext_start'] = search_params['start'] - search_params['extras']['ext_spatial'] = [ - (extent.package_id, extent.spatial_ranking) \ - for extent in extents[start:start+rows]] - else: - extents = bbox_query(bbox) - are_no_results = extents.count() == 0 - - if are_no_results: - # We don't need to perform the search - search_params['abort_search'] = True - else: - # We'll perform the existing search but also filtering by the ids - # of datasets within the bbox - bbox_query_ids = [extent.package_id for extent in extents] - - q = search_params.get('q','').strip() or '""' - # Note: `"" AND` query doesn't work in github ci - new_q = '%s AND ' % q if q and q != '""' else '' - new_q += '(%s)' % ' OR '.join(['id:%s' % id for id in bbox_query_ids]) - - search_params['q'] = new_q + search_params = search_backends[search_backend]().search_params( + bbox, search_params) return search_params diff --git a/ckanext/spatial/search/__init__.py b/ckanext/spatial/search/__init__.py new file mode 100644 index 00000000..513d2d52 --- /dev/null +++ b/ckanext/spatial/search/__init__.py @@ -0,0 +1,242 @@ +import json +import logging + +import shapely.geometry + +try: + from shapely.errors import GeometryTypeError +except ImportError: + # Previous version of shapely uses ValueError and TypeError + GeometryTypeError = (ValueError, TypeError) + +from ckantoolkit import config, asbool +from ckanext.spatial.lib import normalize_bbox, fit_bbox, fit_linear_ring + +log = logging.getLogger(__name__) + + +class SpatialSearchBackend: + """Base class for all datastore backends.""" + + def parse_geojson(self, geom_from_metadata): + + try: + geometry = json.loads(geom_from_metadata) + except (AttributeError, ValueError) as e: + log.error( + "Geometry not valid JSON {}, not indexing :: {}".format( + e, geom_from_metadata[:100] + ) + ) + return None + + return geometry + + def shape_from_geometry(self, geometry): + try: + shape = shapely.geometry.shape(geometry) + except GeometryTypeError as e: + log.error("{}, not indexing :: {}".format(e, json.dumps(geometry)[:100])) + return None + + return shape + + +class SolrBBoxSearchBackend(SpatialSearchBackend): + def index_dataset(self, dataset_dict): + """ + We always index the envelope of the geometry regardless of + if it's an actual bounding box (polygon) + """ + + geom_from_metadata = dataset_dict.get("spatial") + geometry = self.parse_geojson(geom_from_metadata) + shape = self.shape_from_geometry(geometry) + + if not shape: + return dataset_dict + + bounds = shape.bounds + bbox = fit_bbox(normalize_bbox(list(bounds))) + + dataset_dict[ + "spatial_bbox" + ] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})".format(**bbox) + + return dataset_dict + + def search_params(self, bbox, search_params): + + bbox = fit_bbox(bbox) + + if not search_params.get("fq_list"): + search_params["fq_list"] = [] + + default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" + + spatial_query = config.get("ckanext.spatial.solr_query", default_spatial_query) + + search_params["fq_list"].append( + spatial_query.format(spatial_field="spatial_bbox", **bbox) + ) + + return search_params + + +class SolrSpatialFieldSearchBackend(SpatialSearchBackend): + def index_dataset(self, dataset_dict): + wkt = None + geom_from_metadata = dataset_dict.get("spatial") + geometry = self.parse_geojson(geom_from_metadata) + if not geometry: + return dataset_dict + + # We allow multiple geometries as GeometryCollections + if geometry["type"] == "GeometryCollection": + geometries = geometry["geometries"] + else: + geometries = [geometry] + + # Check potential problems with bboxes in each geometry + wkt = [] + for geom in geometries: + if ( + geom["type"] == "Polygon" + and len(geom["coordinates"]) == 1 + and len(geom["coordinates"][0]) == 5 + ): + + # Check wrong bboxes (4 same points) + xs = [p[0] for p in geom["coordinates"][0]] + ys = [p[1] for p in geom["coordinates"][0]] + + if xs.count(xs[0]) == 5 and ys.count(ys[0]) == 5: + wkt.append("POINT({x} {y})".format(x=xs[0], y=ys[0])) + else: + # Check if coordinates are defined counter-clockwise, + # otherwise we'll get wrong results from Solr + lr = shapely.geometry.polygon.LinearRing(geom["coordinates"][0]) + lr_coords = ( + list(lr.coords) + if lr.is_ccw + else list(reversed(list(lr.coords))) + ) + polygon = shapely.geometry.polygon.Polygon( + fit_linear_ring(lr_coords) + ) + wkt.append(polygon.wkt) + + shape = self.shape_from_geometry(geometry) + + if not wkt: + shape = shapely.geometry.shape(geometry) + if not shape.is_valid: + log.error("Wrong geometry, not indexing") + return dataset_dict + if shape.bounds[0] < -180 or shape.bounds[2] > 180: + log.error( + """ +Geometries outside the -180, -90, 180, 90 boundaries are not supported, +you need to split the geometry in order to fit the parts. Not indexing""" + ) + return dataset_dict + wkt = shape.wkt + + dataset_dict["spatial_geom"] = wkt + + return dataset_dict + + def search_params(self, bbox, search_params): + + bbox = fit_bbox(bbox) + + if not search_params.get("fq_list"): + search_params["fq_list"] = [] + + default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" + + spatial_query = config.get("ckanext.spatial.solr_query", default_spatial_query) + + search_params["fq_list"].append( + spatial_query.format(spatial_field="spatial_geom", **bbox) + ) + + return search_params + + +class PostgisSearchBackend(SpatialSearchBackend): + """ + Note: The PostGIS search functionality will be removed in future versions + """ + + def index_dataset(self, dataset_dict): + return dataset_dict + + def search_params(self, bbox, search_params): + from ckanext.spatial.postgis.model import bbox_query, bbox_query_ordered + from ckan.lib.search import SearchError + + # Adjust easting values + while bbox["minx"] < -180: + bbox["minx"] += 360 + bbox["maxx"] += 360 + while bbox["minx"] > 180: + bbox["minx"] -= 360 + bbox["maxx"] -= 360 + + # Note: This will be deprecated at some point in favour of the + # Solr 4 spatial sorting capabilities + if search_params.get("sort") == "spatial desc" and asbool( + config.get("ckanext.spatial.use_postgis_sorting", "False") + ): + if search_params["q"] or search_params["fq"]: + raise SearchError( + "Spatial ranking cannot be mixed with other search parameters" + ) + # ...because it is too inefficient to use SOLR to filter + # results and return the entire set to this class and + # after_search do the sorting and paging. + extents = bbox_query_ordered(bbox) + are_no_results = not extents + search_params["extras"]["ext_rows"] = search_params["rows"] + search_params["extras"]["ext_start"] = search_params["start"] + # this SOLR query needs to return no actual results since + # they are in the wrong order anyway. We just need this SOLR + # query to get the count and facet counts. + rows = 0 + search_params["sort"] = None # SOLR should not sort. + # Store the rankings of the results for this page, so for + # after_search to construct the correctly sorted results + rows = search_params["extras"]["ext_rows"] = search_params["rows"] + start = search_params["extras"]["ext_start"] = search_params["start"] + search_params["extras"]["ext_spatial"] = [ + (extent.package_id, extent.spatial_ranking) + for extent in extents[start : start + rows] + ] + else: + extents = bbox_query(bbox) + are_no_results = extents.count() == 0 + + if are_no_results: + # We don't need to perform the search + search_params["abort_search"] = True + else: + # We'll perform the existing search but also filtering by the ids + # of datasets within the bbox + bbox_query_ids = [extent.package_id for extent in extents] + + q = search_params.get("q", "").strip() or '""' + # Note: `"" AND` query doesn't work in github ci + new_q = "%s AND " % q if q and q != '""' else "" + new_q += "(%s)" % " OR ".join(["id:%s" % id for id in bbox_query_ids]) + + search_params["q"] = new_q + + return search_params + + +search_backends = { + "solr-bbox": SolrBBoxSearchBackend, + "solr-spatial-field": SolrSpatialFieldSearchBackend, + "postgis": PostgisSearchBackend, +} From ec0cc00bbe65195400532d226d67cb3027042487 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 11 Oct 2022 14:18:08 +0200 Subject: [PATCH 49/66] Revert to the old implementation for Bbox based searches The approach using Solr's own BBoxField turned out to be not optimal as it required the use of LocalParams. This is fine if the query is used in `fq` but CKAN core does not allow it on `q` (unreasonably IMO). Long story short the old implementation is a bit more verbose but supports: * Proper spatial sorting * Combining queries in `q` with a spatial filter * Antimeridian queries handling It doesn't support Point geometries, but users can use `solr-spatial-field` if they need them --- ckanext/spatial/search/__init__.py | 65 ++++++++++++++++---- ckanext/spatial/tests/base.py | 2 +- ckanext/spatial/tests/test_spatial_search.py | 48 ++++----------- doc/spatial-search.rst | 31 +++++----- 4 files changed, 83 insertions(+), 63 deletions(-) diff --git a/ckanext/spatial/search/__init__.py b/ckanext/spatial/search/__init__.py index 513d2d52..daf471e2 100644 --- a/ckanext/spatial/search/__init__.py +++ b/ckanext/spatial/search/__init__.py @@ -57,29 +57,72 @@ def index_dataset(self, dataset_dict): return dataset_dict bounds = shape.bounds - bbox = fit_bbox(normalize_bbox(list(bounds))) - dataset_dict[ - "spatial_bbox" - ] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})".format(**bbox) + bbox = normalize_bbox(list(bounds)) + if not bbox: + return dataset_dict + + dataset_dict.update(bbox) return dataset_dict def search_params(self, bbox, search_params): + """ + This will add the following parameters to the query: - bbox = fit_bbox(bbox) + defType - edismax (We need to define EDisMax to use bf) + bf - {function} A boost function to influence the score (thus + influencing the sorting). The algorithm can be basically defined as: - if not search_params.get("fq_list"): - search_params["fq_list"] = [] + 2 * X / Q + T - default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" + Where X is the intersection between the query area Q and the + target geometry T. It gives a ratio from 0 to 1 where 0 means + no overlap at all and 1 a perfect fit - spatial_query = config.get("ckanext.spatial.solr_query", default_spatial_query) + fq - Adds a filter that force the value returned by the previous + function to be between 0 and 1, effectively applying the + spatial filter. - search_params["fq_list"].append( - spatial_query.format(spatial_field="spatial_bbox", **bbox) + """ + + while bbox["minx"] < -180: + bbox["minx"] += 360 + bbox["maxx"] += 360 + while bbox["minx"] > 180: + bbox["minx"] -= 360 + bbox["maxx"] -= 360 + + values = dict( + input_minx=bbox["minx"], + input_maxx=bbox["maxx"], + input_miny=bbox["miny"], + input_maxy=bbox["maxy"], + area_search=abs(bbox["maxx"] - bbox["minx"]) + * abs(bbox["maxy"] - bbox["miny"]), ) + bf = ( + """div( + mul( + mul(max(0, sub(min({input_maxx},maxx) , max({input_minx},minx))), + max(0, sub(min({input_maxy},maxy) , max({input_miny},miny))) + ), + 2), + add({area_search},mul(sub(maxy, miny), sub(maxx, minx))) + )""".format( + **values + ) + .replace("\n", "") + .replace(" ", "") + ) + + search_params["fq_list"] = search_params.get("fq_list", []) + search_params["fq_list"].append("{!frange incl=false l=0 u=1}%s" % bf) + + search_params["bf"] = bf + search_params["defType"] = "edismax" + return search_params diff --git a/ckanext/spatial/tests/base.py b/ckanext/spatial/tests/base.py index 064a0295..3d292cd1 100644 --- a/ckanext/spatial/tests/base.py +++ b/ckanext/spatial/tests/base.py @@ -3,7 +3,7 @@ import os geojson_examples = { - "point": '{"type":"Point","coordinates":[100.0,0.0]}', + "point": '{"type":"Point","coordinates":[100.0,2.0]}', "point_2": '{"type":"Point","coordinates":[20,10]}', "line": '{"type":"LineString","coordinates":[[100.0,0.0],[101.0,1.0]]}', "polygon": '{"type":"Polygon","coordinates":[[[100.0,0.0],[101.0,0.0],' diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index ab4e5ad0..858d6f28 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -39,7 +39,7 @@ class TestBBoxSearch(SpatialTestBase): def test_spatial_query(self): dataset = factories.Dataset( - extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + extras=[{"key": "spatial", "value": extents["ohio"]}] ) result = helpers.call_action( @@ -49,9 +49,20 @@ def test_spatial_query(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] + def test_spatial_query_point(self): + dataset = factories.Dataset( + extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + ) + + result = helpers.call_action( + "package_search", extras={"ext_bbox": "-180,-90,180,90"} + ) + + assert result["count"] == 0 + def test_spatial_query_outside_bbox(self): factories.Dataset( - extras=[{"key": "spatial", "value": self.geojson_examples["point"]}] + extras=[{"key": "spatial", "value": extents["ohio"]}] ) result = helpers.call_action( @@ -243,39 +254,6 @@ def test_spatial_query_antimeridian_4(self): assert result["count"] == 1 assert result["results"][0]["id"] == dataset["id"] - def test_custom_spatial_query(self, monkeypatch, ckan_config): - """ - ┌────────────────┐ xxxxxx - │ xxxxx│xx xxx - │ xxx │ xxx - │ xx │ xx - │ xxxx │ xxx - │ x x│xxxxx - │ xxx xxxx│ - │ xxxx │ - │ │ - └────────────────┘ - """ - dataset = factories.Dataset(extras=[{"key": "spatial", "value": extents["nz"]}]) - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "175,-39.5,176.5,-39"} - ) - - assert result["count"] == 1 - assert result["results"][0]["id"] == dataset["id"] - - monkeypatch.setitem( - ckan_config, - "ckanext.spatial.solr_query", - "{{!field f={spatial_field}}}Contains(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))") - - result = helpers.call_action( - "package_search", extras={"ext_bbox": "175,-39.5,176.5,-39"} - ) - - assert result["count"] == 0 - def test_geometry_collection(self): """ Test a geometry collection """ diff --git a/doc/spatial-search.rst b/doc/spatial-search.rst index 46ba82dd..8d068ebc 100644 --- a/doc/spatial-search.rst +++ b/doc/spatial-search.rst @@ -22,7 +22,7 @@ your ini file. This plugin in turn requires the ``spatial_metadata`` plugin, eg: To define which backend to use for the spatial search use the following configuration option (see `Choosing a backend for the spatial search`_):: - ckanext.spatial.search_backend = solr + ckanext.spatial.search_backend = solr-bbox Geo-Indexing your datasets @@ -69,11 +69,13 @@ In this case you need to implement the ``before_dataset_index()`` method of the def before_dataset_search(self, dataset_dict): - # When using the default `solr` backend (based on bounding boxes), you need to - # include the `spatial_bbox` field in the returned dataset_dict. Make sure to use - # the correct syntax expected by Solr: + # When using the default `solr-bbox` backend (based on bounding boxes), you need to + # include the following fields in the returned dataset_dict: - dataset_dict["spatial_bbox"] = "ENVELOPE({minx}, {maxx}, {maxy}, {miny})" + dataset_dict["minx"] = minx + dataset_dict["maxx"] = maxx + dataset_dict["miny"] = miny + dataset_dict["maxy"] = maxy # When using the `solr-spatial-field` backend, you need to include the `spatial_geom` # field in the returned dataset_dict. This should be a valid geometry in WKT format. @@ -120,7 +122,7 @@ The following table summarizes the different spatial search backends: +-------------------------+--------------------------------------+--------------------+ | Backend | Supported geometries indexed in Solr | Solr setup needed | +=========================+======================================+====================+ -| ``solr-bbox`` (default) | Bounding Box | Custom field | +| ``solr-bbox`` (default) | Bounding Box, Polygon (extents only) | Custom fields | +-------------------------+--------------------------------------+--------------------+ | ``solr-spatial-field`` | Bounding Box, Point and Polygon | Custom field + JTS | +-------------------------+--------------------------------------+--------------------+ @@ -130,26 +132,23 @@ The following table summarizes the different spatial search backends: The ``solr-bbox`` backend is probably a good starting point. Here are more -details about the available options (again, you don't need to modify Solr if you are using one of the spatial enabled official Docker images): +details about the available options (again, you don't need to modify Solr if you are using one of the spatially enabled official Docker images): * ``solr-bbox`` This option always indexes just the extent of the provided geometries, whether if it's an - actual bounding box or not. It uses Solr's `BBoxField `_ so you need to add the following to your Solr schema:: - - - - - + actual bounding box or not. It supports spatial sorting of the returned results (based on the closeness of their bounding box to the query bounding box). It uses standard Solr float fields so you just need to add the following to your Solr schema:: - + + + + * ``solr-spatial-field`` This option uses the `RPT `_ Solr field, which allows - to index points, rectangles and more complex geometries like polygons. This requires the install of the `JTS`_ library. See the linked Solr documentation for details on this. + to index points, rectangles and more complex geometries like polygons. This requires the install of the `JTS`_ library. See the linked Solr documentation for details on this. Note that it does not support spatial sorting of the returned results. You will need to add the following field type and field to your Solr schema file to enable it :: From 03ad3ef31caab91af131db2107293825a230ea1b Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 11 Oct 2022 15:09:44 +0200 Subject: [PATCH 50/66] Add test for combined q in spatial field --- ckanext/spatial/tests/test_spatial_search.py | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ckanext/spatial/tests/test_spatial_search.py b/ckanext/spatial/tests/test_spatial_search.py index 858d6f28..b12715ca 100644 --- a/ckanext/spatial/tests/test_spatial_search.py +++ b/ckanext/spatial/tests/test_spatial_search.py @@ -692,6 +692,33 @@ def test_custom_spatial_query(self, monkeypatch, ckan_config): assert result["count"] == 0 + def test_spatial_search_combined_with_other_q(self): + dataset_extents = [ + ("Dataset A", (0, 1, 1, 2), "rabbit"), + ("Dataset B", (1, 1, 2, 2), "rabbit"), + ("Dataset C", (0, 0, 1, 1), "mole"), + ("Dataset D", (1, 0, 2, 1), "badger"), + ] + + for item in dataset_extents: + geom = """ + {{"type":"Polygon", + "coordinates":[[[{xmin},{ymax}],[{xmin},{ymin}],[{xmax},{ymin}],[{xmax},{ymax}],[{xmin},{ymax}]]]}} + """.format( + xmin=item[1][0], ymin=item[1][1], xmax=item[1][2], ymax=item[1][3]) + factories.Dataset( + title=item[0], + notes=item[2], + extras=[{"key": "spatial", "value": geom}] + + ) + + result = helpers.call_action( + "package_search", q="rabbit", extras={"ext_bbox": "0.75,0.75,1.75,1.75"} + ) + + assert result["count"] == 2 + @pytest.mark.usefixtures("clean_db", "clean_index", "harvest_setup", "with_plugins") @pytest.mark.ckan_config( From 6c48fdd32d57101c8885a83b11365951afb00a47 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 11 Oct 2022 15:13:39 +0200 Subject: [PATCH 51/66] Document ckanext.spatial.solr_query --- ckanext/spatial/search/__init__.py | 2 +- doc/spatial-search.rst | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ckanext/spatial/search/__init__.py b/ckanext/spatial/search/__init__.py index daf471e2..a54141de 100644 --- a/ckanext/spatial/search/__init__.py +++ b/ckanext/spatial/search/__init__.py @@ -196,7 +196,7 @@ def search_params(self, bbox, search_params): if not search_params.get("fq_list"): search_params["fq_list"] = [] - default_spatial_query = "{{!field f={spatial_field}}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" + default_spatial_query = "{{!field f=spatial_geom}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" spatial_query = config.get("ckanext.spatial.solr_query", default_spatial_query) diff --git a/doc/spatial-search.rst b/doc/spatial-search.rst index 8d068ebc..a06cdd18 100644 --- a/doc/spatial-search.rst +++ b/doc/spatial-search.rst @@ -148,7 +148,7 @@ details about the available options (again, you don't need to modify Solr if you * ``solr-spatial-field`` This option uses the `RPT `_ Solr field, which allows - to index points, rectangles and more complex geometries like polygons. This requires the install of the `JTS`_ library. See the linked Solr documentation for details on this. Note that it does not support spatial sorting of the returned results. + to index points, rectangles and more complex geometries like polygons. This requires the install of the `JTS`_ library. See the linked Solr documentation for details on this. Note that it does not support spatial sorting of the returned results. You will need to add the following field type and field to your Solr schema file to enable it :: @@ -168,6 +168,9 @@ details about the available options (again, you don't need to modify Solr if you + By default, the ``solr-sptatial-field`` backend uses the following query. This can be customized by setting the ``ckanext.spatial.solr_query`` configuration option, but note that all placeholders must be included:: + + "{{!field f=spatial_geom}}Intersects(ENVELOPE({minx}, {maxx}, {maxy}, {miny}))" .. note:: The old ``postgis`` search backend is deprecated and will be removed in future versions of the extension. You should migrate to one of the other backends instead but if you need to keep using it for a while see :ref:`legacy_postgis`. @@ -258,5 +261,5 @@ For adding the map to the main body, add this to the main dataset page template You need to load the ``spatial_metadata`` plugin to use these snippets. .. _action API: http://docs.ckan.org/en/latest/apiv3.html -.. _JTS: https://github.com/locationtech/jts +.. _JTS: https://github.com/locationtech/jts .. _GeoJSON: http://geojson.org From 7bb0127e26feef00292cb59a4da8ef868a093125 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 7 Dec 2022 15:50:06 +0100 Subject: [PATCH 52/66] Remove unused openlayers distribution --- .../openlayers/OpenLayers_dataset_map.js | 663 ------------------ .../openlayers/OpenLayers_wms_preview.js | 525 -------------- .../public/js/vendor/openlayers/README.txt | 30 - .../public/js/vendor/openlayers/ckan.cfg | 40 -- .../js/vendor/openlayers/ckan_dataset_map.cfg | 39 -- .../js/vendor/openlayers/ckan_wms_preview.cfg | 40 -- .../public/js/vendor/openlayers/img/blank.gif | Bin 43 -> 0 bytes .../js/vendor/openlayers/img/east-mini.png | Bin 298 -> 0 bytes .../img/layer-switcher-maximize.png | Bin 303 -> 0 bytes .../img/layer-switcher-minimize.png | Bin 367 -> 0 bytes .../js/vendor/openlayers/img/north-mini.png | Bin 298 -> 0 bytes .../js/vendor/openlayers/img/options.png | Bin 327 -> 0 bytes .../js/vendor/openlayers/img/slider.png | Bin 236 -> 0 bytes .../js/vendor/openlayers/img/south-mini.png | Bin 310 -> 0 bytes .../js/vendor/openlayers/img/west-mini.png | Bin 299 -> 0 bytes .../vendor/openlayers/img/zoom-minus-mini.png | Bin 229 -> 0 bytes .../js/vendor/openlayers/img/zoom-panel.png | Bin 641 -> 0 bytes .../vendor/openlayers/img/zoom-plus-mini.png | Bin 276 -> 0 bytes .../vendor/openlayers/img/zoom-world-mini.png | Bin 545 -> 0 bytes .../js/vendor/openlayers/img/zoombar.png | Bin 232 -> 0 bytes .../openlayers/theme/default/framedCloud.css | 0 .../openlayers/theme/default/google.css | 17 - .../openlayers/theme/default/ie6-style.css | 7 - .../vendor/openlayers/theme/default/style.css | 423 ----------- 24 files changed, 1784 deletions(-) delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/OpenLayers_dataset_map.js delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/OpenLayers_wms_preview.js delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/README.txt delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/ckan.cfg delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/ckan_dataset_map.cfg delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/ckan_wms_preview.cfg delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/blank.gif delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/east-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/layer-switcher-maximize.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/layer-switcher-minimize.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/north-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/options.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/slider.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/south-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/west-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/zoom-minus-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/zoom-panel.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/zoom-plus-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/zoom-world-mini.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/img/zoombar.png delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/theme/default/framedCloud.css delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/theme/default/google.css delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/theme/default/ie6-style.css delete mode 100644 ckanext/spatial/public/js/vendor/openlayers/theme/default/style.css diff --git a/ckanext/spatial/public/js/vendor/openlayers/OpenLayers_dataset_map.js b/ckanext/spatial/public/js/vendor/openlayers/OpenLayers_dataset_map.js deleted file mode 100644 index 0338ce1e..00000000 --- a/ckanext/spatial/public/js/vendor/openlayers/OpenLayers_dataset_map.js +++ /dev/null @@ -1,663 +0,0 @@ -/* - - OpenLayers.js -- OpenLayers Map Viewer Library - - Copyright 2005-2011 OpenLayers Contributors, released under the FreeBSD - license. Please see http://svn.openlayers.org/trunk/openlayers/license.txt - for the full text of the license. - - Includes compressed code under the following licenses: - - (For uncompressed versions of the code used please see the - OpenLayers SVN repository: ) - -*/ - -/* Contains portions of Prototype.js: - * - * Prototype JavaScript framework, version 1.4.0 - * (c) 2005 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://prototype.conio.net/ - * - *--------------------------------------------------------------------------*/ - -/** -* -* Contains portions of Rico -* -* Copyright 2005 Sabre Airline Solutions -* -* Licensed under the Apache License, Version 2.0 (the "License"); you -* may not use this file except in compliance with the License. You -* may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -* implied. See the License for the specific language governing -* permissions and limitations under the License. -* -**/ - -/** - * Contains XMLHttpRequest.js - * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - */ - -/** - * Contains portions of Gears - * - * Copyright 2007, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of Google Inc. nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Sets up google.gears.*, which is *the only* supported way to access Gears. - * - * Circumvent this file at your own risk! - * - * In the future, Gears may automatically define google.gears.* without this - * file. Gears may use these objects to transparently fix bugs and compatibility - * issues. Applications that use the code below will continue to work seamlessly - * when that happens. - */ - -/** - * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is - * Copyright (c) 2006, Yahoo! Inc. - * All rights reserved. - * - * Redistribution and use of this software in source and binary forms, with or - * without modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Yahoo! Inc. nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission of Yahoo! Inc. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/* -* The theme used for the OpenLayers controls is the "dark" theme made available -* by Development Seed under the BSD License: -* -* https://github.com/developmentseed/openlayers_themes/blob/master/LICENSE.txt -* -*/ -var OpenLayers={VERSION_NUMBER:"$Revision: 10995 $",singleFile:!0,_getScriptLocation:function(){for(var a=/(^|(.*?\/))(OpenLayers.js)(\?|$)/,b=document.getElementsByTagName("script"),c,d="",e=0,f=b.length;e<\/script>";0parseFloat(h))a.style.filter="alpha(opacity="+100*h+")",a.style.opacity=h;else if(1==parseFloat(h))a.style.filter="",a.style.opacity=""}; -OpenLayers.Util.createDiv=function(a,b,c,d,e,f,g,h){var i=document.createElement("div");if(d)i.style.backgroundImage="url("+d+")";a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="absolute");OpenLayers.Util.modifyDOMElement(i,a,b,c,e,f,g,h);return i}; -OpenLayers.Util.createImage=function(a,b,c,d,e,f,g,h){var i=document.createElement("img");a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="relative");OpenLayers.Util.modifyDOMElement(i,a,b,c,e,f,null,g);if(h)i.style.display="none",OpenLayers.Event.observe(i,"load",OpenLayers.Function.bind(OpenLayers.Util.onImageLoad,i)),OpenLayers.Event.observe(i,"error",OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError,i));i.style.alt=a;i.galleryImg="no";if(d)i.src=d;return i}; -OpenLayers.Util.setOpacity=function(a,b){OpenLayers.Util.modifyDOMElement(a,null,null,null,null,null,null,b)};OpenLayers.Util.onImageLoad=function(){if(!this.viewRequestID||this.map&&this.viewRequestID==this.map.viewRequestID)this.style.display="";OpenLayers.Element.removeClass(this,"olImageLoadError")};OpenLayers.IMAGE_RELOAD_ATTEMPTS=0; -OpenLayers.Util.onImageLoadError=function(){this._attempts=this._attempts?this._attempts+1:1;if(this._attempts<=OpenLayers.IMAGE_RELOAD_ATTEMPTS){var a=this.urls;if(a&&a instanceof Array&&1d++;)e=Math.floor(a.length*Math.random()),e=a[e];this.src=b.replace(c,e)}else this.src=this.src}else OpenLayers.Element.addClass(this,"olImageLoadError");this.style.display= -""};OpenLayers.Util.alphaHackNeeded=null;OpenLayers.Util.alphaHack=function(){if(null==OpenLayers.Util.alphaHackNeeded){var a=navigator.appVersion.split("MSIE"),a=parseFloat(a[1]),b=!1;try{b=!!document.body.filters}catch(c){}OpenLayers.Util.alphaHackNeeded=b&&5.5<=a&&7>a}return OpenLayers.Util.alphaHackNeeded}; -OpenLayers.Util.modifyAlphaImageDiv=function(a,b,c,d,e,f,g,h,i){OpenLayers.Util.modifyDOMElement(a,b,c,d,f,null,null,i);b=a.childNodes[0];if(e)b.src=e;OpenLayers.Util.modifyDOMElement(b,a.id+"_innerImage",null,d,"relative",g);if(OpenLayers.Util.alphaHack()){if("none"!=a.style.display)a.style.display="inline-block";null==h&&(h="scale");a.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+b.src+"', sizingMethod='"+h+"')";0<=parseFloat(a.style.opacity)&&1>parseFloat(a.style.opacity)&& -(a.style.filter+=" alpha(opacity="+100*a.style.opacity+")");b.style.filter="alpha(opacity=0)"}}; -OpenLayers.Util.createAlphaImageDiv=function(a,b,c,d,e,f,g,h,i){var j=OpenLayers.Util.createDiv(),k=OpenLayers.Util.createImage(null,null,null,null,null,null,null,!1);j.appendChild(k);if(i)k.style.display="none",OpenLayers.Event.observe(k,"load",OpenLayers.Function.bind(OpenLayers.Util.onImageLoad,j)),OpenLayers.Event.observe(k,"error",OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError,j));OpenLayers.Util.modifyAlphaImageDiv(j,a,b,c,d,e,f,g,h);return j}; -OpenLayers.Util.upperCaseObject=function(a){var b={},c;for(c in a)b[c.toUpperCase()]=a[c];return b};OpenLayers.Util.applyDefaults=function(a,b){var a=a||{},c="function"==typeof window.Event&&b instanceof window.Event,d;for(d in b)if(void 0===a[d]||!c&&b.hasOwnProperty&&b.hasOwnProperty(d)&&!a.hasOwnProperty(d))a[d]=b[d];if(!c&&b&&b.hasOwnProperty&&b.hasOwnProperty("toString")&&!a.hasOwnProperty("toString"))a.toString=b.toString;return a}; -OpenLayers.Util.getParameterString=function(a){var b=[],c;for(c in a){var d=a[c];if(null!=d&&"function"!=typeof d){if("object"==typeof d&&d.constructor==Array){for(var e=[],f,g=0,h=d.length;ge&&(e="0"+e);e+="\u00b0";0<=c.indexOf("dm")&&(10>d&&(d="0"+d),e+=d+"'",0<=c.indexOf("dms")&&(10>f&&(f="0"+f),e+=f+'"'));return e="lon"==b?e+(0>a?OpenLayers.i18n("W"):OpenLayers.i18n("E")):e+(0>a?OpenLayers.i18n("S"):OpenLayers.i18n("N"))}; -OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},userError:function(a){alert(a)},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"}; -(function(){for(var a=document.getElementsByTagName("script"),b=0,c=a.length;bthis.right)this.right=b.right;if(null==this.top||b.top> -this.top)this.top=b.top}}},containsLonLat:function(a,b){return this.contains(a.lon,a.lat,b)},containsPixel:function(a,b){return this.contains(a.x,a.y,b)},contains:function(a,b,c){null==c&&(c=!0);if(null==a||null==b)return!1;var a=OpenLayers.Util.toFloat(a),b=OpenLayers.Util.toFloat(b),d=!1;return d=c?a>=this.left&&a<=this.right&&b>=this.bottom&&b<=this.top:a>this.left&&athis.bottom&&b=this.bottom&&a.top<=this.top||this.top>a.bottom&&this.top=this.left&&a.left<=this.right||this.left>=a.left&&this.left<=a.right,e=a.right>=this.left&&a.right<=this.right||this.right>=a.left&&this.right<=a.right,c=(a.bottom>=this.bottom&&a.bottom<=this.top||this.bottom>=a.bottom&&this.bottom<=a.top||c)&&(d||e);return c},containsBounds:function(a,b,c){null==b&&(b=!1);null==c&&(c=!0);var d=this.contains(a.left,a.bottom, -c),e=this.contains(a.right,a.bottom,c),f=this.contains(a.left,a.top,c),a=this.contains(a.right,a.top,c);return b?d||e||f||a:d&&e&&f&&a},determineQuadrant:function(a){var b="",c=this.getCenterLonLat(),b=b+(a.lat=a.right&&e.right>a.right;)e=e.add(-a.getWidth(),0)}return e},CLASS_NAME:"OpenLayers.Bounds"}); -OpenLayers.Bounds.fromString=function(a,b){var c=a.split(",");return OpenLayers.Bounds.fromArray(c,b)};OpenLayers.Bounds.fromArray=function(a,b){return!0===b?new OpenLayers.Bounds(parseFloat(a[1]),parseFloat(a[0]),parseFloat(a[3]),parseFloat(a[2])):new OpenLayers.Bounds(parseFloat(a[0]),parseFloat(a[1]),parseFloat(a[2]),parseFloat(a[3]))};OpenLayers.Bounds.fromSize=function(a){return new OpenLayers.Bounds(0,a.h,a.w,0)}; -OpenLayers.Bounds.oppositeQuadrant=function(a){var b;b=""+("t"==a.charAt(0)?"b":"t");return b+="l"==a.charAt(1)?"r":"l"}; -OpenLayers.Element={visible:function(a){return"none"!=OpenLayers.Util.getElement(a).style.display},toggle:function(){for(var a=0,b=arguments.length;aa.right;)b.lon-=a.getWidth()}return b},CLASS_NAME:"OpenLayers.LonLat"}); -OpenLayers.LonLat.fromString=function(a){a=a.split(",");return new OpenLayers.LonLat(a[0],a[1])}; -OpenLayers.Pixel=OpenLayers.Class({x:0,y:0,initialize:function(a,b){this.x=parseFloat(a);this.y=parseFloat(b)},toString:function(){return"x="+this.x+",y="+this.y},clone:function(){return new OpenLayers.Pixel(this.x,this.y)},equals:function(a){var b=!1;null!=a&&(b=this.x==a.x&&this.y==a.y||isNaN(this.x)&&isNaN(this.y)&&isNaN(a.x)&&isNaN(a.y));return b},distanceTo:function(a){return Math.sqrt(Math.pow(this.x-a.x,2)+Math.pow(this.y-a.y,2))},add:function(a,b){if(null==a||null==b){var c=OpenLayers.i18n("pixelAddError"); -OpenLayers.Console.error(c);return null}return new OpenLayers.Pixel(this.x+a,this.y+b)},offset:function(a){var b=this.clone();a&&(b=this.add(a.x,a.y));return b},CLASS_NAME:"OpenLayers.Pixel"}); -OpenLayers.Size=OpenLayers.Class({w:0,h:0,initialize:function(a,b){this.w=parseFloat(a);this.h=parseFloat(b)},toString:function(){return"w="+this.w+",h="+this.h},clone:function(){return new OpenLayers.Size(this.w,this.h)},equals:function(a){var b=!1;null!=a&&(b=this.w==a.w&&this.h==a.h||isNaN(this.w)&&isNaN(this.h)&&isNaN(a.w)&&isNaN(a.h));return b},CLASS_NAME:"OpenLayers.Size"}); -OpenLayers.Feature=OpenLayers.Class({layer:null,id:null,lonlat:null,data:null,marker:null,popupClass:null,popup:null,initialize:function(a,b,c){this.layer=a;this.lonlat=b;this.data=null!=c?c:{};this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){null!=this.layer&&null!=this.layer.map&&null!=this.popup&&this.layer.map.removePopup(this.popup);null!=this.layer&&null!=this.marker&&this.layer.removeMarker(this.marker);this.data=this.lonlat=this.id=this.layer=null;if(null!=this.marker)this.destroyMarker(this.marker), -this.marker=null;if(null!=this.popup)this.destroyPopup(this.popup),this.popup=null},onScreen:function(){var a=!1;null!=this.layer&&null!=this.layer.map&&(a=this.layer.map.getExtent().containsLonLat(this.lonlat));return a},createMarker:function(){if(null!=this.lonlat)this.marker=new OpenLayers.Marker(this.lonlat,this.data.icon);return this.marker},destroyMarker:function(){this.marker.destroy()},createPopup:function(a){if(null!=this.lonlat){if(!this.popup)this.popup=new (this.popupClass?this.popupClass: -OpenLayers.Popup.AnchoredBubble)(this.id+"_popup",this.lonlat,this.data.popupSize,this.data.popupContentHTML,this.marker?this.marker.icon:null,a);if(null!=this.data.overflow)this.popup.contentDiv.style.overflow=this.data.overflow;this.popup.feature=this}return this.popup},destroyPopup:function(){if(this.popup)this.popup.feature=null,this.popup.destroy(),this.popup=null},CLASS_NAME:"OpenLayers.Feature"});OpenLayers.State={UNKNOWN:"Unknown",INSERT:"Insert",UPDATE:"Update",DELETE:"Delete"}; -OpenLayers.Feature.Vector=OpenLayers.Class(OpenLayers.Feature,{fid:null,geometry:null,attributes:null,bounds:null,state:null,style:null,url:null,renderIntent:"default",initialize:function(a,b,c){OpenLayers.Feature.prototype.initialize.apply(this,[null,null,b]);this.lonlat=null;this.geometry=a?a:null;this.state=null;this.attributes={};if(b)this.attributes=OpenLayers.Util.extend(this.attributes,b);this.style=c?c:null},destroy:function(){if(this.layer)this.layer.removeFeatures(this),this.layer=null; -this.geometry=null;OpenLayers.Feature.prototype.destroy.apply(this,arguments)},clone:function(){return new OpenLayers.Feature.Vector(this.geometry?this.geometry.clone():null,this.attributes,this.style)},onScreen:function(a){var b=!1;this.layer&&this.layer.map&&(b=this.layer.map.getExtent(),a?(a=this.geometry.getBounds(),b=b.intersectsBounds(a)):b=b.toGeometry().intersects(this.geometry));return b},getVisibility:function(){return!(this.style&&"none"==this.style.display||!this.layer||this.layer&&this.layer.styleMap&& -"none"==this.layer.styleMap.createSymbolizer(this,this.renderIntent).display||this.layer&&!this.layer.getVisibility())},createMarker:function(){return null},destroyMarker:function(){},createPopup:function(){return null},atPoint:function(a,b,c){var d=!1;this.geometry&&(d=this.geometry.atPoint(a,b,c));return d},destroyPopup:function(){},move:function(a){if(this.layer&&this.geometry.move){var a="OpenLayers.LonLat"==a.CLASS_NAME?this.layer.getViewPortPxFromLonLat(a):a,b=this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat()), -c=this.layer.map.getResolution();this.geometry.move(c*(a.x-b.x),c*(b.y-a.y));this.layer.drawFeature(this);return b}},toState:function(a){if(a==OpenLayers.State.UPDATE)switch(this.state){case OpenLayers.State.UNKNOWN:case OpenLayers.State.DELETE:this.state=a}else if(a==OpenLayers.State.INSERT)switch(this.state){case OpenLayers.State.UNKNOWN:break;default:this.state=a}else if(a==OpenLayers.State.DELETE)switch(this.state){case OpenLayers.State.UNKNOWN:case OpenLayers.State.UPDATE:this.state=a}else if(a== -OpenLayers.State.UNKNOWN)this.state=a},CLASS_NAME:"OpenLayers.Feature.Vector"}); -OpenLayers.Feature.Vector.style={"default":{fillColor:"#ee9900",fillOpacity:0.4,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"#ee9900",strokeOpacity:1,strokeWidth:1,strokeLinecap:"round",strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"inherit"},select:{fillColor:"blue",fillOpacity:0.4,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"blue",strokeOpacity:1, -strokeWidth:2,strokeLinecap:"round",strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"pointer"},temporary:{fillColor:"#66cccc",fillOpacity:0.2,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"#66cccc",strokeOpacity:1,strokeLinecap:"round",strokeWidth:2,strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1, -hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"inherit"},"delete":{display:"none"}}; -OpenLayers.Style=OpenLayers.Class({id:null,name:null,title:null,description:null,layerName:null,isDefault:!1,rules:null,context:null,defaultStyle:null,defaultsPerSymbolizer:!1,propertyStyles:null,initialize:function(a,b){OpenLayers.Util.extend(this,b);this.rules=[];b&&b.rules&&this.addRules(b.rules);this.setDefaultStyle(a||OpenLayers.Feature.Vector.style["default"]);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){for(var a=0,b=this.rules.length;athis.maxZIndex)this.maxZIndex=b},getNextElement:function(a){a+=1;if(a=2*a[1]?"longdash":1==a[0]||1==a[1]?"dot":"dash":4==a.length?1*a[0]>=2*a[1]?"longdashdot":"dashdot":"solid"}},createNode:function(a,b){var c=document.createElement(a);if(b)c.id=b;c.unselectable="on";c.onselectstart=OpenLayers.Function.False;return c},nodeTypeCompare:function(a,b){var c=b,d=c.indexOf(":");-1!=d&&(c=c.substr(d+1));var e=a.nodeName,d=e.indexOf(":");-1!=d&&(e=e.substr(d+1));return c==e},createRenderRoot:function(){return this.nodeFactory(this.container.id+ -"_vmlRoot","div")},createRoot:function(a){return this.nodeFactory(this.container.id+a,"olv:group")},drawPoint:function(a,b){return this.drawCircle(a,b,1)},drawCircle:function(a,b,c){if(!isNaN(b.x)&&!isNaN(b.y)){var d=this.getResolution();a.style.left=(b.x/d-this.offset.x|0)-c+"px";a.style.top=(b.y/d-this.offset.y|0)-c+"px";b=2*c;a.style.width=b+"px";a.style.height=b+"px";return a}return!1},drawLineString:function(a,b){return this.drawLine(a,b,!1)},drawLinearRing:function(a,b){return this.drawLine(a, -b,!0)},drawLine:function(a,b,c){this.setNodeDimension(a,b);for(var d=this.getResolution(),e=b.components.length,f=Array(e),g,h,i=0;ithis.duration&&this.stop()},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(a,b,c,d){return c*a/d+b},easeOut:function(a,b,c,d){return c*a/d+b},easeInOut:function(a,b,c,d){return c*a/d+b},CLASS_NAME:"OpenLayers.Easing.Linear"}; -OpenLayers.Easing.Expo={easeIn:function(a,b,c,d){return 0==a?b:c*Math.pow(2,10*(a/d-1))+b},easeOut:function(a,b,c,d){return a==d?b+c:c*(-Math.pow(2,-10*a/d)+1)+b},easeInOut:function(a,b,c,d){return 0==a?b:a==d?b+c:1>(a/=d/2)?c/2*Math.pow(2,10*(a-1))+b:c/2*(-Math.pow(2,-10*--a)+2)+b},CLASS_NAME:"OpenLayers.Easing.Expo"}; -OpenLayers.Easing.Quad={easeIn:function(a,b,c,d){return c*(a/=d)*a+b},easeOut:function(a,b,c,d){return-c*(a/=d)*(a-2)+b},easeInOut:function(a,b,c,d){return 1>(a/=d/2)?c/2*a*a+b:-c/2*(--a*(a-2)-1)+b},CLASS_NAME:"OpenLayers.Easing.Quad"}; -OpenLayers.Format=OpenLayers.Class({options:null,externalProjection:null,internalProjection:null,data:null,keepData:!1,initialize:function(a){OpenLayers.Util.extend(this,a);this.options=a},destroy:function(){},read:function(){OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"))},write:function(){OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"))},CLASS_NAME:"OpenLayers.Format"}); -OpenLayers.Format.WKT=OpenLayers.Class(OpenLayers.Format,{initialize:function(a){this.regExes={typeStr:/^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,spaces:/\s+/,parenComma:/\)\s*,\s*\(/,doubleParenComma:/\)\s*\)\s*,\s*\(\s*\(/,trimParens:/^\s*\(?(.*?)\)?\s*$/};OpenLayers.Format.prototype.initialize.apply(this,[a])},read:function(a){var b,c;if(c=this.regExes.typeStr.exec(a))if(a=c[1].toLowerCase(),c=c[2],this.parse[a]&&(b=this.parse[a].apply(this,[c])),this.internalProjection&&this.externalProjection)if(b&&"OpenLayers.Feature.Vector"== -b.CLASS_NAME)b.geometry.transform(this.externalProjection,this.internalProjection);else if(b&&"geometrycollection"!=a&&"object"==typeof b){a=0;for(c=b.length;a=f&&0<=o&&1>=o&&(d?(h=a.x1+f*h,o=a.y1+f*i,e=new OpenLayers.Geometry.Point(h,o)):e=!0));if(c)if(e){if(d){a=[a,b];b=0;a:for(;2>b;++b){f=a[b];for(i=1;3>i;++i)if(h=f["x"+i],o=f["y"+i],d=Math.sqrt(Math.pow(h-e.x,2)+Math.pow(o-e.y,2)),db;++b){h=a[b];o=a[(b+1)%2];for(i=1;3>i;++i)if(f={x:h["x"+i],y:h["y"+i]},g=OpenLayers.Geometry.distanceToSegment(f,o),g.distance=k||(1<=k?(e=g,f=h):(e+=k*i,f+=k*j));return{distance:Math.sqrt(Math.pow(e-c,2)+Math.pow(f-d,2)),x:e,y:f}}; -OpenLayers.Geometry.Collection=OpenLayers.Class(OpenLayers.Geometry,{components:null,componentTypes:null,initialize:function(a){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.components=[];null!=a&&this.addComponents(a)},destroy:function(){this.components.length=0;this.components=null;OpenLayers.Geometry.prototype.destroy.apply(this,arguments)},clone:function(){for(var a=eval("new "+this.CLASS_NAME+"()"),b=0,c=this.components.length;bf)break;if(!(i.x2Math.max(g,h))&&!(Math.max(j,k)h&&(i>j.y1&&ij.y2))break;e=c?{distance:e.distance,x0:e.x,y0:e.y,x1:h,y1:i}:e.distance}else if(a instanceof OpenLayers.Geometry.LineString){var g=this.getSortedSegments(),h=a.getSortedSegments(),n,l,m=h.length,r={point:!0}, -k=0,o=g.length;a:for(;kb.length)return this;var c=function(a,b,d,i){for(var j=0,k=0,o=b,n;oj&&(j=n,k=o)}j>i&&k!=b&&(e.push(k),c(a,b,k,i),c(a,k,d,i))},d=b.length-1,e=[];e.push(0);for(e.push(d);b[0].equals(b[d]);)d--, -e.push(d);c(b,0,d,a);a=[];e.sort(function(a,b){return a-b});for(d=0;d=g&&c<=h||g>=h&&c<=g&&c>=h)){j=-1;break}}else{i=b(((g-h)*a+(h*e-g*f))/(e-f),14);if(i==c&&(e=e&&a<=f||e>f&&a<=e&&a>=f)){j=-1;break}i<=c||g!=h&&(iMath.max(g, -h))||(e=e&&af&&a=f)&&++j}return-1==j?1:!!(j&1)},intersects:function(a){var b=!1;if("OpenLayers.Geometry.Point"==a.CLASS_NAME)b=this.containsPoint(a);else if("OpenLayers.Geometry.LineString"==a.CLASS_NAME)b=a.intersects(this);else if("OpenLayers.Geometry.LinearRing"==a.CLASS_NAME)b=OpenLayers.Geometry.LineString.prototype.intersects.apply(this,[a]);else for(var c=0,d=a.components.length;cb.status)this.events.triggerEvent("success",a),e&&e(b);if(b.status&&(200>b.status||300<=b.status))this.events.triggerEvent("failure",a),f&&f(b)},GET:function(a){a=OpenLayers.Util.extend(a,{method:"GET"});return OpenLayers.Request.issue(a)}, -POST:function(a){a=OpenLayers.Util.extend(a,{method:"POST"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},PUT:function(a){a=OpenLayers.Util.extend(a,{method:"PUT"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},DELETE:function(a){a=OpenLayers.Util.extend(a, -{method:"DELETE"});return OpenLayers.Request.issue(a)},HEAD:function(a){a=OpenLayers.Util.extend(a,{method:"HEAD"});return OpenLayers.Request.issue(a)},OPTIONS:function(a){a=OpenLayers.Util.extend(a,{method:"OPTIONS"});return OpenLayers.Request.issue(a)}}; -(function(){function a(){this._object=f&&!i?new f:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function b(){return new a}function c(a){b.onreadystatechange&&b.onreadystatechange.apply(a);a.dispatchEvent({type:"readystatechange",bubbles:!1,cancelable:!1,timeStamp:new Date+0})}function d(a){try{a.responseText=a._object.responseText}catch(b){}try{var c=a._object,d=c.responseXML,e=c.responseText;if(h&&e&&d&&!d.documentElement&&c.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/))d= -new window.ActiveXObject("Microsoft.XMLDOM"),d.async=!1,d.validateOnParse=!1,d.loadXML(e);a.responseXML=d&&(h&&0!=d.parseError||!d.documentElement||d.documentElement&&"parsererror"==d.documentElement.tagName)?null:d}catch(f){}try{a.status=a._object.status}catch(g){}try{a.statusText=a._object.statusText}catch(i){}}function e(a){a._object.onreadystatechange=new window.Function}var f=window.XMLHttpRequest,g=!!window.controllers,h=window.document.all&&!window.opera,i=h&&window.navigator.userAgent.match(/MSIE 7.0/); -b.prototype=a.prototype;if(g&&f.wrapped)b.wrapped=f.wrapped;b.UNSENT=0;b.OPENED=1;b.HEADERS_RECEIVED=2;b.LOADING=3;b.DONE=4;b.prototype.readyState=b.UNSENT;b.prototype.responseText="";b.prototype.responseXML=null;b.prototype.status=0;b.prototype.statusText="";b.prototype.priority="NORMAL";b.prototype.onreadystatechange=null;b.onreadystatechange=null;b.onopen=null;b.onsend=null;b.onabort=null;b.prototype.open=function(a,f,i,n,l){delete this._headers;3>arguments.length&&(i=!0);this._async=i;var m=this, -r=this.readyState,p;h&&i&&(p=function(){r!=b.DONE&&(e(m),m.abort())},window.attachEvent("onunload",p));b.onopen&&b.onopen.apply(this,arguments);4b.UNSENT)this._aborted=!0;this._object.abort();e(this);this.readyState=b.UNSENT;delete this._data};b.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};b.prototype.getResponseHeader=function(a){return this._object.getResponseHeader(a)};b.prototype.setRequestHeader=function(a,b){if(!this._headers)this._headers={};this._headers[a]=b;return this._object.setRequestHeader(a, -b)};b.prototype.addEventListener=function(a,b,c){for(var d=0,e;e=this._listeners[d];d++)if(e[0]==a&&e[1]==b&&e[2]==c)return;this._listeners.push([a,b,c])};b.prototype.removeEventListener=function(a,b,c){for(var d=0,e;(e=this._listeners[d])&&!(e[0]==a&&e[1]==b&&e[2]==c);d++);e&&this._listeners.splice(d,1)};b.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){}, -initEvent:function(){}};"readystatechange"==a.type&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var b=0,c;c=this._listeners[b];b++)c[0]==a.type&&!c[2]&&(c[1].handleEvent||c[1]).apply(this,[a])};b.prototype.toString=function(){return"[object XMLHttpRequest]"};b.toString=function(){return"[XMLHttpRequest]"};if(!window.Function.prototype.apply)window.Function.prototype.apply=function(a,b){b||(b=[]);a.__func=this;a.__func(b[0],b[1],b[2], -b[3],b[4]);delete a.__func};OpenLayers.Request.XMLHttpRequest=b})();OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(a){OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest",{statusText:a.statusText}))};OpenLayers.loadURL=function(a,b,c,d,e){"string"==typeof b&&(b=OpenLayers.Util.getParameters(b));return OpenLayers.Request.GET({url:a,params:b,success:d?d:OpenLayers.nullHandler,failure:e?e:OpenLayers.nullHandler,scope:c})}; -OpenLayers.parseXMLString=function(a){var b=a.indexOf("<");0(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]))a.Connection="close";if("object"==typeof this.options.requestHeaders){var b=this.options.requestHeaders;if("function"==typeof b.push)for(var c=0,d=b.length;c< -d;c+=2)a[b[c]]=b[c+1];else for(c in b)a[c]=b[c]}for(var e in a)this.transport.setRequestHeader(e,a[e])},success:function(){var a=this.getStatus();return!a||200<=a&&300>a},getStatus:function(){try{return this.transport.status||0}catch(a){return 0}},respondToReadyState:function(a){var a=OpenLayers.Ajax.Request.Events[a],b=new OpenLayers.Ajax.Response(this);if("Complete"==a){try{this._complete=!0,(this.options["on"+b.status]||this.options["on"+(this.success()?"Success":"Failure")]||OpenLayers.Ajax.emptyFunction)(b)}catch(c){this.dispatchException(c)}b.getHeader("Content-type")}try{(this.options["on"+ -a]||OpenLayers.Ajax.emptyFunction)(b),OpenLayers.Ajax.Responders.dispatch("on"+a,this,b)}catch(d){this.dispatchException(d)}if("Complete"==a)this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction},getHeader:function(a){try{return this.transport.getResponseHeader(a)}catch(b){return null}},dispatchException:function(a){var b=this.options.onException;if(b)b(this,a),OpenLayers.Ajax.Responders.dispatch("onException",this,a);else{for(var b=!1,c=OpenLayers.Ajax.Responders.responders,d=0;db)b=0;else if(b>this.layers.length)b=this.layers.length;if(c!=b){this.layers.splice(c,1);this.layers.splice(b,0,a);for(var c=0,d=this.layers.length;c=this.minPx.y+i;d=this.minPx.x;f=this.maxPx.x;g||(c=c&&e<=this.maxPx.x-h&&e>=this.minPx.x+h);if(c){if(!this.dragging)this.dragging=!0,this.events.triggerEvent("movestart");this.center=null;if(a)this.layerContainerDiv.style.left=parseInt(this.layerContainerDiv.style.left)- -a+"px",this.minPx.x-=a,this.maxPx.x-=a,g&&(this.maxPx.x>f&&(this.maxPx.x-=f-d),this.minPx.xthis.restrictedExtent.getWidth()?a=new OpenLayers.LonLat(g.lon,a.lat):f.leftthis.restrictedExtent.right&&(a=a.add(this.restrictedExtent.right-f.right,0));f.getHeight()>this.restrictedExtent.getHeight()?a=new OpenLayers.LonLat(a.lon,g.lat):f.bottomthis.restrictedExtent.top&&(a=a.add(0,this.restrictedExtent.top-f.top))}}e=e||this.isValidZoomLevel(b)&&b!=this.getZoom();f=this.isValidLonLat(a)&&!a.equals(this.center);if(e||f||d){d||this.events.triggerEvent("movestart");if(f)!e&& -this.center&&this.centerLayerContainer(a),this.center=a.clone();a=e?this.getResolutionForZoom(b):this.getResolution();if(e||null==this.layerContainerOrigin){this.layerContainerOrigin=this.getCachedCenter();this.layerContainerDiv.style.left="0px";this.layerContainerDiv.style.top="0px";var h=this.getMaxExtent({restricted:!0}),f=h.getCenterLonLat(),g=this.center.lon-f.lon,i=f.lat-this.center.lat,f=Math.round(h.getWidth()/a),h=Math.round(h.getHeight()/a),g=(this.size.w-f)/2-g/a,i=(this.size.h-h)/2-i/ -a;this.minPx=new OpenLayers.Pixel(g,i);this.maxPx=new OpenLayers.Pixel(g+f,i+h)}if(e)this.zoom=b,this.resolution=a,this.viewRequestID++;a=this.getExtent();this.baseLayer.visibility&&(this.baseLayer.moveTo(a,e,c.dragging),c.dragging||this.baseLayer.events.triggerEvent("moveend",{zoomChanged:e}));a=this.baseLayer.getExtent();for(b=this.layers.length-1;0<=b;--b)if(f=this.layers[b],f!==this.baseLayer&&!f.isBaseLayer){g=f.calculateInRange();if(f.inRange!=g)(f.inRange=g)||f.display(!1),this.events.triggerEvent("changelayer", -{layer:f,property:"visibility"});g&&f.visibility&&(f.moveTo(a,e,c.dragging),c.dragging||f.events.triggerEvent("moveend",{zoomChanged:e}))}this.events.triggerEvent("move");d||this.events.triggerEvent("moveend");if(e){b=0;for(c=this.popups.length;b=this.getRestrictedMinZoom()&&a=this.restrictedMinZoom&&a>=this.minResolution&&a<=this.maxResolution);return a},setIsBaseLayer:function(a){if(a!=this.isBaseLayer)this.isBaseLayer=a,null!=this.map&&this.map.events.triggerEvent("changebaselayer",{layer:this})},initResolutions:function(){var a,b,c,d={},e=!0;for(a=0,b=this.RESOLUTION_PROPERTIES.length;a=a||"number"!==typeof d&&"number"!==typeof c)){b=Array(a);var e=2;"number"==typeof c&&"number"==typeof d&&(e=Math.pow(d/c,1/(a-1)));var f;if("number"===typeof d)for(f=0;f=a&&(f=h,e=c),h<=a){g=h;break}c=f-g;c=0f)break;f=e}else if(this.resolutions[c]=a.bottom-f*this.buffer||o-this.tileSize.w* -(b-1)?this.shiftColumn(!0):c.x<-this.tileSize.w*b?this.shiftColumn(!1):c.y>-this.tileSize.h*(b-1)?this.shiftRow(!0):c.y<-this.tileSize.h*b?this.shiftRow(!1):a=!1;if(a)this.timerId=window.setTimeout(this._moveGriddedTiles,0)},shiftRow:function(a){for(var b=this.grid,c=b[a?0:this.grid.length-1],d=this.map.getResolution(),e=a?-this.tileSize.h:this.tileSize.h,d=d*-e,f=a?b.pop():b.shift(),g=0,h=c.length;ga;)for(var c=this.grid.pop(),d=0,e=c.length;db;){d=0;for(e=this.grid.length;da?"0"+a:a}return'"'+a.getFullYear()+"-"+ -b(a.getMonth()+1)+"-"+b(a.getDate())+"T"+b(a.getHours())+":"+b(a.getMinutes())+":"+b(a.getSeconds())+'"'}},CLASS_NAME:"OpenLayers.Format.JSON"}); -OpenLayers.Control.Panel=OpenLayers.Class(OpenLayers.Control,{controls:null,autoActivate:!0,defaultControl:null,saveState:!1,allowDepress:!1,activeState:null,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,[a]);this.controls=[];this.activeState={}},destroy:function(){OpenLayers.Control.prototype.destroy.apply(this,arguments);for(var a,b=this.controls.length-1;0<=b;b--)a=this.controls[b],a.events&&a.events.un({activate:this.iconOn,deactivate:this.iconOff}),OpenLayers.Event.stopObservingElement(a.panel_div), -a.panel_div=null;this.activeState=null},activate:function(){if(OpenLayers.Control.prototype.activate.apply(this,arguments)){for(var a,b=0,c=this.controls.length;b=this.down.xy.distanceTo(a.xy))&&this.touch&&this.down.touches.length===this.last.touches.length)for(var a=0,c=this.down.touches.length;athis.pixelTolerance){b=!1;break}return b},getTouchDistance:function(a,b){return Math.sqrt(Math.pow(a.clientX-b.clientX,2)+Math.pow(a.clientY-b.clientY,2))},passesDblclickTolerance:function(){var a=!0;this.down&&this.first&&(a=this.down.xy.distanceTo(this.first.xy)<=this.dblclickTolerance);return a},clearTimer:function(){if(null!=this.timerId)window.clearTimeout(this.timerId),this.timerId=null;if(null!=this.rightclickTimerId)window.clearTimeout(this.rightclickTimerId),this.rightclickTimerId= -null},delayedCall:function(a){this.timerId=null;a&&this.callback("click",[a])},getEventInfo:function(a){var b;if(a.touches){var c=a.touches.length;b=Array(c);for(var d,e=0;ea.lon)0>b.lon?b.lon=-180-(b.lon+180):a.lon=180+a.lon+180;return new OpenLayers.Bounds(b.lon,a.lat,a.lon,b.lat)},showTile:function(){this.shouldDraw&& -this.show()},show:function(){},hide:function(){},CLASS_NAME:"OpenLayers.Tile"}); -OpenLayers.Tile.Image=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,layerAlphaHack:null,isBackBuffer:!1,isFirstDraw:!0,backBufferTile:null,maxGetUrlLength:null,initialize:function(a,b,c,d,e,f){OpenLayers.Tile.prototype.initialize.apply(this,arguments);null!=this.maxGetUrlLength&&OpenLayers.Util.extend(this,OpenLayers.Tile.Image.IFrame);this.url=d;this.frame=document.createElement("div");this.frame.style.overflow="hidden";this.frame.style.position="absolute";this.layerAlphaHack= -this.layer.alpha&&OpenLayers.Util.alphaHack()},destroy:function(){null!=this.imgDiv&&this.removeImgDiv();this.imgDiv=null;null!=this.frame&&this.frame.parentNode==this.layer.div&&this.layer.div.removeChild(this.frame);this.frame=null;if(this.backBufferTile)this.backBufferTile.destroy(),this.backBufferTile=null;this.layer.events.unregister("loadend",this,this.resetBackBuffer);OpenLayers.Tile.prototype.destroy.apply(this,arguments)},clone:function(a){null==a&&(a=new OpenLayers.Tile.Image(this.layer, -this.position,this.bounds,this.url,this.size));a=OpenLayers.Tile.prototype.clone.apply(this,[a]);a.imgDiv=null;return a},draw:function(){if(this.layer!=this.layer.map.baseLayer&&this.layer.reproject)this.bounds=this.getBoundsFromBaseLayer(this.position);var a=OpenLayers.Tile.prototype.draw.apply(this,arguments);if(-1!=OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)||this.layer.singleTile)if(a){if(!this.backBufferTile)this.backBufferTile=this.clone(),this.backBufferTile.hide(), -this.backBufferTile.isBackBuffer=!0,this.events.register("loadend",this,this.resetBackBuffer),this.layer.events.register("loadend",this,this.resetBackBuffer);this.startTransition()}else this.backBufferTile&&this.backBufferTile.clear();else if(a&&this.isFirstDraw)this.events.register("loadend",this,this.showTile),this.isFirstDraw=!1;if(!a)return!1;this.isLoading?this.events.triggerEvent("reload"):(this.isLoading=!0,this.events.triggerEvent("loadstart"));return this.renderTile()},resetBackBuffer:function(){this.showTile(); -if(this.backBufferTile&&(this.isFirstDraw||!this.layer.numLoadingTiles)){this.isFirstDraw=!1;var a=this.layer.maxExtent;if(a&&this.bounds.intersectsBounds(a,!1))this.backBufferTile.position=this.position,this.backBufferTile.bounds=this.bounds,this.backBufferTile.size=this.size,this.backBufferTile.imageSize=this.layer.getImageSize(this.bounds)||this.size,this.backBufferTile.imageOffset=this.layer.imageOffset,this.backBufferTile.resolution=this.layer.getResolution(),this.backBufferTile.renderTile(); -this.backBufferTile.hide()}},renderTile:function(){this.layer.async?(this.initImgDiv(),this.layer.getURLasync(this.bounds,this,"url",this.positionImage)):(this.url=this.layer.getURL(this.bounds),this.initImgDiv(),this.positionImage());return!0},positionImage:function(){if(null!==this.layer){OpenLayers.Util.modifyDOMElement(this.frame,null,this.position,this.size);var a=this.layer.getImageSize(this.bounds);this.layerAlphaHack?OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,null,null,a,this.url):(OpenLayers.Util.modifyDOMElement(this.imgDiv, -null,null,a),this.imgDiv.src=this.url)}},clear:function(){if(this.imgDiv&&(this.hide(),OpenLayers.Tile.Image.useBlankTile))this.imgDiv.src=OpenLayers.Util.getImagesLocation()+"blank.gif"},initImgDiv:function(){if(null==this.imgDiv){var a=this.layer.imageOffset,b=this.layer.getImageSize(this.bounds);this.imgDiv=this.layerAlphaHack?OpenLayers.Util.createAlphaImageDiv(null,a,b,null,"relative",null,null,null,!0):OpenLayers.Util.createImage(null,a,b,null,"relative",null,null,!0);if(this.layer.url instanceof -Array)this.imgDiv.urls=this.layer.url.slice();this.imgDiv.className="olTileImage";this.frame.style.zIndex=this.isBackBuffer?0:1;this.frame.appendChild(this.imgDiv);this.layer.div.appendChild(this.frame);null!=this.layer.opacity&&OpenLayers.Util.modifyDOMElement(this.imgDiv,null,null,null,null,null,null,this.layer.opacity);this.imgDiv.map=this.layer.map;var c=function(){if(this.isLoading)this.isLoading=!1,this.events.triggerEvent("loadend")};this.layerAlphaHack?OpenLayers.Event.observe(this.imgDiv.childNodes[0], -"load",OpenLayers.Function.bind(c,this)):OpenLayers.Event.observe(this.imgDiv,"load",OpenLayers.Function.bind(c,this));OpenLayers.Event.observe(this.imgDiv,"error",OpenLayers.Function.bind(function(){this.imgDiv._attempts>OpenLayers.IMAGE_RELOAD_ATTEMPTS&&c.call(this)},this))}this.imgDiv.viewRequestID=this.layer.map.viewRequestID},removeImgDiv:function(){OpenLayers.Event.stopObservingElement(this.imgDiv);if(this.imgDiv.parentNode==this.frame)this.frame.removeChild(this.imgDiv),this.imgDiv.map=null; -this.imgDiv.urls=null;var a=this.imgDiv.firstChild;a?(OpenLayers.Event.stopObservingElement(a),this.imgDiv.removeChild(a),delete a):this.imgDiv.src=OpenLayers.Util.getImagesLocation()+"blank.gif"},checkImgURL:function(){this.layer&&(OpenLayers.Util.isEquivalentUrl(this.layerAlphaHack?this.imgDiv.firstChild.src:this.imgDiv.src,this.url)||this.hide())},startTransition:function(){if(this.backBufferTile&&this.backBufferTile.imgDiv){var a=1;this.backBufferTile.resolution&&(a=this.backBufferTile.resolution/ -this.layer.getResolution());if(1!=a){if("resize"==this.layer.transitionEffect){var b=new OpenLayers.LonLat(this.backBufferTile.bounds.left,this.backBufferTile.bounds.top),c=new OpenLayers.Size(this.backBufferTile.size.w*a,this.backBufferTile.size.h*a),b=this.layer.map.getLayerPxFromLonLat(b);OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,null,b,c);c=this.backBufferTile.imageSize;c=new OpenLayers.Size(c.w*a,c.h*a);(b=this.backBufferTile.imageOffset)&&(b=new OpenLayers.Pixel(b.x*a,b.y*a)); -OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv,null,b,c);this.backBufferTile.show()}}else this.layer.singleTile?this.backBufferTile.show():this.backBufferTile.hide()}},show:function(){this.frame.style.display="";if(-1!=OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)&&!0===OpenLayers.IS_GECKO)this.frame.scrollLeft=this.frame.scrollLeft},hide:function(){this.frame.style.display="none"},CLASS_NAME:"OpenLayers.Tile.Image"}); -OpenLayers.Tile.Image.useBlankTile="safari"==OpenLayers.BROWSER_NAME||"opera"==OpenLayers.BROWSER_NAME; -OpenLayers.Handler.Drag=OpenLayers.Class(OpenLayers.Handler,{started:!1,stopDown:!0,dragging:!1,touch:!1,last:null,start:null,lastMoveEvt:null,oldOnselectstart:null,interval:0,timeoutId:null,documentDrag:!1,documentEvents:null,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(!0===this.documentDrag){var d=this;this._docMove=function(a){d.mousemove({xy:{x:a.clientX,y:a.clientY},element:document})};this._docUp=function(a){d.mouseup({xy:{x:a.clientX,y:a.clientY}})}}}, -dragstart:function(a){var b=!0;this.dragging=!1;if(this.checkModifiers(a)&&(OpenLayers.Event.isLeftClick(a)||OpenLayers.Event.isSingleTouch(a))){this.started=!0;this.last=this.start=a.xy;OpenLayers.Element.addClass(this.map.viewPortDiv,"olDragDown");this.down(a);this.callback("down",[a.xy]);OpenLayers.Event.stop(a);if(!this.oldOnselectstart)this.oldOnselectstart=document.onselectstart?document.onselectstart:OpenLayers.Function.True;document.onselectstart=OpenLayers.Function.False;b=!this.stopDown}else this.started= -!1,this.last=this.start=null;return b},dragmove:function(a){this.lastMoveEvt=a;if(this.started&&!this.timeoutId&&(a.xy.x!=this.last.x||a.xy.y!=this.last.y)){!0===this.documentDrag&&this.documentEvents&&(a.element===document?(this.adjustXY(a),this.setEvent(a)):this.removeDocumentEvents());if(0window.opera.version()&&(b=-b)):a.detail&& -(b=-a.detail/3);this.delta+=b;this.interval?(window.clearTimeout(this._timeoutId),this._timeoutId=window.setTimeout(OpenLayers.Function.bind(function(){this.wheelZoom(a)},this),this.interval)):this.wheelZoom(a)}OpenLayers.Event.stop(a)}}},wheelZoom:function(a){var b=this.delta;this.delta=0;if(b){if(this.mousePosition)a.xy=this.mousePosition;if(!a.xy)a.xy=this.map.getPixelFromLonLat(this.map.getCenter());0>b?this.callback("down",[a,this.cumulative?b:-1]):this.callback("up",[a,this.cumulative?b:1])}}, -mousemove:function(a){this.mousePosition=a.xy},activate:function(a){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var b=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",b);OpenLayers.Event.observe(window,"mousewheel",b);OpenLayers.Event.observe(document,"mousewheel",b);return!0}return!1},deactivate:function(a){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var b=this.wheelListener;OpenLayers.Event.stopObserving(window,"DOMMouseScroll",b);OpenLayers.Event.stopObserving(window, -"mousewheel",b);OpenLayers.Event.stopObserving(document,"mousewheel",b);return!0}return!1},CLASS_NAME:"OpenLayers.Handler.MouseWheel"}); -OpenLayers.Control.Navigation=OpenLayers.Class(OpenLayers.Control,{dragPan:null,dragPanOptions:null,pinchZoom:null,pinchZoomOptions:null,documentDrag:!1,zoomBox:null,zoomBoxEnabled:!0,zoomWheelEnabled:!0,mouseWheelOptions:null,handleRightClicks:!1,zoomBoxKeyMask:OpenLayers.Handler.MOD_SHIFT,autoActivate:!0,initialize:function(a){this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,arguments)},destroy:function(){this.deactivate();this.dragPan&&this.dragPan.destroy();this.dragPan=null; -this.zoomBox&&this.zoomBox.destroy();this.zoomBox=null;this.pinchZoom&&this.pinchZoom.destroy();this.pinchZoom=null;OpenLayers.Control.prototype.destroy.apply(this,arguments)},activate:function(){this.dragPan.activate();this.zoomWheelEnabled&&this.handlers.wheel.activate();this.handlers.click.activate();this.zoomBoxEnabled&&this.zoomBox.activate();this.pinchZoom&&this.pinchZoom.activate();return OpenLayers.Control.prototype.activate.apply(this,arguments)},deactivate:function(){this.pinchZoom&&this.pinchZoom.deactivate(); -this.zoomBox.deactivate();this.dragPan.deactivate();this.handlers.click.deactivate();this.handlers.wheel.deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},draw:function(){if(this.handleRightClicks)this.map.viewPortDiv.oncontextmenu=OpenLayers.Function.False;this.handlers.click=new OpenLayers.Handler.Click(this,{click:this.defaultClick,dblclick:this.defaultDblClick,dblrightclick:this.defaultDblRightClick},{"double":!0,stopDouble:!0});this.dragPan=new OpenLayers.Control.DragPan(OpenLayers.Util.extend({map:this.map, -documentDrag:this.documentDrag},this.dragPanOptions));this.zoomBox=new OpenLayers.Control.ZoomBox({map:this.map,keyMask:this.zoomBoxKeyMask});this.dragPan.draw();this.zoomBox.draw();this.handlers.wheel=new OpenLayers.Handler.MouseWheel(this,{up:this.wheelUp,down:this.wheelDown},this.mouseWheelOptions);if(OpenLayers.Control.PinchZoom)this.pinchZoom=new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({map:this.map},this.pinchZoomOptions))},defaultClick:function(a){a.lastTouches&&2==a.lastTouches.length&& -this.map.zoomOut()},defaultDblClick:function(a){this.map.setCenter(this.map.getLonLatFromViewPortPx(a.xy),this.map.zoom+1)},defaultDblRightClick:function(a){this.map.setCenter(this.map.getLonLatFromViewPortPx(a.xy),this.map.zoom-1)},wheelChange:function(a,b){var c=this.map.getZoom(),d=this.map.getZoom()+Math.round(b),d=Math.max(d,0),d=Math.min(d,this.map.getNumZoomLevels());if(d!==c){var e=this.map.getSize(),c=e.w/2-a.xy.x,e=a.xy.y-e.h/2,f=this.map.baseLayer.getResolutionForZoom(d),g=this.map.getLonLatFromPixel(a.xy); -this.map.setCenter(new OpenLayers.LonLat(g.lon+c*f,g.lat+e*f),d)}},wheelUp:function(a,b){this.wheelChange(a,b||1)},wheelDown:function(a,b){this.wheelChange(a,b||-1)},disableZoomBox:function(){this.zoomBoxEnabled=!1;this.zoomBox.deactivate()},enableZoomBox:function(){this.zoomBoxEnabled=!0;this.active&&this.zoomBox.activate()},disableZoomWheel:function(){this.zoomWheelEnabled=!1;this.handlers.wheel.deactivate()},enableZoomWheel:function(){this.zoomWheelEnabled=!0;this.active&&this.handlers.wheel.activate()}, -CLASS_NAME:"OpenLayers.Control.Navigation"}); -OpenLayers.StyleMap=OpenLayers.Class({styles:null,extendDefault:!0,initialize:function(a,b){this.styles={"default":new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),select:new OpenLayers.Style(OpenLayers.Feature.Vector.style.select),temporary:new OpenLayers.Style(OpenLayers.Feature.Vector.style.temporary),"delete":new OpenLayers.Style(OpenLayers.Feature.Vector.style["delete"])};if(a instanceof OpenLayers.Style)this.styles["default"]=a,this.styles.select=a,this.styles.temporary=a,this.styles["delete"]= -a;else if("object"==typeof a)for(var c in a)if(a[c]instanceof OpenLayers.Style)this.styles[c]=a[c];else if("object"==typeof a[c])this.styles[c]=new OpenLayers.Style(a[c]);else{this.styles["default"]=new OpenLayers.Style(a);this.styles.select=new OpenLayers.Style(a);this.styles.temporary=new OpenLayers.Style(a);this.styles["delete"]=new OpenLayers.Style(a);break}OpenLayers.Util.extend(this,b)},destroy:function(){for(var a in this.styles)this.styles[a].destroy();this.styles=null},createSymbolizer:function(a, -b){a||(a=new OpenLayers.Feature.Vector);this.styles[b]||(b="default");a.renderIntent=b;var c={};this.extendDefault&&"default"!=b&&(c=this.styles["default"].createSymbolizer(a));return OpenLayers.Util.extend(c,this.styles[b].createSymbolizer(a))},addUniqueValueRules:function(a,b,c,d){var e=[],f;for(f in c)e.push(new OpenLayers.Rule({symbolizer:c[f],context:d,filter:new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.EQUAL_TO,property:b,value:f})}));this.styles[a].addRules(e)},CLASS_NAME:"OpenLayers.StyleMap"}); -OpenLayers.Layer.Vector=OpenLayers.Class(OpenLayers.Layer,{EVENT_TYPES:"beforefeatureadded,beforefeaturesadded,featureadded,featuresadded,beforefeatureremoved,beforefeaturesremoved,featureremoved,featuresremoved,beforefeatureselected,featureselected,featureunselected,beforefeaturemodified,featuremodified,afterfeaturemodified,vertexmodified,vertexremoved,sketchstarted,sketchmodified,sketchcomplete,refresh".split(","),isBaseLayer:!1,isFixed:!1,features:null,filter:null,selectedFeatures:null,unrenderedFeatures:null, -reportError:!0,style:null,styleMap:null,strategies:null,protocol:null,renderers:["SVG","VML","Canvas"],renderer:null,rendererOptions:null,geometryType:null,drawn:!1,initialize:function(a,b){this.EVENT_TYPES=OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(OpenLayers.Layer.prototype.EVENT_TYPES);OpenLayers.Layer.prototype.initialize.apply(this,arguments);(!this.renderer||!this.renderer.supported())&&this.assignRenderer();if(!this.renderer||!this.renderer.supported())this.renderer=null,this.displayError(); -if(!this.styleMap)this.styleMap=new OpenLayers.StyleMap;this.features=[];this.selectedFeatures=[];this.unrenderedFeatures={};if(this.strategies)for(var c=0,d=this.strategies.length;cOpenStreetMap",sphericalMercator:!0,url:"http://tile.openstreetmap.org/${z}/${x}/${y}.png",clone:function(a){null==a&&(a=new OpenLayers.Layer.OSM(this.name,this.url,this.getOptions()));return a=OpenLayers.Layer.XYZ.prototype.clone.apply(this,[a])},wrapDateLine:!0,CLASS_NAME:"OpenLayers.Layer.OSM"}); -OpenLayers.Control.Scale=OpenLayers.Class(OpenLayers.Control,{element:null,geodesic:!1,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);this.element=OpenLayers.Util.getElement(a)},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.element)this.element=document.createElement("div"),this.div.appendChild(this.element);this.map.events.register("moveend",this,this.updateScale);this.updateScale();return this.div},updateScale:function(){var a; -if(!0===this.geodesic){if(!this.map.getUnits())return;a=OpenLayers.INCHES_PER_UNIT;a=(this.map.getGeodesicPixelSize().w||1.0E-6)*a.km*OpenLayers.DOTS_PER_INCH}else a=this.map.getScale();if(a)a=9500<=a&&95E4>=a?Math.round(a/1E3)+"K":95E4<=a?Math.round(a/1E6)+"M":Math.round(a),this.element.innerHTML=OpenLayers.i18n("scale",{scaleDenom:a})},CLASS_NAME:"OpenLayers.Control.Scale"}); -OpenLayers.Renderer.SVG=OpenLayers.Class(OpenLayers.Renderer.Elements,{xmlns:"http://www.w3.org/2000/svg",xlinkns:"http://www.w3.org/1999/xlink",MAX_PIXEL:15E3,translationParameters:null,symbolMetrics:null,initialize:function(a){if(this.supported())OpenLayers.Renderer.Elements.prototype.initialize.apply(this,arguments),this.translationParameters={x:0,y:0},this.symbolMetrics={}},supported:function(){return document.implementation&&(document.implementation.hasFeature("org.w3c.svg","1.0")||document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#SVG", -"1.1")||document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1"))},inValidRange:function(a,b,c){a+=c?0:this.translationParameters.x;b+=c?0:this.translationParameters.y;return a>=-this.MAX_PIXEL&&a<=this.MAX_PIXEL&&b>=-this.MAX_PIXEL&&b<=this.MAX_PIXEL},setExtent:function(a,b){OpenLayers.Renderer.Elements.prototype.setExtent.apply(this,arguments);var c=this.getResolution(),d=-a.left/c,c=a.top/c;if(b)return this.left=d,this.top=c,this.rendererRoot.setAttributeNS(null, -"viewBox","0 0 "+this.size.w+" "+this.size.h),this.translate(0,0),!0;(d=this.translate(d-this.left,c-this.top))||this.setExtent(a,!0);return d},translate:function(a,b){if(this.inValidRange(a,b,!0)){var c="";if(a||b)c="translate("+a+","+b+")";this.root.setAttributeNS(null,"transform",c);this.translationParameters={x:a,y:b};return!0}return!1},setSize:function(a){OpenLayers.Renderer.prototype.setSize.apply(this,arguments);this.rendererRoot.setAttributeNS(null,"width",this.size.w);this.rendererRoot.setAttributeNS(null, -"height",this.size.h)},getNodeType:function(a,b){var c=null;switch(a.CLASS_NAME){case "OpenLayers.Geometry.Point":c=b.externalGraphic?"image":this.isComplexSymbol(b.graphicName)?"svg":"circle";break;case "OpenLayers.Geometry.Rectangle":c="rect";break;case "OpenLayers.Geometry.LineString":c="polyline";break;case "OpenLayers.Geometry.LinearRing":c="polygon";break;case "OpenLayers.Geometry.Polygon":case "OpenLayers.Geometry.Curve":case "OpenLayers.Geometry.Surface":c="path"}return c},setStyle:function(a, -b,c){var b=b||a._style,c=c||a._options,d=parseFloat(a.getAttributeNS(null,"r")),e=1,f;if("OpenLayers.Geometry.Point"==a._geometryClass&&d){a.style.visibility="";if(!1===b.graphic)a.style.visibility="hidden";else if(b.externalGraphic){f=this.getPosition(a);if(b.graphicTitle)a.setAttributeNS(null,"title",b.graphicTitle),d=this.nodeFactory(null,"title"),d.textContent=b.graphicTitle,a.appendChild(d);b.graphicWidth&&b.graphicHeight&&a.setAttributeNS(null,"preserveAspectRatio","none");var d=b.graphicWidth|| -b.graphicHeight,g=b.graphicHeight||b.graphicWidth,d=d?d:2*b.pointRadius,g=g?g:2*b.pointRadius,h=void 0!=b.graphicYOffset?b.graphicYOffset:-(0.5*g),i=b.graphicOpacity||b.fillOpacity;a.setAttributeNS(null,"x",(f.x+(void 0!=b.graphicXOffset?b.graphicXOffset:-(0.5*d))).toFixed());a.setAttributeNS(null,"y",(f.y+h).toFixed());a.setAttributeNS(null,"width",d);a.setAttributeNS(null,"height",g);a.setAttributeNS(this.xlinkns,"href",b.externalGraphic);a.setAttributeNS(null,"style","opacity: "+i);a.onclick=OpenLayers.Renderer.SVG.preventDefault}else if(this.isComplexSymbol(b.graphicName)){var d= -3*b.pointRadius,g=2*d,j=this.importSymbol(b.graphicName);f=this.getPosition(a);e=3*this.symbolMetrics[j.id][0]/g;h=a.parentNode;i=a.nextSibling;h&&h.removeChild(a);a.firstChild&&a.removeChild(a.firstChild);a.appendChild(j.firstChild.cloneNode(!0));a.setAttributeNS(null,"viewBox",j.getAttributeNS(null,"viewBox"));a.setAttributeNS(null,"width",g);a.setAttributeNS(null,"height",g);a.setAttributeNS(null,"x",f.x-d);a.setAttributeNS(null,"y",f.y-d);i?h.insertBefore(a,i):h&&h.appendChild(a)}else a.setAttributeNS(null, -"r",b.pointRadius);d=b.rotation;if((void 0!==d||void 0!==a._rotation)&&f)a._rotation=d,d|=0,"svg"!==a.nodeName?a.setAttributeNS(null,"transform","rotate("+d+" "+f.x+" "+f.y+")"):(f=this.symbolMetrics[j.id],a.firstChild.setAttributeNS(null,"transform","rotate("+d+" "+f[1]+" "+f[2]+")"))}c.isFilled?(a.setAttributeNS(null,"fill",b.fillColor),a.setAttributeNS(null,"fill-opacity",b.fillOpacity)):a.setAttributeNS(null,"fill","none");c.isStroked?(a.setAttributeNS(null,"stroke",b.strokeColor),a.setAttributeNS(null, -"stroke-opacity",b.strokeOpacity),a.setAttributeNS(null,"stroke-width",b.strokeWidth*e),a.setAttributeNS(null,"stroke-linecap",b.strokeLinecap||"round"),a.setAttributeNS(null,"stroke-linejoin","round"),b.strokeDashstyle&&a.setAttributeNS(null,"stroke-dasharray",this.dashStyle(b,e))):a.setAttributeNS(null,"stroke","none");b.pointerEvents&&a.setAttributeNS(null,"pointer-events",b.pointerEvents);null!=b.cursor&&a.setAttributeNS(null,"cursor",b.cursor);return a},dashStyle:function(a,b){var c=a.strokeWidth* -b,d=a.strokeDashstyle;switch(d){case "solid":return"none";case "dot":return[1,4*c].join();case "dash":return[4*c,4*c].join();case "dashdot":return[4*c,4*c,1,4*c].join();case "longdash":return[8*c,4*c].join();case "longdashdot":return[8*c,4*c,1,4*c].join();default:return OpenLayers.String.trim(d).replace(/\s+/g,",")}},createNode:function(a,b){var c=document.createElementNS(this.xmlns,a);b&&c.setAttributeNS(null,"id",b);return c},nodeTypeCompare:function(a,b){return b==a.nodeName},createRenderRoot:function(){return this.nodeFactory(this.container.id+ -"_svgRoot","svg")},createRoot:function(a){return this.nodeFactory(this.container.id+a,"g")},createDefs:function(){var a=this.nodeFactory(this.container.id+"_defs","defs");this.rendererRoot.appendChild(a);return a},drawPoint:function(a,b){return this.drawCircle(a,b,1)},drawCircle:function(a,b,c){var d=this.getResolution(),e=b.x/d+this.left,b=this.top-b.y/d;return this.inValidRange(e,b)?(a.setAttributeNS(null,"cx",e),a.setAttributeNS(null,"cy",b),a.setAttributeNS(null,"r",c),a):!1},drawLineString:function(a, -b){var c=this.getComponentsString(b.components);return c.path?(a.setAttributeNS(null,"points",c.path),c.complete?a:null):!1},drawLinearRing:function(a,b){var c=this.getComponentsString(b.components);return c.path?(a.setAttributeNS(null,"points",c.path),c.complete?a:null):!1},drawPolygon:function(a,b){for(var c="",d=!0,e=!0,f,g,h=0,i=b.components.length;hh;)d.removeChild(d.lastChild);for(var i=0;id)i=(c-g)/(h-f),h=0>h?-d:d,c=g+(h-f)*i;if(c<-e||c>e)i=(h-f)/(c-g),c=0>c?-e:e,h=f+(c-g)*i;return h+","+c},getShortString:function(a){var b=this.getResolution(),c=a.x/b+this.left,a=this.top-a.y/b;return this.inValidRange(c, -a)?c+","+a:!1},getPosition:function(a){return{x:parseFloat(a.getAttributeNS(null,"cx")),y:parseFloat(a.getAttributeNS(null,"cy"))}},importSymbol:function(a){if(!this.defs)this.defs=this.createDefs();var b=this.container.id+"-"+a,c=document.getElementById(b);if(null!=c)return c;var d=OpenLayers.Renderer.symbol[a];if(!d)throw Error(a+" is not a valid symbol name");var a=this.nodeFactory(b,"symbol"),e=this.nodeFactory(null,"polygon");a.appendChild(e);for(var c=new OpenLayers.Bounds(Number.MAX_VALUE, -Number.MAX_VALUE,0,0),f=[],g,h,i=0;ithis.granularity||Math.abs(a.xy.y-this.lastXy.y)>this.granularity)this.lastXy=a.xy;else if(b=this.map.getLonLatFromPixel(a.xy))if(this.displayProjection&&b.transform(this.map.getProjectionObject(),this.displayProjection),this.lastXy=a.xy,a=this.formatOutput(b),a!=this.element.innerHTML)this.element.innerHTML=a},reset:function(){if(null!=this.emptyString)this.element.innerHTML=this.emptyString},formatOutput:function(a){var b=parseInt(this.numDigits);return this.prefix+a.lon.toFixed(b)+ -this.separator+a.lat.toFixed(b)+this.suffix},CLASS_NAME:"OpenLayers.Control.MousePosition"}); -OpenLayers.Geometry.MultiLineString=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.LineString"],initialize:function(a){OpenLayers.Geometry.Collection.prototype.initialize.apply(this,arguments)},split:function(a,b){for(var c=null,d=b&&b.mutual,e,f,g,h,i=[],j=[a],k=0,o=this.components.length;k
To get rid of this message, select a new BaseLayer in the layer switcher in the upper-right corner.

Most likely, this is because the Google Maps library script was either not included, or does not contain the correct API key for your site.

Developers: For help getting this working correctly, click here", -getLayerWarning:"The ${layerType} Layer was unable to load correctly.

To get rid of this message, select a new BaseLayer in the layer switcher in the upper-right corner.

Most likely, this is because the ${layerLib} library script was not correctly included.

Developers: For help getting this working correctly, click here",scale:"Scale = 1 : ${scaleDenom}",W:"W",E:"E",N:"N",S:"S",graticule:"Graticule",layerAlreadyAdded:"You tried to add the layer: ${layerName} to the map, but it has already been added", -reprojectDeprecated:"You are using the 'reproject' option on the ${layerName} layer. This option is deprecated: its use was designed to support displaying data over commercial basemaps, but that functionality should now be achieved by using Spherical Mercator support. More information is available from http://trac.openlayers.org/wiki/SphericalMercator.",methodDeprecated:"This method has been deprecated and will be removed in 3.0. Please use ${newMethod} instead.",boundsAddError:"You must pass both x and y values to the add function.", -lonlatAddError:"You must pass both lon and lat values to the add function.",pixelAddError:"You must pass both x and y values to the add function.",unsupportedGeometryType:"Unsupported geometry type: ${geomType}",pagePositionFailed:"OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",filterEvaluateNotImplemented:"evaluate is not implemented for this filter type.",proxyNeeded:"You probably need to set OpenLayers.ProxyHost to access ${url}.See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost", -end:""};OpenLayers.Rico=OpenLayers.Rico||{}; -OpenLayers.Rico.Color=OpenLayers.Class({initialize:function(a,b,c){this.rgb={r:a,g:b,b:c}},setRed:function(a){this.rgb.r=a},setGreen:function(a){this.rgb.g=a},setBlue:function(a){this.rgb.b=a},setHue:function(a){var b=this.asHSB();b.h=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},setSaturation:function(a){var b=this.asHSB();b.s=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},setBrightness:function(a){var b=this.asHSB();b.b=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)}, -darken:function(a){var b=this.asHSB();this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,Math.max(b.b-a,0))},brighten:function(a){var b=this.asHSB();this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,Math.min(b.b+a,1))},blend:function(a){this.rgb.r=Math.floor((this.rgb.r+a.rgb.r)/2);this.rgb.g=Math.floor((this.rgb.g+a.rgb.g)/2);this.rgb.b=Math.floor((this.rgb.b+a.rgb.b)/2)},isBright:function(){this.asHSB();return 0.5c;c++)a+=b.charAt(c)+b.charAt(c);0==a.indexOf("#")&&(a=a.substring(1));b=a.substring(0,2);c=a.substring(2,4);a=a.substring(4,6);return new OpenLayers.Rico.Color(parseInt(b,16),parseInt(c,16),parseInt(a,16))}; -OpenLayers.Rico.Color.createColorFromBackground=function(a){var b=OpenLayers.Element.getStyle(OpenLayers.Util.getElement(a),"backgroundColor");if("transparent"==b&&a.parentNode)return OpenLayers.Rico.Color.createColorFromBackground(a.parentNode);if(null==b)return new OpenLayers.Rico.Color(255,255,255);return 0==b.indexOf("rgb(")?(a=b.substring(4,b.length-1).split(","),new OpenLayers.Rico.Color(parseInt(a[0]),parseInt(a[1]),parseInt(a[2]))):0==b.indexOf("#")?OpenLayers.Rico.Color.createFromHex(b): -new OpenLayers.Rico.Color(255,255,255)}; -OpenLayers.Rico.Color.HSBtoRGB=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=parseInt(255*c+0.5);else{var a=6*(a-Math.floor(a)),g=a-Math.floor(a),h=c*(1-b),i=c*(1-b*g),b=c*(1-b*(1-g));switch(parseInt(a)){case 0:d=255*c+0.5;e=255*b+0.5;f=255*h+0.5;break;case 1:d=255*i+0.5;e=255*c+0.5;f=255*h+0.5;break;case 2:d=255*h+0.5;e=255*c+0.5;f=255*b+0.5;break;case 3:d=255*h+0.5;e=255*i+0.5;f=255*c+0.5;break;case 4:d=255*b+0.5;e=255*h+0.5;f=255*c+0.5;break;case 5:d=255*c+0.5,e=255*h+0.5,f=255*i+0.5}}return{r:parseInt(d), -g:parseInt(e),b:parseInt(f)}};OpenLayers.Rico.Color.RGBtoHSB=function(a,b,c){var d,e=a>b?a:b;c>e&&(e=c);var f=aa&&(a+=1)}return{h:a,s:d,b:e/255}}; -OpenLayers.Layer.SphericalMercator={getExtent:function(){var a=null;return a=this.sphericalMercator?this.map.calculateBounds():OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this)},getLonLatFromViewPortPx:function(a){return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this,arguments)},getViewPortPxFromLonLat:function(a){return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this,arguments)},initMercatorParameters:function(){this.RESOLUTIONS=[];for(var a=0;a<=this.MAX_ZOOM_LEVEL;++a)this.RESOLUTIONS[a]= -156543.03390625/Math.pow(2,a);this.units="m";this.projection=this.projection||"EPSG:900913"},forwardMercator:function(a,b){var c=2.003750834E7*a/180,d=Math.log(Math.tan((90+b)*Math.PI/360))/(Math.PI/180);return new OpenLayers.LonLat(c,2.003750834E7*d/180)},inverseMercator:function(a,b){var c=180*(a/2.003750834E7),d;d=180/Math.PI*(2*Math.atan(Math.exp(180*(b/2.003750834E7)*Math.PI/180))-Math.PI/2);return new OpenLayers.LonLat(c,d)},projectForward:function(a){var b=OpenLayers.Layer.SphericalMercator.forwardMercator(a.x, -a.y);a.x=b.lon;a.y=b.lat;return a},projectInverse:function(a){var b=OpenLayers.Layer.SphericalMercator.inverseMercator(a.x,a.y);a.x=b.lon;a.y=b.lat;return a}};(function(){var a=["EPSG:900913","EPSG:3857","EPSG:102113","EPSG:102100"],b=OpenLayers.Projection.addTransform,c=OpenLayers.Layer.SphericalMercator,d=OpenLayers.Projection.nullTransform,e,f,g,h,i;for(e=0,f=a.length;e"+a.innerHTML+""},_roundTopCorners:function(a,b,c){for(var d=this._createCorner(c),e=0;e) - -*/ - -/* Contains portions of Prototype.js: - * - * Prototype JavaScript framework, version 1.4.0 - * (c) 2005 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://prototype.conio.net/ - * - *--------------------------------------------------------------------------*/ - -/** -* -* Contains portions of Rico -* -* Copyright 2005 Sabre Airline Solutions -* -* Licensed under the Apache License, Version 2.0 (the "License"); you -* may not use this file except in compliance with the License. You -* may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -* implied. See the License for the specific language governing -* permissions and limitations under the License. -* -**/ - -/** - * Contains XMLHttpRequest.js - * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - */ - -/** - * Contains portions of Gears - * - * Copyright 2007, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of Google Inc. nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Sets up google.gears.*, which is *the only* supported way to access Gears. - * - * Circumvent this file at your own risk! - * - * In the future, Gears may automatically define google.gears.* without this - * file. Gears may use these objects to transparently fix bugs and compatibility - * issues. Applications that use the code below will continue to work seamlessly - * when that happens. - */ - -/** - * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is - * Copyright (c) 2006, Yahoo! Inc. - * All rights reserved. - * - * Redistribution and use of this software in source and binary forms, with or - * without modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Yahoo! Inc. nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission of Yahoo! Inc. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/* -* The theme used for the OpenLayers controls is the "dark" theme made available -* by Development Seed under the BSD License: -* -* https://github.com/developmentseed/openlayers_themes/blob/master/LICENSE.txt -* -*/ -/* -* The default map marker is derived from an icon available at The Noun Project: -* -* http://thenounproject.com/ -* -*/ -var OpenLayers={VERSION_NUMBER:"Release 2.11",singleFile:!0,_getScriptLocation:function(){for(var a=/(^|(.*?\/))(OpenLayers.js)(\?|$)/,b=document.getElementsByTagName("script"),c,d="",e=0,f=b.length;e<\/script>";a.length>0&&document.write(a.join(""))}})();OpenLayers.VERSION_NUMBER="Release 2.11"; -OpenLayers.String={startsWith:function(a,b){return a.indexOf(b)==0},contains:function(a,b){return a.indexOf(b)!=-1},trim:function(a){return a.replace(/^\s\s*/,"").replace(/\s\s*$/,"")},camelize:function(a){for(var a=a.split("-"),b=a[0],c=1,d=a.length;c0&&(c=parseFloat(a.toPrecision(b)));return c},format:function(a,b,c,d){b=typeof b!="undefined"?b:0;c=typeof c!="undefined"?c:OpenLayers.Number.thousandsSeparator;d=typeof d!="undefined"?d:OpenLayers.Number.decimalSeparator;b!=null&&(a=parseFloat(a.toFixed(b)));var e=a.toString().split(".");e.length==1&&b==null&&(b=0);a=e[0];if(c)for(var f=/(-?[0-9]+)([0-9]{3})/;f.test(a);)a=a.replace(f,"$1"+c+"$2"); -b==0?b=a:(c=e.length>1?e[1]:"0",b!=null&&(c+=Array(b-c.length+1).join("0")),b=a+d+c);return b}};if(!Number.prototype.limitSigDigs)Number.prototype.limitSigDigs=function(a){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{newMethod:"OpenLayers.Number.limitSigDigs"}));return OpenLayers.Number.limitSigDigs(this,a)}; -OpenLayers.Function={bind:function(a,b){var c=Array.prototype.slice.apply(arguments,[2]);return function(){var d=c.concat(Array.prototype.slice.apply(arguments,[0]));return a.apply(b,d)}},bindAsEventListener:function(a,b){return function(c){return a.call(b,c||window.event)}},False:function(){return!1},True:function(){return!0},Void:function(){}}; -if(!Function.prototype.bind)Function.prototype.bind=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{newMethod:"OpenLayers.Function.bind"}));Array.prototype.unshift.apply(arguments,[this]);return OpenLayers.Function.bind.apply(null,arguments)}; -if(!Function.prototype.bindAsEventListener)Function.prototype.bindAsEventListener=function(a){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{newMethod:"OpenLayers.Function.bindAsEventListener"}));return OpenLayers.Function.bindAsEventListener(this,a)};OpenLayers.Array={filter:function(a,b,c){var d=[];if(Array.prototype.filter)d=a.filter(b,c);else{var e=a.length;if(typeof b!="function")throw new TypeError;for(var f=0;f1?(a=[d,b].concat(Array.prototype.slice.call(arguments).slice(1,a-1),c),OpenLayers.inherit.apply(null,a)):d.prototype=c;return d};OpenLayers.Class.isPrototype=function(){};OpenLayers.Class.create=function(){return function(){arguments&&arguments[0]!=OpenLayers.Class.isPrototype&&this.initialize.apply(this,arguments)}}; -OpenLayers.Class.inherit=function(a){var b=function(){a.call(this)},c=[b].concat(Array.prototype.slice.call(arguments));OpenLayers.inherit.apply(null,c);return b.prototype};OpenLayers.inherit=function(a,b){var c=function(){};c.prototype=b.prototype;a.prototype=new c;var d,e;for(c=2,d=arguments.length;c=0;c--)a[c]==b&&a.splice(c,1);return a};OpenLayers.Util.clearArray=function(a){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{newMethod:"array = []"}));a.length=0}; -OpenLayers.Util.indexOf=function(a,b){if(typeof a.indexOf=="function")return a.indexOf(b);else{for(var c=0,d=a.length;c=0&&parseFloat(h)<1)a.style.filter="alpha(opacity="+h*100+")",a.style.opacity=h;else if(parseFloat(h)==1)a.style.filter="",a.style.opacity=""}; -OpenLayers.Util.createDiv=function(a,b,c,d,e,f,g,h){var i=document.createElement("div");if(d)i.style.backgroundImage="url("+d+")";a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="absolute");OpenLayers.Util.modifyDOMElement(i,a,b,c,e,f,g,h);return i}; -OpenLayers.Util.createImage=function(a,b,c,d,e,f,g,h){var i=document.createElement("img");a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="relative");OpenLayers.Util.modifyDOMElement(i,a,b,c,e,f,null,g);if(h)i.style.display="none",OpenLayers.Event.observe(i,"load",OpenLayers.Function.bind(OpenLayers.Util.onImageLoad,i)),OpenLayers.Event.observe(i,"error",OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError,i));i.style.alt=a;i.galleryImg="no";if(d)i.src=d;return i}; -OpenLayers.Util.setOpacity=function(a,b){OpenLayers.Util.modifyDOMElement(a,null,null,null,null,null,null,b)};OpenLayers.Util.onImageLoad=function(){if(!this.viewRequestID||this.map&&this.viewRequestID==this.map.viewRequestID)this.style.display="";OpenLayers.Element.removeClass(this,"olImageLoadError")};OpenLayers.IMAGE_RELOAD_ATTEMPTS=0; -OpenLayers.Util.onImageLoadError=function(){this._attempts=this._attempts?this._attempts+1:1;if(this._attempts<=OpenLayers.IMAGE_RELOAD_ATTEMPTS){var a=this.urls;if(a&&OpenLayers.Util.isArray(a)&&a.length>1){var b=this.src.toString(),c,d;for(d=0;c=a[d];d++)if(b.indexOf(c)!=-1)break;var e=Math.floor(a.length*Math.random()),e=a[e];for(d=0;e==c&&d++<4;)e=Math.floor(a.length*Math.random()),e=a[e];this.src=b.replace(c,e)}else this.src=this.src}else OpenLayers.Element.addClass(this,"olImageLoadError"); -this.style.display=""};OpenLayers.Util.alphaHackNeeded=null;OpenLayers.Util.alphaHack=function(){if(OpenLayers.Util.alphaHackNeeded==null){var a=navigator.appVersion.split("MSIE"),a=parseFloat(a[1]),b=!1;try{b=!!document.body.filters}catch(c){}OpenLayers.Util.alphaHackNeeded=b&&a>=5.5&&a<7}return OpenLayers.Util.alphaHackNeeded}; -OpenLayers.Util.modifyAlphaImageDiv=function(a,b,c,d,e,f,g,h,i){OpenLayers.Util.modifyDOMElement(a,b,c,d,f,null,null,i);b=a.childNodes[0];if(e)b.src=e;OpenLayers.Util.modifyDOMElement(b,a.id+"_innerImage",null,d,"relative",g);if(OpenLayers.Util.alphaHack()){if(a.style.display!="none")a.style.display="inline-block";h==null&&(h="scale");a.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+b.src+"', sizingMethod='"+h+"')";parseFloat(a.style.opacity)>=0&&parseFloat(a.style.opacity)< -1&&(a.style.filter+=" alpha(opacity="+a.style.opacity*100+")");b.style.filter="alpha(opacity=0)"}}; -OpenLayers.Util.createAlphaImageDiv=function(a,b,c,d,e,f,g,h,i){var k=OpenLayers.Util.createDiv(),q=OpenLayers.Util.createImage(null,null,null,null,null,null,null,!1);k.appendChild(q);if(i)q.style.display="none",OpenLayers.Event.observe(q,"load",OpenLayers.Function.bind(OpenLayers.Util.onImageLoad,k)),OpenLayers.Event.observe(q,"error",OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError,k));OpenLayers.Util.modifyAlphaImageDiv(k,a,b,c,d,e,f,g,h);return k}; -OpenLayers.Util.upperCaseObject=function(a){var b={},c;for(c in a)b[c.toUpperCase()]=a[c];return b};OpenLayers.Util.applyDefaults=function(a,b){var a=a||{},c=typeof window.Event=="function"&&b instanceof window.Event,d;for(d in b)if(a[d]===void 0||!c&&b.hasOwnProperty&&b.hasOwnProperty(d)&&!a.hasOwnProperty(d))a[d]=b[d];if(!c&&b&&b.hasOwnProperty&&b.hasOwnProperty("toString")&&!a.hasOwnProperty("toString"))a.toString=b.toString;return a}; -OpenLayers.Util.getParameterString=function(a){var b=[],c;for(c in a){var d=a[c];if(d!=null&&typeof d!="function"){if(typeof d=="object"&&d.constructor==Array){for(var e=[],f,g=0,h=d.length;g1.0E-12&&--r>0;){var m=Math.sin(q),j=Math.cos(q),n=Math.sqrt(h*m*h*m+(g*k-i*h*j)*(g*k-i*h*j));if(n==0)return 0;var j=i*k+g*h*j,l=Math.atan2(n,j),o=Math.asin(g* -h*m/n),p=Math.cos(o)*Math.cos(o),m=j-2*i*k/p,t=c/16*p*(4+c*(4-3*p)),s=q,q=f+(1-t)*c*Math.sin(o)*(l+t*n*(m+t*j*(-1+2*m*m)))}if(r==0)return NaN;d=p*(d*d-e*e)/(e*e);c=d/1024*(256+d*(-128+d*(74-47*d)));return(e*(1+d/16384*(4096+d*(-768+d*(320-175*d))))*(l-c*n*(m+c/4*(j*(-1+2*m*m)-c/6*m*(-3+4*n*n)*(-3+4*m*m))))).toFixed(3)/1E3}; -OpenLayers.Util.destinationVincenty=function(a,b,c){for(var d=OpenLayers.Util,e=d.VincentyConstants,f=e.a,g=e.b,h=e.f,e=a.lon,a=a.lat,i=d.rad(b),b=Math.sin(i),i=Math.cos(i),a=(1-h)*Math.tan(d.rad(a)),k=1/Math.sqrt(1+a*a),q=a*k,s=Math.atan2(a,i),a=k*b,r=1-a*a,f=r*(f*f-g*g)/(g*g),m=1+f/16384*(4096+f*(-768+f*(320-175*f))),j=f/1024*(256+f*(-128+f*(74-47*f))),f=c/(g*m),n=2*Math.PI;Math.abs(f-n)>1.0E-12;)var l=Math.cos(2*s+f),o=Math.sin(f),p=Math.cos(f),t=j*o*(l+j/4*(p*(-1+2*l*l)-j/6*l*(-3+4*o*o)*(-3+4* -l*l))),n=f,f=c/(g*m)+t;c=q*o-k*p*i;g=Math.atan2(q*p+k*o*i,(1-h)*Math.sqrt(a*a+c*c));b=Math.atan2(o*b,k*p-q*o*i);i=h/16*r*(4+h*(4-3*r));l=b-(1-i)*h*a*(f+i*o*(l+i*p*(-1+2*l*l)));Math.atan2(a,-c);return new OpenLayers.LonLat(e+d.deg(l),d.deg(g))}; -OpenLayers.Util.getParameters=function(a){var a=a===null||a===void 0?window.location.href:a,b="";if(OpenLayers.String.contains(a,"?"))var b=a.indexOf("?")+1,c=OpenLayers.String.contains(a,"#")?a.indexOf("#"):a.length,b=a.substring(b,c);for(var a={},b=b.split(/[&;]/),c=0,d=b.length;c1?1/a:a};OpenLayers.Util.getResolutionFromScale=function(a,b){var c;a&&(b==null&&(b="degrees"),c=1/(OpenLayers.Util.normalizeScale(a)*OpenLayers.INCHES_PER_UNIT[b]*OpenLayers.DOTS_PER_INCH));return c}; -OpenLayers.Util.getScaleFromResolution=function(a,b){b==null&&(b="degrees");return a*OpenLayers.INCHES_PER_UNIT[b]*OpenLayers.DOTS_PER_INCH};OpenLayers.Util.safeStopPropagation=function(a){OpenLayers.Event.stop(a,!0)}; -OpenLayers.Util.pagePosition=function(a){var b=[0,0],c=OpenLayers.Util.getViewportElement();if(!a||a==window||a==c)return b;var d=OpenLayers.IS_GECKO&&document.getBoxObjectFor&&OpenLayers.Element.getStyle(a,"position")=="absolute"&&(a.style.top==""||a.style.left==""),e=null;if(a.getBoundingClientRect)a=a.getBoundingClientRect(),e=c.scrollTop,b[0]=a.left+c.scrollLeft,b[1]=a.top+e;else if(document.getBoxObjectFor&&!d)a=document.getBoxObjectFor(a),c=document.getBoxObjectFor(c),b[0]=a.screenX-c.screenX, -b[1]=a.screenY-c.screenY;else{b[0]=a.offsetLeft;b[1]=a.offsetTop;e=a.offsetParent;if(e!=a)for(;e;)b[0]+=e.offsetLeft,b[1]+=e.offsetTop,e=e.offsetParent;c=OpenLayers.BROWSER_NAME;if(c=="opera"||c=="safari"&&OpenLayers.Element.getStyle(a,"position")=="absolute")b[1]-=document.body.offsetTop;for(e=a.offsetParent;e&&e!=document.body;){b[0]-=e.scrollLeft;if(c!="opera"||e.tagName!="TR")b[1]-=e.scrollTop;e=e.offsetParent}}return b}; -OpenLayers.Util.getViewportElement=function(){var a=arguments.callee.viewportElement;if(a==void 0)a=OpenLayers.BROWSER_NAME=="msie"&&document.compatMode!="CSS1Compat"?document.body:document.documentElement,arguments.callee.viewportElement=a;return a}; -OpenLayers.Util.isEquivalentUrl=function(a,b,c){c=c||{};OpenLayers.Util.applyDefaults(c,{ignoreCase:!0,ignorePort80:!0,ignoreHash:!0});var a=OpenLayers.Util.createUrlObject(a,c),b=OpenLayers.Util.createUrlObject(b,c),d;for(d in a)if(d!=="args"&&a[d]!=b[d])return!1;for(d in a.args){if(a.args[d]!=b.args[d])return!1;delete b.args[d]}for(d in b.args)return!1;return!0}; -OpenLayers.Util.createUrlObject=function(a,b){b=b||{};if(!/^\w+:\/\//.test(a)){var c=window.location,d=c.port?":"+c.port:"",d=c.protocol+"//"+c.host.split(":").shift()+d;a.indexOf("/")===0?a=d+a:(c=c.pathname.split("/"),c.pop(),a=d+c.join("/")+"/"+a)}b.ignoreCase&&(a=a.toLowerCase());c=document.createElement("a");c.href=a;d={};d.host=c.host.split(":").shift();d.protocol=c.protocol;d.port=b.ignorePort80?c.port=="80"||c.port=="0"?"":c.port:c.port==""||c.port=="0"?"80":c.port;d.hash=b.ignoreHash||c.hash=== -"#"?"":c.hash;var e=c.search;e||(e=a.indexOf("?"),e=e!=-1?a.substr(e):"");d.args=OpenLayers.Util.getParameters(e);d.pathname=c.pathname.charAt(0)=="/"?c.pathname:"/"+c.pathname;return d};OpenLayers.Util.removeTail=function(a){var b=null,b=a.indexOf("?"),c=a.indexOf("#");return b=b==-1?c!=-1?a.substr(0,c):a:c!=-1?a.substr(0,Math.min(b,c)):a.substr(0,b)};OpenLayers.IS_GECKO=function(){var a=navigator.userAgent.toLowerCase();return a.indexOf("webkit")==-1&&a.indexOf("gecko")!=-1}(); -OpenLayers.BROWSER_NAME=function(){var a="",b=navigator.userAgent.toLowerCase();b.indexOf("opera")!=-1?a="opera":b.indexOf("msie")!=-1?a="msie":b.indexOf("safari")!=-1?a="safari":b.indexOf("mozilla")!=-1&&(a=b.indexOf("firefox")!=-1?"firefox":"mozilla");return a}();OpenLayers.Util.getBrowserName=function(){return OpenLayers.BROWSER_NAME}; -OpenLayers.Util.getRenderedDimensions=function(a,b,c){var d,e,f=document.createElement("div");f.style.visibility="hidden";var g=c&&c.containerElement?c.containerElement:document.body;if(b)if(b.w)d=b.w,f.style.width=d+"px";else if(b.h)e=b.h,f.style.height=e+"px";if(c&&c.displayClass)f.className=c.displayClass;b=document.createElement("div");b.innerHTML=a;b.style.overflow="visible";if(b.childNodes){a=0;for(c=b.childNodes.length;a=60&&(f-=60,d+=1,d>=60&&(d-=60,e+=1));e<10&&(e="0"+e);e+="\u00b0";c.indexOf("dm")>=0&&(d<10&&(d="0"+d),e+=d+"'",c.indexOf("dms")>=0&&(f<10&&(f="0"+f),e+=f+'"'));e+=b=="lon"?a<0?OpenLayers.i18n("W"):OpenLayers.i18n("E"):a<0?OpenLayers.i18n("S"):OpenLayers.i18n("N");return e};OpenLayers.Rico=OpenLayers.Rico||{}; -OpenLayers.Rico.Corner={round:function(a,b){a=OpenLayers.Util.getElement(a);this._setOptions(b);var c=this.options.color;this.options.color=="fromElement"&&(c=this._background(a));var d=this.options.bgColor;this.options.bgColor=="fromParent"&&(d=this._background(a.offsetParent));this._roundCornersImpl(a,c,d)},changeColor:function(a,b){a.style.backgroundColor=b;for(var c=a.parentNode.getElementsByTagName("span"),d=0;d"+a.innerHTML+""},_roundTopCorners:function(a,b,c){for(var d=this._createCorner(c),e=0;e=0;e--)d.appendChild(this._createCornerSlice(b, -c,e,"bottom"));a.style.paddingBottom=0;a.appendChild(d)},_createCorner:function(a){var b=document.createElement("div");b.style.backgroundColor=this._isTransparent()?"transparent":a;return b},_createCornerSlice:function(a,b,c,d){var e=document.createElement("span"),f=e.style;f.backgroundColor=a;f.display="block";f.height="1px";f.overflow="hidden";f.fontSize="1px";a=this._borderColor(a,b);if(this.options.border&&c==0)f.borderTopStyle="solid",f.borderTopWidth="1px",f.borderLeftWidth="0px",f.borderRightWidth= -"0px",f.borderBottomWidth="0px",f.height="0px",f.borderColor=a;else if(a)f.borderColor=a,f.borderStyle="solid",f.borderWidth="0px 1px";if(!this.options.compact&&c==this.options.numSlices-1)f.height="2px";this._setMargin(e,c,d);this._setBorder(e,c,d);return e},_setOptions:function(a){this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:!0,border:!1,compact:!1};OpenLayers.Util.extend(this.options,a||{});this.options.numSlices=this.options.compact?2:4;if(this._isTransparent())this.options.blend= -!1},_whichSideTop:function(){if(this._hasString(this.options.corners,"all","top"))return"";if(this.options.corners.indexOf("tl")>=0&&this.options.corners.indexOf("tr")>=0)return"";if(this.options.corners.indexOf("tl")>=0)return"left";else if(this.options.corners.indexOf("tr")>=0)return"right";return""},_whichSideBottom:function(){if(this._hasString(this.options.corners,"all","bottom"))return"";if(this.options.corners.indexOf("bl")>=0&&this.options.corners.indexOf("br")>=0)return"";if(this.options.corners.indexOf("bl")>= -0)return"left";else if(this.options.corners.indexOf("br")>=0)return"right";return""},_borderColor:function(a,b){return a=="transparent"?b:this.options.border?this.options.border:this.options.blend?this._blend(b,a):""},_setMargin:function(a,b,c){b=this._marginSize(b);c=c=="top"?this._whichSideTop():this._whichSideBottom();c=="left"?(a.style.marginLeft=b+"px",a.style.marginRight="0px"):c=="right"?(a.style.marginRight=b+"px",a.style.marginLeft="0px"):(a.style.marginLeft=b+"px",a.style.marginRight=b+ -"px")},_setBorder:function(a,b,c){b=this._borderSize(b);c=c=="top"?this._whichSideTop():this._whichSideBottom();c=="left"?(a.style.borderLeftWidth=b+"px",a.style.borderRightWidth="0px"):c=="right"?(a.style.borderRightWidth=b+"px",a.style.borderLeftWidth="0px"):(a.style.borderLeftWidth=b+"px",a.style.borderRightWidth=b+"px");if(this.options.border!=!1)a.style.borderLeftWidth=b+"px",a.style.borderRightWidth=b+"px"},_marginSize:function(a){if(this._isTransparent())return 0;var b=[5,3,2,1],c=[3,2,1,0], -d=[2,1],e=[1,0];return this.options.compact&&this.options.blend?e[a]:this.options.compact?d[a]:this.options.blend?c[a]:b[a]},_borderSize:function(a){var b=[5,3,2,1],c=[2,1,1,1],d=[1,0],e=[0,2,0,0];if(this.options.compact&&(this.options.blend||this._isTransparent()))return 1;else if(this.options.compact)return d[a];else if(this.options.blend)return c[a];else if(this.options.border)return e[a];else if(this._isTransparent())return b[a];return 0},_hasString:function(a){for(var b=1;b= -0)return!0;return!1},_blend:function(a,b){var c=OpenLayers.Rico.Color.createFromHex(a);c.blend(OpenLayers.Rico.Color.createFromHex(b));return c},_background:function(a){try{return OpenLayers.Rico.Color.createColorFromBackground(a).asHex()}catch(b){return"#ffffff"}},_isTransparent:function(){return this.options.color=="transparent"},_isTopRounded:function(){return this._hasString(this.options.corners,"all","top","tl","tr")},_isBottomRounded:function(){return this._hasString(this.options.corners,"all", -"bottom","bl","br")},_hasSingleTextChild:function(a){return a.childNodes.length==1&&a.childNodes[0].nodeType==3}};OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},userError:function(a){alert(a)},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"}; -(function(){for(var a=document.getElementsByTagName("script"),b=0,c=a.length;bthis.duration&&this.stop()},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(a,b,c,d){return c*a/d+b},easeOut:function(a,b,c,d){return c*a/d+b},easeInOut:function(a,b,c,d){return c*a/d+b},CLASS_NAME:"OpenLayers.Easing.Linear"}; -OpenLayers.Easing.Expo={easeIn:function(a,b,c,d){return a==0?b:c*Math.pow(2,10*(a/d-1))+b},easeOut:function(a,b,c,d){return a==d?b+c:c*(-Math.pow(2,-10*a/d)+1)+b},easeInOut:function(a,b,c,d){return a==0?b:a==d?b+c:(a/=d/2)<1?c/2*Math.pow(2,10*(a-1))+b:c/2*(-Math.pow(2,-10*--a)+2)+b},CLASS_NAME:"OpenLayers.Easing.Expo"}; -OpenLayers.Easing.Quad={easeIn:function(a,b,c,d){return c*(a/=d)*a+b},easeOut:function(a,b,c,d){return-c*(a/=d)*(a-2)+b},easeInOut:function(a,b,c,d){return(a/=d/2)<1?c/2*a*a+b:-c/2*(--a*(a-2)-1)+b},CLASS_NAME:"OpenLayers.Easing.Quad"}; -OpenLayers.Lang={code:null,defaultCode:"en",getCode:function(){OpenLayers.Lang.code||OpenLayers.Lang.setCode();return OpenLayers.Lang.code},setCode:function(a){var b;a||(a=OpenLayers.BROWSER_NAME=="msie"?navigator.userLanguage:navigator.language);a=a.split("-");a[0]=a[0].toLowerCase();typeof OpenLayers.Lang[a[0]]=="object"&&(b=a[0]);if(a[1]){var c=a[0]+"-"+a[1].toUpperCase();typeof OpenLayers.Lang[c]=="object"&&(b=c)}if(!b)OpenLayers.Console.warn("Failed to find OpenLayers.Lang."+a.join("-")+" dictionary, falling back to default language"), -b=OpenLayers.Lang.defaultCode;OpenLayers.Lang.code=b},translate:function(a,b){var c=OpenLayers.Lang[OpenLayers.Lang.getCode()];(c=c&&c[a])||(c=a);b&&(c=OpenLayers.String.format(c,b));return c}};OpenLayers.i18n=OpenLayers.Lang.translate; -OpenLayers.Bounds=OpenLayers.Class({left:null,bottom:null,right:null,top:null,centerLonLat:null,initialize:function(a,b,c,d){if(a!=null)this.left=OpenLayers.Util.toFloat(a);if(b!=null)this.bottom=OpenLayers.Util.toFloat(b);if(c!=null)this.right=OpenLayers.Util.toFloat(c);if(d!=null)this.top=OpenLayers.Util.toFloat(d)},clone:function(){return new OpenLayers.Bounds(this.left,this.bottom,this.right,this.top)},equals:function(a){var b=!1;a!=null&&(b=this.left==a.left&&this.right==a.right&&this.top==a.top&& -this.bottom==a.bottom);return b},toString:function(){return[this.left,this.bottom,this.right,this.top].join(",")},toArray:function(a){return a===!0?[this.bottom,this.left,this.top,this.right]:[this.left,this.bottom,this.right,this.top]},toBBOX:function(a,b){a==null&&(a=6);var c=Math.pow(10,a),d=Math.round(this.left*c)/c,e=Math.round(this.bottom*c)/c,f=Math.round(this.right*c)/c,c=Math.round(this.top*c)/c;return b===!0?e+","+d+","+c+","+f:d+","+e+","+f+","+c},toGeometry:function(){return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(this.left, -this.bottom),new OpenLayers.Geometry.Point(this.right,this.bottom),new OpenLayers.Geometry.Point(this.right,this.top),new OpenLayers.Geometry.Point(this.left,this.top)])])},getWidth:function(){return this.right-this.left},getHeight:function(){return this.top-this.bottom},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight())},getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2)},getCenterLonLat:function(){if(!this.centerLonLat)this.centerLonLat= -new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2);return this.centerLonLat},scale:function(a,b){b==null&&(b=this.getCenterLonLat());var c,d;b.CLASS_NAME=="OpenLayers.LonLat"?(c=b.lon,d=b.lat):(c=b.x,d=b.y);return new OpenLayers.Bounds((this.left-c)*a+c,(this.bottom-d)*a+d,(this.right-c)*a+c,(this.top-d)*a+d)},add:function(a,b){if(a==null||b==null){var c=OpenLayers.i18n("boundsAddError");OpenLayers.Console.error(c);return null}return new OpenLayers.Bounds(this.left+a,this.bottom+ -b,this.right+a,this.top+b)},extend:function(a){var b=null;if(a){switch(a.CLASS_NAME){case "OpenLayers.LonLat":b=new OpenLayers.Bounds(a.lon,a.lat,a.lon,a.lat);break;case "OpenLayers.Geometry.Point":b=new OpenLayers.Bounds(a.x,a.y,a.x,a.y);break;case "OpenLayers.Bounds":b=a}if(b){this.centerLonLat=null;if(this.left==null||b.leftthis.right)this.right=b.right;if(this.top==null||b.top> -this.top)this.top=b.top}}},containsLonLat:function(a,b){return this.contains(a.lon,a.lat,b)},containsPixel:function(a,b){return this.contains(a.x,a.y,b)},contains:function(a,b,c){c==null&&(c=!0);if(a==null||b==null)return!1;var a=OpenLayers.Util.toFloat(a),b=OpenLayers.Util.toFloat(b),d=!1;return d=c?a>=this.left&&a<=this.right&&b>=this.bottom&&b<=this.top:a>this.left&&athis.bottom&&b=this.bottom&&a.top<=this.top||this.top>a.bottom&&this.top=this.left&&a.left<=this.right||this.left>=a.left&&this.left<=a.right,e=a.right>=this.left&&a.right<=this.right||this.right>=a.left&&this.right<=a.right,c=(a.bottom>=this.bottom&&a.bottom<=this.top||this.bottom>=a.bottom&&this.bottom<=a.top||c)&&(d||e);return c},containsBounds:function(a,b,c){b==null&&(b=!1);c==null&&(c=!0);var d=this.contains(a.left,a.bottom, -c),e=this.contains(a.right,a.bottom,c),f=this.contains(a.left,a.top,c),a=this.contains(a.right,a.top,c);return b?d||e||f||a:d&&e&&f&&a},determineQuadrant:function(a){var b="",c=this.getCenterLonLat();b+=a.lat=a.right&&e.right>a.right;)e=e.add(-a.getWidth(),0)}return e},CLASS_NAME:"OpenLayers.Bounds"}); -OpenLayers.Bounds.fromString=function(a,b){var c=a.split(",");return OpenLayers.Bounds.fromArray(c,b)};OpenLayers.Bounds.fromArray=function(a,b){return b===!0?new OpenLayers.Bounds(parseFloat(a[1]),parseFloat(a[0]),parseFloat(a[3]),parseFloat(a[2])):new OpenLayers.Bounds(parseFloat(a[0]),parseFloat(a[1]),parseFloat(a[2]),parseFloat(a[3]))};OpenLayers.Bounds.fromSize=function(a){return new OpenLayers.Bounds(0,a.h,a.w,0)}; -OpenLayers.Bounds.oppositeQuadrant=function(a){var b="";b+=a.charAt(0)=="t"?"b":"t";b+=a.charAt(1)=="l"?"r":"l";return b}; -OpenLayers.Element={visible:function(a){return OpenLayers.Util.getElement(a).style.display!="none"},toggle:function(){for(var a=0,b=arguments.length;aa.right;)b.lon-=a.getWidth()}return b},CLASS_NAME:"OpenLayers.LonLat"}); -OpenLayers.LonLat.fromString=function(a){a=a.split(",");return new OpenLayers.LonLat(a[0],a[1])};OpenLayers.LonLat.fromArray=function(a){var b=OpenLayers.Util.isArray(a);return new OpenLayers.LonLat(b&&a[0],b&&a[1])}; -OpenLayers.Pixel=OpenLayers.Class({x:0,y:0,initialize:function(a,b){this.x=parseFloat(a);this.y=parseFloat(b)},toString:function(){return"x="+this.x+",y="+this.y},clone:function(){return new OpenLayers.Pixel(this.x,this.y)},equals:function(a){var b=!1;a!=null&&(b=this.x==a.x&&this.y==a.y||isNaN(this.x)&&isNaN(this.y)&&isNaN(a.x)&&isNaN(a.y));return b},distanceTo:function(a){return Math.sqrt(Math.pow(this.x-a.x,2)+Math.pow(this.y-a.y,2))},add:function(a,b){if(a==null||b==null){var c=OpenLayers.i18n("pixelAddError"); -OpenLayers.Console.error(c);return null}return new OpenLayers.Pixel(this.x+a,this.y+b)},offset:function(a){var b=this.clone();a&&(b=this.add(a.x,a.y));return b},CLASS_NAME:"OpenLayers.Pixel"}); -OpenLayers.Size=OpenLayers.Class({w:0,h:0,initialize:function(a,b){this.w=parseFloat(a);this.h=parseFloat(b)},toString:function(){return"w="+this.w+",h="+this.h},clone:function(){return new OpenLayers.Size(this.w,this.h)},equals:function(a){var b=!1;a!=null&&(b=this.w==a.w&&this.h==a.h||isNaN(this.w)&&isNaN(this.h)&&isNaN(a.w)&&isNaN(a.h));return b},CLASS_NAME:"OpenLayers.Size"}); -OpenLayers.Event={observers:!1,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(a){return a.target||a.srcElement},isSingleTouch:function(a){return a.touches&&a.touches.length==1},isMultiTouch:function(a){return a.touches&&a.touches.length>1},isLeftClick:function(a){return a.which&&a.which==1||a.button&&a.button==1},isRightClick:function(a){return a.which&&a.which==3||a.button&&a.button==2},stop:function(a,b){if(!b)a.preventDefault? -a.preventDefault():a.returnValue=!1;a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},findElement:function(a,b){for(var c=OpenLayers.Event.element(a);c.parentNode&&(!c.tagName||c.tagName.toUpperCase()!=b.toUpperCase());)c=c.parentNode;return c},observe:function(a,b,c,d){a=OpenLayers.Util.getElement(a);d=d||!1;if(b=="keypress"&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||a.attachEvent))b="keydown";if(!this.observers)this.observers={};if(!a._eventCacheID){var e="eventCacheID_";a.id&& -(e=a.id+"_"+e);a._eventCacheID=OpenLayers.Util.createUniqueID(e)}e=a._eventCacheID;this.observers[e]||(this.observers[e]=[]);this.observers[e].push({element:a,name:b,observer:c,useCapture:d});a.addEventListener?a.addEventListener(b,c,d):a.attachEvent&&a.attachEvent("on"+b,c)},stopObservingElement:function(a){a=OpenLayers.Util.getElement(a)._eventCacheID;this._removeElementObservers(OpenLayers.Event.observers[a])},_removeElementObservers:function(a){if(a)for(var b=a.length-1;b>=0;b--){var c=a[b];OpenLayers.Event.stopObserving.apply(this, -[c.element,c.name,c.observer,c.useCapture])}},stopObserving:function(a,b,c,d){var d=d||!1,a=OpenLayers.Util.getElement(a),e=a._eventCacheID;if(b=="keypress"&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||a.detachEvent))b="keydown";var f=!1,g=OpenLayers.Event.observers[e];if(g)for(var h=0;!f&&h=0;--a)this.controls[a].destroy();this.controls=null}if(this.layers!=null){for(a=this.layers.length-1;a>=0;--a)this.layers[a].destroy(!1);this.layers=null}this.viewPortDiv&&this.div.removeChild(this.viewPortDiv); -this.viewPortDiv=null;if(this.eventListeners)this.events.un(this.eventListeners),this.eventListeners=null;this.events.destroy();this.events=null},setOptions:function(a){var b=this.minPx&&a.restrictedExtent!=this.restrictedExtent;OpenLayers.Util.extend(this,a);b&&this.moveTo(this.getCachedCenter(),this.zoom,{forceZoomChange:!0})},getTileSize:function(){return this.tileSize},getBy:function(a,b,c){var d=typeof c.test=="function";return OpenLayers.Array.filter(this[a],function(a){return a[b]==c||d&&c.test(a[b])})}, -getLayersBy:function(a,b){return this.getBy("layers",a,b)},getLayersByName:function(a){return this.getLayersBy("name",a)},getLayersByClass:function(a){return this.getLayersBy("CLASS_NAME",a)},getControlsBy:function(a,b){return this.getBy("controls",a,b)},getControlsByClass:function(a){return this.getControlsBy("CLASS_NAME",a)},getLayer:function(a){for(var b=null,c=0,d=this.layers.length;cthis.layers.length)b=this.layers.length;if(c!=b){this.layers.splice(c,1);this.layers.splice(b,0,a);for(var c=0,d=this.layers.length;c=0;--c)this.removePopup(this.popups[c]); -a.map=this;this.popups.push(a);if(c=a.draw())c.style.zIndex=this.Z_INDEX_BASE.Popup+this.popups.length,this.layerContainerDiv.appendChild(c)},removePopup:function(a){OpenLayers.Util.removeItem(this.popups,a);if(a.div)try{this.layerContainerDiv.removeChild(a.div)}catch(b){}a.map=null},getSize:function(){var a=null;this.size!=null&&(a=this.size.clone());return a},updateSize:function(){var a=this.getCurrentSize();if(a&&!isNaN(a.h)&&!isNaN(a.w)){this.events.clearMouseCache();var b=this.getSize();if(b== -null)this.size=b=a;if(!a.equals(b)){this.size=a;a=0;for(b=this.layers.length;a=this.minPx.x+h?Math.round(a):0;b=f<=this.maxPx.y-i&&f>=this.minPx.y+i?Math.round(b):0;c=this.minPx.x;d=this.maxPx.x;if(a||b){if(!this.dragging)this.dragging=!0,this.events.triggerEvent("movestart");this.center=null;if(a)this.layerContainerDiv.style.left= -parseInt(this.layerContainerDiv.style.left)-a+"px",this.minPx.x-=a,this.maxPx.x-=a,g&&(this.maxPx.x>d&&(this.maxPx.x-=d-c),this.minPx.xthis.restrictedExtent.getWidth()? -a=new OpenLayers.LonLat(g.lon,a.lat):f.leftthis.restrictedExtent.right&&(a=a.add(this.restrictedExtent.right-f.right,0));f.getHeight()>this.restrictedExtent.getHeight()?a=new OpenLayers.LonLat(a.lon,g.lat):f.bottomthis.restrictedExtent.top&&(a=a.add(0,this.restrictedExtent.top-f.top))}}e=e||this.isValidZoomLevel(b)&&b!=this.getZoom(); -f=this.isValidLonLat(a)&&!a.equals(this.center);if(e||f||d){d||this.events.triggerEvent("movestart");if(f)!e&&this.center&&this.centerLayerContainer(a),this.center=a.clone();a=e?this.getResolutionForZoom(b):this.getResolution();if(e||this.layerContainerOrigin==null){this.layerContainerOrigin=this.getCachedCenter();this.layerContainerDiv.style.left="0px";this.layerContainerDiv.style.top="0px";var h=this.getMaxExtent({restricted:!0}),f=h.getCenterLonLat(),g=this.center.lon-f.lon,i=f.lat-this.center.lat, -f=Math.round(h.getWidth()/a),h=Math.round(h.getHeight()/a),g=(this.size.w-f)/2-g/a,i=(this.size.h-h)/2-i/a;this.minPx=new OpenLayers.Pixel(g,i);this.maxPx=new OpenLayers.Pixel(g+f,i+h)}if(e)this.zoom=b,this.resolution=a,this.viewRequestID++;a=this.getExtent();this.baseLayer.visibility&&(this.baseLayer.moveTo(a,e,c.dragging),c.dragging||this.baseLayer.events.triggerEvent("moveend",{zoomChanged:e}));a=this.baseLayer.getExtent();for(b=this.layers.length-1;b>=0;--b)if(f=this.layers[b],f!==this.baseLayer&& -!f.isBaseLayer){g=f.calculateInRange();if(f.inRange!=g)(f.inRange=g)||f.display(!1),this.events.triggerEvent("changelayer",{layer:f,property:"visibility"});g&&f.visibility&&(f.moveTo(a,e,c.dragging),c.dragging||f.events.triggerEvent("moveend",{zoomChanged:e}))}this.events.triggerEvent("move");d||this.events.triggerEvent("moveend");if(e){b=0;for(c=this.popups.length;b=0&&a0&&(a=this.layers[0].getResolution());return a},getUnits:function(){var a=null;if(this.baseLayer!=null)a=this.baseLayer.units;return a},getScale:function(){var a=null;this.baseLayer!=null&&(a=this.getResolution(), -a=OpenLayers.Util.getScaleFromResolution(a,this.baseLayer.units));return a},getZoomForExtent:function(a,b){var c=null;this.baseLayer!=null&&(c=this.baseLayer.getZoomForExtent(a,b));return c},getResolutionForZoom:function(a){var b=null;this.baseLayer&&(b=this.baseLayer.getResolutionForZoom(a));return b},getZoomForResolution:function(a,b){var c=null;this.baseLayer!=null&&(c=this.baseLayer.getZoomForResolution(a,b));return c},zoomTo:function(a){this.isValidZoomLevel(a)&&this.setCenter(null,a)},zoomIn:function(){this.zoomTo(this.getZoom()+ -1)},zoomOut:function(){this.zoomTo(this.getZoom()-1)},zoomToExtent:function(a,b){var c=a.getCenterLonLat();if(this.baseLayer.wrapDateLine){c=this.getMaxExtent();for(a=a.clone();a.right=0){this.initResolutions();b&&this.map.baseLayer===this&&(this.map.setCenter(this.map.getCenter(),this.map.getZoomForResolution(c),!1,!0),this.map.events.triggerEvent("changebaselayer", -{layer:this}));break}}},onMapResize:function(){},redraw:function(){var a=!1;if(this.map){this.inRange=this.calculateInRange();var b=this.getExtent();b&&this.inRange&&this.visibility&&(this.moveTo(b,!0,!1),this.events.triggerEvent("moveend",{zoomChanged:!0}),a=!0)}return a},moveTo:function(){var a=this.visibility;this.isBaseLayer||(a=a&&this.inRange);this.display(a)},moveByPx:function(){},setMap:function(a){if(this.map==null){this.map=a;this.maxExtent=this.maxExtent||this.map.maxExtent;this.minExtent= -this.minExtent||this.map.minExtent;this.projection=this.projection||this.map.projection;if(typeof this.projection=="string")this.projection=new OpenLayers.Projection(this.projection);this.units=this.projection.getUnits()||this.units||this.map.units;this.initResolutions();if(!this.isBaseLayer)this.inRange=this.calculateInRange(),this.div.style.display=this.visibility&&this.inRange?"":"none";this.setTileSize()}},afterAdd:function(){},removeMap:function(){},getImageSize:function(){return this.imageSize|| -this.tileSize},setTileSize:function(a){this.tileSize=a=a?a:this.tileSize?this.tileSize:this.map.getTileSize();if(this.gutter)this.imageOffset=new OpenLayers.Pixel(-this.gutter,-this.gutter),this.imageSize=new OpenLayers.Size(a.w+2*this.gutter,a.h+2*this.gutter)},getVisibility:function(){return this.visibility},setVisibility:function(a){if(a!=this.visibility)this.visibility=a,this.display(a),this.redraw(),this.map!=null&&this.map.events.triggerEvent("changelayer",{layer:this,property:"visibility"}), -this.events.triggerEvent("visibilitychanged")},display:function(a){if(a!=(this.div.style.display!="none"))this.div.style.display=a&&this.calculateInRange()?"block":"none"},calculateInRange:function(){var a=!1;this.alwaysInRange?a=!0:this.map&&(a=this.map.getResolution(),a=a>=this.minResolution&&a<=this.maxResolution);return a},setIsBaseLayer:function(a){if(a!=this.isBaseLayer)this.isBaseLayer=a,this.map!=null&&this.map.events.triggerEvent("changebaselayer",{layer:this})},initResolutions:function(){var a, -b,c,d={},e=!0;for(a=0,b=this.RESOLUTION_PROPERTIES.length;a=a&&(f=h,e=c),h<=a){g=h;break}c=f-g;c=c>0?e+(f-a)/c:e}else{f=Number.POSITIVE_INFINITY;for(c=0,d=this.resolutions.length;cf)break;f=e}else if(this.resolutions[c]=a.bottom-f*this.buffer||s=0&&h=0&&(i=this.grid[g][h]);i!=null&&!i.queued? -(a.unshift(i),i.queued=!0,f=0,c=g,d=h):(e=(e+1)%4,f++)}b=0;for(c=a.length;b-this.tileSize.w*(b-1)?this.shiftColumn(!0): -c.x<-this.tileSize.w*b?this.shiftColumn(!1):c.y>-this.tileSize.h*(b-1)?this.shiftRow(!0):c.y<-this.tileSize.h*b?this.shiftRow(!1):a=!1;if(a)this.timerId=window.setTimeout(this._moveGriddedTiles,0)},shiftRow:function(a){var b=this.grid,c=b[a?0:this.grid.length-1],d=this.map.getResolution(),e=a?-this.tileSize.h:this.tileSize.h;d*=-e;for(var f=a?b.pop():b.shift(),g=0,h=c.length;ga;)for(var c=this.grid.pop(),d=0,e=c.length;d -b;){d=0;for(e=this.grid.length;d=200&&b.status<300)this.events.triggerEvent("success",a),e&&e(b);if(b.status&&(b.status<200||b.status>=300))this.events.triggerEvent("failure",a),f&&f(b)},GET:function(a){a=OpenLayers.Util.extend(a,{method:"GET"});return OpenLayers.Request.issue(a)}, -POST:function(a){a=OpenLayers.Util.extend(a,{method:"POST"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},PUT:function(a){a=OpenLayers.Util.extend(a,{method:"PUT"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},DELETE:function(a){a=OpenLayers.Util.extend(a, -{method:"DELETE"});return OpenLayers.Request.issue(a)},HEAD:function(a){a=OpenLayers.Util.extend(a,{method:"HEAD"});return OpenLayers.Request.issue(a)},OPTIONS:function(a){a=OpenLayers.Util.extend(a,{method:"OPTIONS"});return OpenLayers.Request.issue(a)}}; -(function(){function a(){this._object=f&&!i?new f:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function b(){return new a}function c(a){b.onreadystatechange&&b.onreadystatechange.apply(a);a.dispatchEvent({type:"readystatechange",bubbles:!1,cancelable:!1,timeStamp:new Date+0})}function d(a){try{a.responseText=a._object.responseText}catch(b){}try{var c=a._object,d=c.responseXML,e=c.responseText;if(h&&e&&d&&!d.documentElement&&c.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/))d= -new window.ActiveXObject("Microsoft.XMLDOM"),d.async=!1,d.validateOnParse=!1,d.loadXML(e);a.responseXML=d&&(h&&d.parseError!=0||!d.documentElement||d.documentElement&&d.documentElement.tagName=="parsererror")?null:d}catch(f){}try{a.status=a._object.status}catch(g){}try{a.statusText=a._object.statusText}catch(i){}}function e(a){a._object.onreadystatechange=new window.Function}var f=window.XMLHttpRequest,g=!!window.controllers,h=window.document.all&&!window.opera,i=h&&window.navigator.userAgent.match(/MSIE 7.0/); -b.prototype=a.prototype;if(g&&f.wrapped)b.wrapped=f.wrapped;b.UNSENT=0;b.OPENED=1;b.HEADERS_RECEIVED=2;b.LOADING=3;b.DONE=4;b.prototype.readyState=b.UNSENT;b.prototype.responseText="";b.prototype.responseXML=null;b.prototype.status=0;b.prototype.statusText="";b.prototype.priority="NORMAL";b.prototype.onreadystatechange=null;b.onreadystatechange=null;b.onopen=null;b.onsend=null;b.onabort=null;b.prototype.open=function(a,f,i,r,m){delete this._headers;arguments.length<3&&(i=!0);this._async=i;var j=this, -n=this.readyState,l;h&&i&&(l=function(){n!=b.DONE&&(e(j),j.abort())},window.attachEvent("onunload",l));b.onopen&&b.onopen.apply(this,arguments);arguments.length>4?this._object.open(a,f,i,r,m):arguments.length>3?this._object.open(a,f,i,r):this._object.open(a,f,i);this.readyState=b.OPENED;c(this);this._object.onreadystatechange=function(){if(!g||i)j.readyState=j._object.readyState,d(j),j._aborted?j.readyState=b.UNSENT:(j.readyState==b.DONE&&(delete j._data,e(j),h&&i&&window.detachEvent("onunload",l)), -n!=j.readyState&&c(j),n=j.readyState)}};b.prototype.send=function(a){b.onsend&&b.onsend.apply(this,arguments);arguments.length||(a=null);a&&a.nodeType&&(a=window.XMLSerializer?(new window.XMLSerializer).serializeToString(a):a.xml,oRequest._headers["Content-Type"]||oRequest._object.setRequestHeader("Content-Type","application/xml"));this._data=a;this._object.send(this._data);if(g&&!this._async){this.readyState=b.OPENED;for(d(this);this.readyStateb.UNSENT)this._aborted=!0;this._object.abort();e(this);this.readyState=b.UNSENT;delete this._data};b.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};b.prototype.getResponseHeader=function(a){return this._object.getResponseHeader(a)};b.prototype.setRequestHeader=function(a,b){if(!this._headers)this._headers={};this._headers[a]=b;return this._object.setRequestHeader(a,b)}; -b.prototype.addEventListener=function(a,b,c){for(var d=0,e;e=this._listeners[d];d++)if(e[0]==a&&e[1]==b&&e[2]==c)return;this._listeners.push([a,b,c])};b.prototype.removeEventListener=function(a,b,c){for(var d=0,e;e=this._listeners[d];d++)if(e[0]==a&&e[1]==b&&e[2]==c)break;e&&this._listeners.splice(d,1)};b.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){}, -initEvent:function(){}};a.type=="readystatechange"&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var b=0,c;c=this._listeners[b];b++)c[0]==a.type&&!c[2]&&(c[1].handleEvent||c[1]).apply(this,[a])};b.prototype.toString=function(){return"[object XMLHttpRequest]"};b.toString=function(){return"[XMLHttpRequest]"};if(!window.Function.prototype.apply)window.Function.prototype.apply=function(a,b){b||(b=[]);a.__func=this;a.__func(b[0],b[1],b[2], -b[3],b[4]);delete a.__func};OpenLayers.Request.XMLHttpRequest=b})();OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(a){OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest",{statusText:a.statusText}))};OpenLayers.loadURL=function(a,b,c,d,e){typeof b=="string"&&(b=OpenLayers.Util.getParameters(b));return OpenLayers.Request.GET({url:a,params:b,success:d?d:OpenLayers.nullHandler,failure:e?e:OpenLayers.nullHandler,scope:c})}; -OpenLayers.parseXMLString=function(a){var b=a.indexOf("<");b>0&&(a=a.substring(b));return OpenLayers.Util.Try(function(){var b=new ActiveXObject("Microsoft.XMLDOM");b.loadXML(a);return b},function(){return(new DOMParser).parseFromString(a,"text/xml")},function(){var b=new XMLHttpRequest;b.open("GET","data:text/xml;charset=utf-8,"+encodeURIComponent(a),!1);b.overrideMimeType&&b.overrideMimeType("text/xml");b.send(null);return b.responseXML})}; -OpenLayers.Ajax={emptyFunction:function(){},getTransport:function(){return OpenLayers.Util.Try(function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")})||!1},activeRequestCount:0}; -OpenLayers.Ajax.Responders={responders:[],register:function(a){for(var b=0;b-1?"&":"?")+a:/Konqueror|Safari|KHTML/.test(navigator.userAgent)&&(a+="&_=");try{var b=new OpenLayers.Ajax.Response(this);if(this.options.onCreate)this.options.onCreate(b);OpenLayers.Ajax.Responders.dispatch("onCreate",this,b);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);this.options.asynchronous&&window.setTimeout(OpenLayers.Function.bind(this.respondToReadyState, -this,1),10);this.transport.onreadystatechange=OpenLayers.Function.bind(this.onStateChange,this);this.setRequestHeaders();this.body=this.method=="post"?this.options.postBody||a:null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType)this.onStateChange()}catch(c){this.dispatchException(c)}},onStateChange:function(){var a=this.transport.readyState;a>1&&!(a==4&&this._complete)&&this.respondToReadyState(this.transport.readyState)},setRequestHeaders:function(){var a= -{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*",OpenLayers:!0};if(this.method=="post"&&(a["Content-type"]=this.options.contentType+(this.options.encoding?"; charset="+this.options.encoding:""),this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005))a.Connection="close";if(typeof this.options.requestHeaders=="object"){var b=this.options.requestHeaders;if(typeof b.push=="function")for(var c=0,d=b.length;c< -d;c+=2)a[b[c]]=b[c+1];else for(c in b)a[c]=b[c]}for(var e in a)this.transport.setRequestHeader(e,a[e])},success:function(){var a=this.getStatus();return!a||a>=200&&a<300},getStatus:function(){try{return this.transport.status||0}catch(a){return 0}},respondToReadyState:function(a){var a=OpenLayers.Ajax.Request.Events[a],b=new OpenLayers.Ajax.Response(this);if(a=="Complete"){try{this._complete=!0,(this.options["on"+b.status]||this.options["on"+(this.success()?"Success":"Failure")]||OpenLayers.Ajax.emptyFunction)(b)}catch(c){this.dispatchException(c)}b.getHeader("Content-type")}try{(this.options["on"+ -a]||OpenLayers.Ajax.emptyFunction)(b),OpenLayers.Ajax.Responders.dispatch("on"+a,this,b)}catch(d){this.dispatchException(d)}if(a=="Complete")this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction},getHeader:function(a){try{return this.transport.getResponseHeader(a)}catch(b){return null}},dispatchException:function(a){var b=this.options.onException;if(b)b(this,a),OpenLayers.Ajax.Responders.dispatch("onException",this,a);else{for(var b=!1,c=OpenLayers.Ajax.Responders.responders,d=0;d2&&(!window.attachEvent||window.opera)||b==4)this.status=this.getStatus(),this.statusText=this.getStatusText(),this.responseText=a.responseText==null?"":String(a.responseText);if(b==4)a=a.responseXML,this.responseXML=a===void 0?null:a},getStatus:OpenLayers.Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText|| -""}catch(a){return""}},getHeader:OpenLayers.Ajax.Request.prototype.getHeader,getResponseHeader:function(a){return this.transport.getResponseHeader(a)}});OpenLayers.Ajax.getElementsByTagNameNS=function(a,b,c,d){var e=null;return e=a.getElementsByTagNameNS?a.getElementsByTagNameNS(b,d):a.getElementsByTagName(c+":"+d)};OpenLayers.Ajax.serializeXMLToString=function(a){return(new XMLSerializer).serializeToString(a)}; -OpenLayers.Handler=OpenLayers.Class({id:null,control:null,map:null,keyMask:null,active:!1,evt:null,initialize:function(a,b,c){OpenLayers.Util.extend(this,c);this.control=a;this.callbacks=b;(a=this.map||a.map)&&this.setMap(a);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},setMap:function(a){this.map=a},checkModifiers:function(a){return this.keyMask==null?!0:((a.shiftKey?OpenLayers.Handler.MOD_SHIFT:0)|(a.ctrlKey?OpenLayers.Handler.MOD_CTRL:0)|(a.altKey?OpenLayers.Handler.MOD_ALT:0))== -this.keyMask},activate:function(){if(this.active)return!1;for(var a=OpenLayers.Events.prototype.BROWSER_EVENTS,b=0,c=a.length;b=this.down.xy.distanceTo(a.xy))&&this.touch&&this.down.touches.length===this.last.touches.length)for(var a=0,c=this.down.touches.length;a< -c;++a)if(this.getTouchDistance(this.down.touches[a],this.last.touches[a])>this.pixelTolerance){b=!1;break}return b},getTouchDistance:function(a,b){return Math.sqrt(Math.pow(a.clientX-b.clientX,2)+Math.pow(a.clientY-b.clientY,2))},passesDblclickTolerance:function(){var a=!0;this.down&&this.first&&(a=this.down.xy.distanceTo(this.first.xy)<=this.dblclickTolerance);return a},clearTimer:function(){if(this.timerId!=null)window.clearTimeout(this.timerId),this.timerId=null;if(this.rightclickTimerId!=null)window.clearTimeout(this.rightclickTimerId), -this.rightclickTimerId=null},delayedCall:function(a){this.timerId=null;a&&this.callback("click",[a])},getEventInfo:function(a){var b;if(a.touches){var c=a.touches.length;b=Array(c);for(var d,e=0;e0.5},isDark:function(){return!this.isBright()},asRGB:function(){return"rgb("+ -this.rgb.r+","+this.rgb.g+","+this.rgb.b+")"},asHex:function(){return"#"+this.rgb.r.toColorPart()+this.rgb.g.toColorPart()+this.rgb.b.toColorPart()},asHSB:function(){return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r,this.rgb.g,this.rgb.b)},toString:function(){return this.asHex()}}); -OpenLayers.Rico.Color.createFromHex=function(a){if(a.length==4)for(var b=a,a="#",c=1;c<4;c++)a+=b.charAt(c)+b.charAt(c);a.indexOf("#")==0&&(a=a.substring(1));b=a.substring(0,2);c=a.substring(2,4);a=a.substring(4,6);return new OpenLayers.Rico.Color(parseInt(b,16),parseInt(c,16),parseInt(a,16))}; -OpenLayers.Rico.Color.createColorFromBackground=function(a){var b=OpenLayers.Element.getStyle(OpenLayers.Util.getElement(a),"backgroundColor");if(b=="transparent"&&a.parentNode)return OpenLayers.Rico.Color.createColorFromBackground(a.parentNode);return b==null?new OpenLayers.Rico.Color(255,255,255):b.indexOf("rgb(")==0?(a=b.substring(4,b.length-1).split(","),new OpenLayers.Rico.Color(parseInt(a[0]),parseInt(a[1]),parseInt(a[2]))):b.indexOf("#")==0?OpenLayers.Rico.Color.createFromHex(b):new OpenLayers.Rico.Color(255, -255,255)}; -OpenLayers.Rico.Color.HSBtoRGB=function(a,b,c){var d=0,e=0,f=0;if(b==0)f=e=d=parseInt(c*255+0.5);else{var a=(a-Math.floor(a))*6,g=a-Math.floor(a),h=c*(1-b),i=c*(1-b*g),b=c*(1-b*(1-g));switch(parseInt(a)){case 0:d=c*255+0.5;e=b*255+0.5;f=h*255+0.5;break;case 1:d=i*255+0.5;e=c*255+0.5;f=h*255+0.5;break;case 2:d=h*255+0.5;e=c*255+0.5;f=b*255+0.5;break;case 3:d=h*255+0.5;e=i*255+0.5;f=c*255+0.5;break;case 4:d=b*255+0.5;e=h*255+0.5;f=c*255+0.5;break;case 5:d=c*255+0.5,e=h*255+0.5,f=i*255+0.5}}return{r:parseInt(d),g:parseInt(e), -b:parseInt(f)}};OpenLayers.Rico.Color.RGBtoHSB=function(a,b,c){var d,e=a>b?a:b;c>e&&(e=c);var f=a=0;--a)this._removeButton(this.buttons[a])},doubleClick:function(a){OpenLayers.Event.stop(a); -return!1},buttonDown:function(a){if(OpenLayers.Event.isLeftClick(a)){switch(this.action){case "panup":this.map.pan(0,-this.getSlideFactor("h"));break;case "pandown":this.map.pan(0,this.getSlideFactor("h"));break;case "panleft":this.map.pan(-this.getSlideFactor("w"),0);break;case "panright":this.map.pan(this.getSlideFactor("w"),0);break;case "zoomin":this.map.zoomIn();break;case "zoomout":this.map.zoomOut();break;case "zoomworld":this.map.zoomToMaxExtent()}OpenLayers.Event.stop(a)}},CLASS_NAME:"OpenLayers.Control.PanZoom"}); -OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4; -OpenLayers.Control.PanZoomBar=OpenLayers.Class(OpenLayers.Control.PanZoom,{zoomStopWidth:18,zoomStopHeight:11,slider:null,sliderEvents:null,zoombarDiv:null,divEvents:null,zoomWorldIcon:!1,panIcons:!0,forceFixedZoomLevel:!1,mouseDragStart:null,deltaY:null,zoomStart:null,destroy:function(){this._removeZoomBar();this.map.events.un({changebaselayer:this.redraw,scope:this});OpenLayers.Control.PanZoom.prototype.destroy.apply(this,arguments);delete this.mouseDragStart;delete this.zoomStart},setMap:function(a){OpenLayers.Control.PanZoom.prototype.setMap.apply(this, -arguments);this.map.events.register("changebaselayer",this,this.redraw)},redraw:function(){this.div!=null&&(this.removeButtons(),this._removeZoomBar());this.draw()},draw:function(a){OpenLayers.Control.prototype.draw.apply(this,arguments);a=this.position.clone();this.buttons=[];var b=new OpenLayers.Size(18,18);if(this.panIcons){var c=new OpenLayers.Pixel(a.x+b.w/2,a.y),d=b.w;this.zoomWorldIcon&&(c=new OpenLayers.Pixel(a.x+b.w,a.y));this._addButton("panup","north-mini.png",c,b);a.y=c.y+b.h;this._addButton("panleft", -"west-mini.png",a,b);this.zoomWorldIcon&&(this._addButton("zoomworld","zoom-world-mini.png",a.add(b.w,0),b),d*=2);this._addButton("panright","east-mini.png",a.add(d,0),b);this._addButton("pandown","south-mini.png",c.add(0,b.h*2),b);this._addButton("zoomin","zoom-plus-mini.png",c.add(0,b.h*3+5),b);c=this._addZoomBar(c.add(0,b.h*4+5));this._addButton("zoomout","zoom-minus-mini.png",c,b)}else this._addButton("zoomin","zoom-plus-mini.png",a,b),c=this._addZoomBar(a.add(0,b.h)),this._addButton("zoomout", -"zoom-minus-mini.png",c,b),this.zoomWorldIcon&&(c=c.add(0,b.h+3),this._addButton("zoomworld","zoom-world-mini.png",c,b));return this.div},_addZoomBar:function(a){var b=OpenLayers.Util.getImagesLocation(),c=this.id+"_"+this.map.id,d=this.map.getNumZoomLevels()-1-this.map.getZoom(),d=OpenLayers.Util.createAlphaImageDiv(c,a.add(-1,d*this.zoomStopHeight),new OpenLayers.Size(20,9),b+"slider.png","absolute");d.style.cursor="move";this.slider=d;this.sliderEvents=new OpenLayers.Events(this,d,null,!0,{includeXY:!0}); -this.sliderEvents.on({touchstart:this.zoomBarDown,touchmove:this.zoomBarDrag,touchend:this.zoomBarUp,mousedown:this.zoomBarDown,mousemove:this.zoomBarDrag,mouseup:this.zoomBarUp,dblclick:this.doubleClick,click:this.doubleClick});var e=new OpenLayers.Size;e.h=this.zoomStopHeight*this.map.getNumZoomLevels();e.w=this.zoomStopWidth;c=null;OpenLayers.Util.alphaHack()?(c=this.id+"_"+this.map.id,c=OpenLayers.Util.createAlphaImageDiv(c,a,new OpenLayers.Size(e.w,this.zoomStopHeight),b+"zoombar.png","absolute", -null,"crop"),c.style.height=e.h+"px"):c=OpenLayers.Util.createDiv("OpenLayers_Control_PanZoomBar_Zoombar"+this.map.id,a,e,b+"zoombar.png");c.style.cursor="pointer";this.zoombarDiv=c;this.divEvents=new OpenLayers.Events(this,c,null,!0,{includeXY:!0});this.divEvents.on({touchmove:this.passEventToSlider,mousedown:this.divClick,mousemove:this.passEventToSlider,dblclick:this.doubleClick,click:this.doubleClick});this.div.appendChild(c);this.startTop=parseInt(c.style.top);this.div.appendChild(d);this.map.events.register("zoomend", -this,this.moveZoomBar);return a=a.add(0,this.zoomStopHeight*this.map.getNumZoomLevels())},_removeZoomBar:function(){this.sliderEvents.un({touchmove:this.zoomBarDrag,mousedown:this.zoomBarDown,mousemove:this.zoomBarDrag,mouseup:this.zoomBarUp,dblclick:this.doubleClick,click:this.doubleClick});this.sliderEvents.destroy();this.divEvents.un({touchmove:this.passEventToSlider,mousedown:this.divClick,mousemove:this.passEventToSlider,dblclick:this.doubleClick,click:this.doubleClick});this.divEvents.destroy(); -this.div.removeChild(this.zoombarDiv);this.zoombarDiv=null;this.div.removeChild(this.slider);this.slider=null;this.map.events.unregister("zoomend",this,this.moveZoomBar)},passEventToSlider:function(a){this.sliderEvents.handleBrowserEvent(a)},divClick:function(a){if(OpenLayers.Event.isLeftClick(a)){var b=a.xy.y/this.zoomStopHeight;if(this.forceFixedZoomLevel||!this.map.fractionalZoom)b=Math.floor(b);b=this.map.getNumZoomLevels()-1-b;b=Math.min(Math.max(b,0),this.map.getNumZoomLevels()-1);this.map.zoomTo(b); -OpenLayers.Event.stop(a)}},zoomBarDown:function(a){if(OpenLayers.Event.isLeftClick(a)||OpenLayers.Event.isSingleTouch(a))this.map.events.on({touchmove:this.passEventToSlider,mousemove:this.passEventToSlider,mouseup:this.passEventToSlider,scope:this}),this.mouseDragStart=a.xy.clone(),this.zoomStart=a.xy.clone(),this.div.style.cursor="move",this.zoombarDiv.offsets=null,OpenLayers.Event.stop(a)},zoomBarDrag:function(a){if(this.mouseDragStart!=null){var b=this.mouseDragStart.y-a.xy.y,c=OpenLayers.Util.pagePosition(this.zoombarDiv); -if(a.clientY-c[1]>0&&a.clientY-c[1]0&&(a=a.substring(b));b=OpenLayers.Util.Try(OpenLayers.Function.bind(function(){var b;b=window.ActiveXObject&&!this.xmldom?new ActiveXObject("Microsoft.XMLDOM"):this.xmldom;b.loadXML(a);return b},this),function(){return(new DOMParser).parseFromString(a,"text/xml")},function(){var b=new XMLHttpRequest;b.open("GET","data:text/xml;charset=utf-8,"+encodeURIComponent(a),!1);b.overrideMimeType&& -b.overrideMimeType("text/xml");b.send(null);return b.responseXML});if(this.keepData)this.data=b;return b},write:function(a){if(this.xmldom)a=a.xml;else{var b=new XMLSerializer;if(a.nodeType==1){var c=document.implementation.createDocument("","",null);c.importNode&&(a=c.importNode(a,!0));c.appendChild(a);a=b.serializeToString(c)}else a=b.serializeToString(a)}return a},createElementNS:function(a,b){return this.xmldom?typeof a=="string"?this.xmldom.createNode(1,b,a):this.xmldom.createNode(1,b,""):document.createElementNS(a, -b)},createTextNode:function(a){typeof a!=="string"&&(a=String(a));return this.xmldom?this.xmldom.createTextNode(a):document.createTextNode(a)},getElementsByTagNameNS:function(a,b,c){var d=[];if(a.getElementsByTagNameNS)d=a.getElementsByTagNameNS(b,c);else for(var a=a.getElementsByTagName("*"),e,f,g=0,h=a.length;g0?(d=a.substring(0,e),a=a.substring(e+1)):d=c?this.namespaceAlias[c.namespaceURI]:this.defaultPrefix;b=this.writers[d][a].apply(this,[b]);c&&c.appendChild(b);return b},getChildEl:function(a,b, -c){return a&&this.getThisOrNextEl(a.firstChild,b,c)},getNextEl:function(a,b,c){return a&&this.getThisOrNextEl(a.nextSibling,b,c)},getThisOrNextEl:function(a,b,c){a:for(;a;a=a.nextSibling)switch(a.nodeType){case 1:if((!b||b===(a.localName||a.nodeName.split(":").pop()))&&(!c||c===a.namespaceURI))break a;a=null;break a;case 3:if(/^\s*$/.test(a.nodeValue))break;case 4:case 6:case 12:case 10:case 11:a=null;break a}return a||null},lookupNamespaceURI:function(a,b){var c=null;if(a)if(a.lookupNamespaceURI)c= -a.lookupNamespaceURI(b);else a:switch(a.nodeType){case 1:if(a.namespaceURI!==null&&a.prefix===b){c=a.namespaceURI;break a}if(c=a.attributes.length)for(var d,e=0;e0))d.prefix=e[0]},Attribution:function(a, -b){b.attribution={};this.readChildNodes(a,b.attribution)},LogoURL:function(a,b){b.logo={width:a.getAttribute("width"),height:a.getAttribute("height")};this.readChildNodes(a,b.logo)},Style:function(a,b){var c={};b.styles.push(c);this.readChildNodes(a,c)},LegendURL:function(a,b){var c={width:a.getAttribute("width"),height:a.getAttribute("height")};b.legend=c;this.readChildNodes(a,c)},MetadataURL:function(a,b){var c={type:a.getAttribute("type")};b.metadataURLs.push(c);this.readChildNodes(a,c)},DataURL:function(a, -b){b.dataURL={};this.readChildNodes(a,b.dataURL)},FeatureListURL:function(a,b){b.featureListURL={};this.readChildNodes(a,b.featureListURL)},AuthorityURL:function(a,b){var c=a.getAttribute("name"),d={};this.readChildNodes(a,d);b.authorityURLs[c]=d.href},Identifier:function(a,b){var c=a.getAttribute("authority");b.identifiers[c]=this.getChildValue(a)},KeywordList:function(a,b){this.readChildNodes(a,b)},SRS:function(a,b){b.srs[this.getChildValue(a)]=!0}}},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1"}); -OpenLayers.Format.WMSCapabilities.v1_1=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1,{readers:{wms:OpenLayers.Util.applyDefaults({WMT_MS_Capabilities:function(a,b){this.readChildNodes(a,b)},Keyword:function(a,b){b.keywords&&b.keywords.push(this.getChildValue(a))},DescribeLayer:function(a,b){b.describelayer={formats:[]};this.readChildNodes(a,b.describelayer)},GetLegendGraphic:function(a,b){b.getlegendgraphic={formats:[]};this.readChildNodes(a,b.getlegendgraphic)},GetStyles:function(a,b){b.getstyles= -{formats:[]};this.readChildNodes(a,b.getstyles)},PutStyles:function(a,b){b.putstyles={formats:[]};this.readChildNodes(a,b.putstyles)},UserDefinedSymbolization:function(a,b){var c={supportSLD:parseInt(a.getAttribute("SupportSLD"))==1,userLayer:parseInt(a.getAttribute("UserLayer"))==1,userStyle:parseInt(a.getAttribute("UserStyle"))==1,remoteWFS:parseInt(a.getAttribute("RemoteWFS"))==1};b.userSymbols=c},LatLonBoundingBox:function(a,b){b.llbbox=[parseFloat(a.getAttribute("minx")),parseFloat(a.getAttribute("miny")), -parseFloat(a.getAttribute("maxx")),parseFloat(a.getAttribute("maxy"))]},BoundingBox:function(a,b){var c=OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms.BoundingBox.apply(this,[a,b]);c.srs=a.getAttribute("SRS");b.bbox[c.srs]=c},ScaleHint:function(a,b){var c=a.getAttribute("min"),d=a.getAttribute("max"),e=Math.pow(2,0.5),f=OpenLayers.INCHES_PER_UNIT.m;b.maxScale=parseFloat((c/e*f*OpenLayers.DOTS_PER_INCH).toPrecision(13));b.minScale=parseFloat((d/e*f*OpenLayers.DOTS_PER_INCH).toPrecision(13))}, -Dimension:function(a,b){var c={name:a.getAttribute("name").toLowerCase(),units:a.getAttribute("units"),unitsymbol:a.getAttribute("unitSymbol")};b.dimensions[c.name]=c},Extent:function(a,b){var c=a.getAttribute("name").toLowerCase();if(c in b.dimensions){c=b.dimensions[c];c.nearestVal=a.getAttribute("nearestValue")==="1";c.multipleVal=a.getAttribute("multipleValues")==="1";c.current=a.getAttribute("current")==="1";c["default"]=a.getAttribute("default")||"";var d=this.getChildValue(a);c.values=d.split(",")}}}, -OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms)},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_1"});OpenLayers.Format.WMSCapabilities.v1_1_1=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1,{version:"1.1.1",initialize:function(a){OpenLayers.Format.WMSCapabilities.v1_1.prototype.initialize.apply(this,[a])},readers:{wms:OpenLayers.Util.applyDefaults({SRS:function(a,b){b.srs[this.getChildValue(a)]=!0}},OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers.wms)},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_1_1"}); -OpenLayers.Handler.Drag=OpenLayers.Class(OpenLayers.Handler,{started:!1,stopDown:!0,dragging:!1,touch:!1,last:null,start:null,lastMoveEvt:null,oldOnselectstart:null,interval:0,timeoutId:null,documentDrag:!1,documentEvents:null,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(this.documentDrag===!0){var d=this;this._docMove=function(a){d.mousemove({xy:{x:a.clientX,y:a.clientY},element:document})};this._docUp=function(a){d.mouseup({xy:{x:a.clientX,y:a.clientY}})}}}, -dragstart:function(a){var b=!0;this.dragging=!1;if(this.checkModifiers(a)&&(OpenLayers.Event.isLeftClick(a)||OpenLayers.Event.isSingleTouch(a))){this.started=!0;this.last=this.start=a.xy;OpenLayers.Element.addClass(this.map.viewPortDiv,"olDragDown");this.down(a);this.callback("down",[a.xy]);OpenLayers.Event.stop(a);if(!this.oldOnselectstart)this.oldOnselectstart=document.onselectstart?document.onselectstart:OpenLayers.Function.True;document.onselectstart=OpenLayers.Function.False;b=!this.stopDown}else this.started= -!1,this.last=this.start=null;return b},dragmove:function(a){this.lastMoveEvt=a;if(this.started&&!this.timeoutId&&(a.xy.x!=this.last.x||a.xy.y!=this.last.y)){this.documentDrag===!0&&this.documentEvents&&(a.element===document?(this.adjustXY(a),this.setEvent(a)):this.removeDocumentEvents());if(this.interval>0)this.timeoutId=setTimeout(OpenLayers.Function.bind(this.removeTimeout,this),this.interval);this.dragging=!0;this.move(a);this.callback("move",[a.xy]);if(!this.oldOnselectstart)this.oldOnselectstart= -document.onselectstart,document.onselectstart=OpenLayers.Function.False;this.last=a.xy}return!0},dragend:function(a){if(this.started){this.documentDrag===!0&&this.documentEvents&&(this.adjustXY(a),this.removeDocumentEvents());var b=this.start!=this.last;this.dragging=this.started=!1;OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDragDown");this.up(a);this.callback("up",[a.xy]);b&&this.callback("done",[a.xy]);document.onselectstart=this.oldOnselectstart}return!0},down:function(){},move:function(){}, -up:function(){},out:function(){},mousedown:function(a){return this.dragstart(a)},touchstart:function(a){if(!this.touch)this.touch=!0,this.map.events.un({mousedown:this.mousedown,mouseup:this.mouseup,mousemove:this.mousemove,click:this.click,scope:this});return this.dragstart(a)},mousemove:function(a){return this.dragmove(a)},touchmove:function(a){return this.dragmove(a)},removeTimeout:function(){this.timeoutId=null;this.dragging&&this.mousemove(this.lastMoveEvt)},mouseup:function(a){return this.dragend(a)}, -touchend:function(a){a.xy=this.last;return this.dragend(a)},mouseout:function(a){if(this.started&&OpenLayers.Util.mouseLeft(a,this.map.eventsDiv))if(this.documentDrag===!0)this.addDocumentEvents();else{var b=this.start!=this.last;this.dragging=this.started=!1;OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDragDown");this.out(a);this.callback("out",[]);b&&this.callback("done",[a.xy]);if(document.onselectstart)document.onselectstart=this.oldOnselectstart}return!0},click:function(){return this.start== -this.last},activate:function(){var a=!1;if(OpenLayers.Handler.prototype.activate.apply(this,arguments))this.dragging=!1,a=!0;return a},deactivate:function(){var a=!1;if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments))this.dragging=this.started=this.touch=!1,this.last=this.start=null,a=!0,OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDragDown");return a},adjustXY:function(a){var b=OpenLayers.Util.pagePosition(this.map.viewPortDiv);a.xy.x-=b[0];a.xy.y-=b[1]},addDocumentEvents:function(){OpenLayers.Element.addClass(document.body, -"olDragDown");this.documentEvents=!0;OpenLayers.Event.observe(document,"mousemove",this._docMove);OpenLayers.Event.observe(document,"mouseup",this._docUp)},removeDocumentEvents:function(){OpenLayers.Element.removeClass(document.body,"olDragDown");this.documentEvents=!1;OpenLayers.Event.stopObserving(document,"mousemove",this._docMove);OpenLayers.Event.stopObserving(document,"mouseup",this._docUp)},CLASS_NAME:"OpenLayers.Handler.Drag"}); -OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,boxDivClassName:"olHandlerBoxZoomBox",boxOffsets:null,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.dragHandler=new OpenLayers.Handler.Drag(this,{down:this.startBox,move:this.moveBox,out:this.removeBox,up:this.endBox},{keyMask:this.keyMask})},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);if(this.dragHandler)this.dragHandler.destroy(),this.dragHandler= -null},setMap:function(a){OpenLayers.Handler.prototype.setMap.apply(this,arguments);this.dragHandler&&this.dragHandler.setMap(a)},startBox:function(){this.callback("start",[]);this.zoomBox=OpenLayers.Util.createDiv("zoomBox",new OpenLayers.Pixel(-9999,-9999));this.zoomBox.className=this.boxDivClassName;this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE.Popup-1;this.map.eventsDiv.appendChild(this.zoomBox);OpenLayers.Element.addClass(this.map.eventsDiv,"olDrawBox")},moveBox:function(a){var b=this.dragHandler.start.x, -c=this.dragHandler.start.y,d=Math.abs(b-a.x),e=Math.abs(c-a.y),f=this.getBoxOffsets();this.zoomBox.style.width=d+f.width+1+"px";this.zoomBox.style.height=e+f.height+1+"px";this.zoomBox.style.left=(a.x5||Math.abs(this.dragHandler.start.y-a.y)>5){var c=this.dragHandler.start;b=Math.min(c.y,a.y);var d=Math.max(c.y,a.y),e=Math.min(c.x,a.x),a=Math.max(c.x, -a.x);b=new OpenLayers.Bounds(e,d,a,b)}else b=this.dragHandler.start.clone();this.removeBox();this.callback("done",[b])},removeBox:function(){this.map.eventsDiv.removeChild(this.zoomBox);this.boxOffsets=this.zoomBox=null;OpenLayers.Element.removeClass(this.map.eventsDiv,"olDrawBox")},activate:function(){return OpenLayers.Handler.prototype.activate.apply(this,arguments)?(this.dragHandler.activate(),!0):!1},deactivate:function(){return OpenLayers.Handler.prototype.deactivate.apply(this,arguments)?(this.dragHandler.deactivate()&& -this.zoomBox&&this.removeBox(),!0):!1},getBoxOffsets:function(){if(!this.boxOffsets){var a=document.createElement("div");a.style.position="absolute";a.style.border="1px solid black";a.style.width="3px";document.body.appendChild(a);var b=a.clientWidth==3;document.body.removeChild(a);var a=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-left-width")),c=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-right-width")),d=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-top-width")), -e=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-bottom-width"));this.boxOffsets={left:a,right:c,top:d,bottom:e,width:b===!1?a+c:0,height:b===!1?d+e:0}}return this.boxOffsets},CLASS_NAME:"OpenLayers.Handler.Box"}); -OpenLayers.Control.ZoomBox=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,out:!1,alwaysZoom:!1,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask})},zoomBox:function(a){if(a instanceof OpenLayers.Bounds){var b;if(this.out){b=Math.abs(a.right-a.left);var c=Math.abs(a.top-a.bottom);b=Math.min(this.map.size.h/c,this.map.size.w/b);var c=this.map.getExtent(),d=this.map.getLonLatFromPixel(a.getCenterPixel()),a=d.lon-c.getWidth()/ -2*b,e=d.lon+c.getWidth()/2*b,f=d.lat-c.getHeight()/2*b;b=d.lat+c.getHeight()/2*b;b=new OpenLayers.Bounds(a,f,e,b)}else b=this.map.getLonLatFromPixel(new OpenLayers.Pixel(a.left,a.bottom)),c=this.map.getLonLatFromPixel(new OpenLayers.Pixel(a.right,a.top)),b=new OpenLayers.Bounds(b.lon,b.lat,c.lon,c.lat);c=this.map.getZoom();this.map.zoomToExtent(b);c==this.map.getZoom()&&this.alwaysZoom==!0&&this.map.zoomTo(c+(this.out?-1:1))}else this.out?this.map.setCenter(this.map.getLonLatFromPixel(a),this.map.getZoom()- -1):this.map.setCenter(this.map.getLonLatFromPixel(a),this.map.getZoom()+1)},CLASS_NAME:"OpenLayers.Control.ZoomBox"}); -OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:!1,interval:1,documentDrag:!1,kinetic:null,enableKinetic:!1,kineticInterval:10,draw:function(){if(this.enableKinetic){var a={interval:this.kineticInterval};typeof this.enableKinetic==="object"&&(a=OpenLayers.Util.extend(a,this.enableKinetic));this.kinetic=new OpenLayers.Kinetic(a)}this.handler=new OpenLayers.Handler.Drag(this,{move:this.panMap,done:this.panMapDone,down:this.panMapStart},{interval:this.interval, -documentDrag:this.documentDrag})},panMapStart:function(){this.kinetic&&this.kinetic.begin()},panMap:function(a){this.kinetic&&this.kinetic.update(a);this.panned=!0;this.map.pan(this.handler.last.x-a.x,this.handler.last.y-a.y,{dragging:!0,animate:!1})},panMapDone:function(a){if(this.panned){var b=null;this.kinetic&&(b=this.kinetic.end(a));this.map.pan(this.handler.last.x-a.x,this.handler.last.y-a.y,{dragging:!!b,animate:!1});if(b){var c=this;this.kinetic.move(b,function(a,b,f){c.map.pan(a,b,{dragging:!f, -animate:!1})})}this.panned=!1}},CLASS_NAME:"OpenLayers.Control.DragPan"}); -OpenLayers.Handler.MouseWheel=OpenLayers.Class(OpenLayers.Handler,{wheelListener:null,mousePosition:null,interval:0,delta:0,cumulative:!0,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.wheelListener=OpenLayers.Function.bindAsEventListener(this.onWheelEvent,this)},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);this.wheelListener=null},onWheelEvent:function(a){if(this.map&&this.checkModifiers(a)){for(var b=!1,c=!1,d=!1,e= -OpenLayers.Event.element(a);e!=null&&!d&&!b;){if(!b)try{var f=e.currentStyle?e.currentStyle.overflow:document.defaultView.getComputedStyle(e,null).getPropertyValue("overflow"),b=f&&f=="auto"||f=="scroll"}catch(g){}if(!c)for(var d=0,h=this.map.layers.length;da.lon)b.lon<0?b.lon=-180-(b.lon+180):a.lon=180+a.lon+180;return new OpenLayers.Bounds(b.lon,a.lat,a.lon,b.lat)},showTile:function(){this.shouldDraw&& -this.show()},show:function(){},hide:function(){},CLASS_NAME:"OpenLayers.Tile"}); -OpenLayers.Tile.Image=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,layerAlphaHack:null,isBackBuffer:!1,isFirstDraw:!0,backBufferTile:null,maxGetUrlLength:null,initialize:function(a,b,c,d,e,f){OpenLayers.Tile.prototype.initialize.apply(this,arguments);this.maxGetUrlLength!=null&&OpenLayers.Util.extend(this,OpenLayers.Tile.Image.IFrame);this.url=d;this.frame=document.createElement("div");this.frame.style.overflow="hidden";this.frame.style.position="absolute";this.layerAlphaHack= -this.layer.alpha&&OpenLayers.Util.alphaHack()},destroy:function(){this.imgDiv!=null&&this.removeImgDiv();this.imgDiv=null;this.frame!=null&&this.frame.parentNode==this.layer.div&&this.layer.div.removeChild(this.frame);this.frame=null;if(this.backBufferTile)this.backBufferTile.destroy(),this.backBufferTile=null;this.layer.events.unregister("loadend",this,this.resetBackBuffer);OpenLayers.Tile.prototype.destroy.apply(this,arguments)},clone:function(a){a==null&&(a=new OpenLayers.Tile.Image(this.layer, -this.position,this.bounds,this.url,this.size));a=OpenLayers.Tile.prototype.clone.apply(this,[a]);a.imgDiv=null;return a},draw:function(){if(this.layer!=this.layer.map.baseLayer&&this.layer.reproject)this.bounds=this.getBoundsFromBaseLayer(this.position);var a=OpenLayers.Tile.prototype.draw.apply(this,arguments);if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1||this.layer.singleTile)if(a){if(!this.backBufferTile)this.backBufferTile=this.clone(),this.backBufferTile.hide(), -this.backBufferTile.isBackBuffer=!0,this.events.register("loadend",this,this.resetBackBuffer),this.layer.events.register("loadend",this,this.resetBackBuffer);this.startTransition()}else this.backBufferTile&&this.backBufferTile.clear();else if(a&&this.isFirstDraw)this.events.register("loadend",this,this.showTile),this.isFirstDraw=!1;if(!a)return!1;this.isLoading?this.events.triggerEvent("reload"):(this.isLoading=!0,this.events.triggerEvent("loadstart"));return this.renderTile()},resetBackBuffer:function(){this.showTile(); -if(this.backBufferTile&&(this.isFirstDraw||!this.layer.numLoadingTiles)){this.isFirstDraw=!1;var a=this.layer.maxExtent;if(a&&this.bounds.intersectsBounds(a,!1))this.backBufferTile.position=this.position,this.backBufferTile.bounds=this.bounds,this.backBufferTile.size=this.size,this.backBufferTile.imageSize=this.layer.getImageSize(this.bounds)||this.size,this.backBufferTile.imageOffset=this.layer.imageOffset,this.backBufferTile.resolution=this.layer.getResolution(),this.backBufferTile.renderTile(); -this.backBufferTile.hide()}},renderTile:function(){this.layer.async?(this.initImgDiv(),this.layer.getURLasync(this.bounds,this,"url",this.positionImage)):(this.url=this.layer.getURL(this.bounds),this.initImgDiv(),this.positionImage());return!0},positionImage:function(){if(this.layer!==null){OpenLayers.Util.modifyDOMElement(this.frame,null,this.position,this.size);var a=this.layer.getImageSize(this.bounds);this.layerAlphaHack?OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,null,null,a,this.url):(OpenLayers.Util.modifyDOMElement(this.imgDiv, -null,null,a),this.imgDiv.src=this.url)}},clear:function(){if(this.imgDiv&&(this.hide(),OpenLayers.Tile.Image.useBlankTile))this.imgDiv.src=OpenLayers.Util.getImagesLocation()+"blank.gif"},initImgDiv:function(){if(this.imgDiv==null){var a=this.layer.imageOffset,b=this.layer.getImageSize(this.bounds);this.imgDiv=this.layerAlphaHack?OpenLayers.Util.createAlphaImageDiv(null,a,b,null,"relative",null,null,null,!0):OpenLayers.Util.createImage(null,a,b,null,"relative",null,null,!0);if(OpenLayers.Util.isArray(this.layer.url))this.imgDiv.urls= -this.layer.url.slice();this.imgDiv.className="olTileImage";this.frame.style.zIndex=this.isBackBuffer?0:1;this.frame.appendChild(this.imgDiv);this.layer.div.appendChild(this.frame);this.layer.opacity!=null&&OpenLayers.Util.modifyDOMElement(this.imgDiv,null,null,null,null,null,null,this.layer.opacity);this.imgDiv.map=this.layer.map;var c=function(){if(this.isLoading)this.isLoading=!1,this.events.triggerEvent("loadend")};this.layerAlphaHack?OpenLayers.Event.observe(this.imgDiv.childNodes[0],"load",OpenLayers.Function.bind(c, -this)):OpenLayers.Event.observe(this.imgDiv,"load",OpenLayers.Function.bind(c,this));OpenLayers.Event.observe(this.imgDiv,"error",OpenLayers.Function.bind(function(){this.imgDiv._attempts>OpenLayers.IMAGE_RELOAD_ATTEMPTS&&c.call(this)},this))}this.imgDiv.viewRequestID=this.layer.map.viewRequestID},removeImgDiv:function(){OpenLayers.Event.stopObservingElement(this.imgDiv);if(this.imgDiv.parentNode==this.frame)this.frame.removeChild(this.imgDiv),this.imgDiv.map=null;this.imgDiv.urls=null;var a=this.imgDiv.firstChild; -a?(OpenLayers.Event.stopObservingElement(a),this.imgDiv.removeChild(a),delete a):this.imgDiv.src=OpenLayers.Util.getImagesLocation()+"blank.gif"},checkImgURL:function(){this.layer&&(OpenLayers.Util.isEquivalentUrl(this.layerAlphaHack?this.imgDiv.firstChild.src:this.imgDiv.src,this.url)||this.hide())},startTransition:function(){if(this.backBufferTile&&this.backBufferTile.imgDiv){var a=1;this.backBufferTile.resolution&&(a=this.backBufferTile.resolution/this.layer.getResolution());if(a!=1){if(this.layer.transitionEffect== -"resize"){var b=new OpenLayers.LonLat(this.backBufferTile.bounds.left,this.backBufferTile.bounds.top),c=new OpenLayers.Size(this.backBufferTile.size.w*a,this.backBufferTile.size.h*a),b=this.layer.map.getLayerPxFromLonLat(b);OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,null,b,c);c=this.backBufferTile.imageSize;c=new OpenLayers.Size(c.w*a,c.h*a);(b=this.backBufferTile.imageOffset)&&(b=new OpenLayers.Pixel(b.x*a,b.y*a));OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv,null,b, -c);this.backBufferTile.show()}}else this.layer.singleTile?this.backBufferTile.show():this.backBufferTile.hide()}},show:function(){this.frame.style.display="";if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1&&OpenLayers.IS_GECKO===!0)this.frame.scrollLeft=this.frame.scrollLeft},hide:function(){this.frame.style.display="none"},CLASS_NAME:"OpenLayers.Tile.Image"}); -OpenLayers.Tile.Image.useBlankTile=OpenLayers.BROWSER_NAME=="safari"||OpenLayers.BROWSER_NAME=="opera"; -OpenLayers.Layer.WMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",format:"image/jpeg"},reproject:!1,isBaseLayer:!0,encodeBBOX:!1,noMagic:!1,yx:{"EPSG:4326":!0},initialize:function(a,b,c,d){var e=[],c=OpenLayers.Util.upperCaseObject(c);if(parseFloat(c.VERSION)>=1.3&&!c.EXCEPTIONS)c.EXCEPTIONS="INIMAGE";e.push(a,b,c,d);OpenLayers.Layer.Grid.prototype.initialize.apply(this,e);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)); -if(!this.noMagic&&this.params.TRANSPARENT&&this.params.TRANSPARENT.toString().toLowerCase()=="true"){if(d==null||!d.isBaseLayer)this.isBaseLayer=!1;if(this.params.FORMAT=="image/jpeg")this.params.FORMAT=OpenLayers.Util.alphaHack()?"image/gif":"image/png"}},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments)},clone:function(a){a==null&&(a=new OpenLayers.Layer.WMS(this.name,this.url,this.params,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this, -[a])},reverseAxisOrder:function(){return parseFloat(this.params.VERSION)>=1.3&&!!this.yx[this.map.getProjectionObject().getCode()]},getURL:function(a){var a=this.adjustBounds(a),b=this.getImageSize(),c={},d=this.reverseAxisOrder();c.BBOX=this.encodeBBOX?a.toBBOX(null,d):a.toArray(d);c.WIDTH=b.w;c.HEIGHT=b.h;return this.getFullRequestString(c)},mergeNewParams:function(a){a=[OpenLayers.Util.upperCaseObject(a)];return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,a)},getFullRequestString:function(a, -b){var c=this.map.getProjectionObject(),c=this.projection&&this.projection.equals(c)?this.projection.getCode():c.getCode(),c=c=="none"?null:c;parseFloat(this.params.VERSION)>=1.3?this.params.CRS=c:this.params.SRS=c;if(typeof this.params.TRANSPARENT=="boolean")a.TRANSPARENT=this.params.TRANSPARENT?"TRUE":"FALSE";return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments)},CLASS_NAME:"OpenLayers.Layer.WMS"}); -OpenLayers.Control.MousePosition=OpenLayers.Class(OpenLayers.Control,{autoActivate:!0,element:null,prefix:"",separator:", ",suffix:"",numDigits:5,granularity:10,emptyString:null,lastXy:null,displayProjection:null,destroy:function(){this.deactivate();OpenLayers.Control.prototype.destroy.apply(this,arguments)},activate:function(){return OpenLayers.Control.prototype.activate.apply(this,arguments)?(this.map.events.register("mousemove",this,this.redraw),this.map.events.register("mouseout",this,this.reset), -this.redraw(),!0):!1},deactivate:function(){return OpenLayers.Control.prototype.deactivate.apply(this,arguments)?(this.map.events.unregister("mousemove",this,this.redraw),this.map.events.unregister("mouseout",this,this.reset),this.element.innerHTML="",!0):!1},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.element)this.div.left="",this.div.top="",this.element=this.div;return this.div},redraw:function(a){var b;if(a==null)this.reset();else if(this.lastXy==null||Math.abs(a.xy.x- -this.lastXy.x)>this.granularity||Math.abs(a.xy.y-this.lastXy.y)>this.granularity)this.lastXy=a.xy;else if(b=this.map.getLonLatFromPixel(a.xy))if(this.displayProjection&&b.transform(this.map.getProjectionObject(),this.displayProjection),this.lastXy=a.xy,a=this.formatOutput(b),a!=this.element.innerHTML)this.element.innerHTML=a},reset:function(){if(this.emptyString!=null)this.element.innerHTML=this.emptyString},formatOutput:function(a){var b=parseInt(this.numDigits);return this.prefix+a.lon.toFixed(b)+ -this.separator+a.lat.toFixed(b)+this.suffix},CLASS_NAME:"OpenLayers.Control.MousePosition"}); -OpenLayers.Format.WMSCapabilities.v1_3=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1,{readers:{wms:OpenLayers.Util.applyDefaults({WMS_Capabilities:function(a,b){this.readChildNodes(a,b)},LayerLimit:function(a,b){b.layerLimit=parseInt(this.getChildValue(a))},MaxWidth:function(a,b){b.maxWidth=parseInt(this.getChildValue(a))},MaxHeight:function(a,b){b.maxHeight=parseInt(this.getChildValue(a))},BoundingBox:function(a,b){var c=OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms.BoundingBox.apply(this, -[a,b]);c.srs=a.getAttribute("CRS");b.bbox[c.srs]=c},CRS:function(a,b){this.readers.wms.SRS.apply(this,[a,b])},EX_GeographicBoundingBox:function(a,b){b.llbbox=[];this.readChildNodes(a,b.llbbox)},westBoundLongitude:function(a,b){b[0]=this.getChildValue(a)},eastBoundLongitude:function(a,b){b[2]=this.getChildValue(a)},southBoundLatitude:function(a,b){b[1]=this.getChildValue(a)},northBoundLatitude:function(a,b){b[3]=this.getChildValue(a)},MinScaleDenominator:function(a,b){b.maxScale=parseFloat(this.getChildValue(a)).toPrecision(16)}, -MaxScaleDenominator:function(a,b){b.minScale=parseFloat(this.getChildValue(a)).toPrecision(16)},Dimension:function(a,b){var c={name:a.getAttribute("name").toLowerCase(),units:a.getAttribute("units"),unitsymbol:a.getAttribute("unitSymbol"),nearestVal:a.getAttribute("nearestValue")==="1",multipleVal:a.getAttribute("multipleValues")==="1","default":a.getAttribute("default")||"",current:a.getAttribute("current")==="1",values:this.getChildValue(a).split(",")};b.dimensions[c.name]=c},Keyword:function(a, -b){var c={value:this.getChildValue(a),vocabulary:a.getAttribute("vocabulary")};b.keywords&&b.keywords.push(c)}},OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms),sld:{UserDefinedSymbolization:function(a,b){this.readers.wms.UserDefinedSymbolization.apply(this,[a,b]);b.userSymbols.inlineFeature=parseInt(a.getAttribute("InlineFeature"))==1;b.userSymbols.remoteWCS=parseInt(a.getAttribute("RemoteWCS"))==1},DescribeLayer:function(a,b){this.readers.wms.DescribeLayer.apply(this,[a,b])},GetLegendGraphic:function(a, -b){this.readers.wms.GetLegendGraphic.apply(this,[a,b])}}},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_3"});OpenLayers.Format.WMSCapabilities.v1_3_0=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_3,{version:"1.3.0",CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_3_0"}); -OpenLayers.Lang.en={unhandledRequest:"Unhandled request return ${statusText}",Permalink:"Permalink",Overlays:"Overlays","Base Layer":"Base Layer",readNotImplemented:"Read not implemented.",writeNotImplemented:"Write not implemented.",noFID:"Can't update a feature for which there is no FID.",errorLoadingGML:"Error in loading GML file ${url}",browserNotSupported:"Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",componentShouldBe:"addFeatures : component should be an ${geomType}", -getFeatureError:"getFeatureFromEvent called on layer with no renderer. This usually means you destroyed a layer, but not some handler which is associated with it.",minZoomLevelError:"The minZoomLevel property is only intended for use with the FixedZoomLevels-descendent layers. That this wfs layer checks for minZoomLevel is a relic of thepast. We cannot, however, remove it without possibly breaking OL based applications that may depend on it. Therefore we are deprecating it -- the minZoomLevel check below will be removed at 3.0. Please instead use min/max resolution setting as described here: http://trac.openlayers.org/wiki/SettingZoomLevels", -commitSuccess:"WFS Transaction: SUCCESS ${response}",commitFailed:"WFS Transaction: FAILED ${response}",googleWarning:"The Google Layer was unable to load correctly.

To get rid of this message, select a new BaseLayer in the layer switcher in the upper-right corner.

Most likely, this is because the Google Maps library script was either not included, or does not contain the correct API key for your site.

Developers: For help getting this working correctly, click here", -getLayerWarning:"The ${layerType} Layer was unable to load correctly.

To get rid of this message, select a new BaseLayer in the layer switcher in the upper-right corner.

Most likely, this is because the ${layerLib} library script was not correctly included.

Developers: For help getting this working correctly, click here","Scale = 1 : ${scaleDenom}":"Scale = 1 : ${scaleDenom}",W:"W",E:"E",N:"N",S:"S",Graticule:"Graticule", -layerAlreadyAdded:"You tried to add the layer: ${layerName} to the map, but it has already been added",reprojectDeprecated:"You are using the 'reproject' option on the ${layerName} layer. This option is deprecated: its use was designed to support displaying data over commercial basemaps, but that functionality should now be achieved by using Spherical Mercator support. More information is available from http://trac.openlayers.org/wiki/SphericalMercator.",methodDeprecated:"This method has been deprecated and will be removed in 3.0. Please use ${newMethod} instead.", -boundsAddError:"You must pass both x and y values to the add function.",lonlatAddError:"You must pass both lon and lat values to the add function.",pixelAddError:"You must pass both x and y values to the add function.",unsupportedGeometryType:"Unsupported geometry type: ${geomType}",filterEvaluateNotImplemented:"evaluate is not implemented for this filter type.",proxyNeeded:"You probably need to set OpenLayers.ProxyHost to access ${url}.See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost", -end:""}; -OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1_1,{version:"1.1.1",profile:"WMSC",initialize:function(a){OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.initialize.apply(this,[a])},readers:{wms:OpenLayers.Util.applyDefaults({VendorSpecificCapabilities:function(a,b){b.vendorSpecific={tileSets:[]};this.readChildNodes(a,b.vendorSpecific)},TileSet:function(a,b){var c={srs:{},bbox:{},resolutions:[]};this.readChildNodes(a,c);b.tileSets.push(c)},Resolutions:function(a, -b){for(var c=this.getChildValue(a).split(" "),d=0,e=c.length;d=9500&&a<=95E4?Math.round(a/1E3)+"K":a>=95E4?Math.round(a/1E6)+"M":Math.round(a),this.element.innerHTML=OpenLayers.i18n("Scale = 1 : ${scaleDenom}",{scaleDenom:a})},CLASS_NAME:"OpenLayers.Control.Scale"}); diff --git a/ckanext/spatial/public/js/vendor/openlayers/README.txt b/ckanext/spatial/public/js/vendor/openlayers/README.txt deleted file mode 100644 index 72e95191..00000000 --- a/ckanext/spatial/public/js/vendor/openlayers/README.txt +++ /dev/null @@ -1,30 +0,0 @@ -These are custom builds of the OpenLayers Javascript mapping library, -slimmed down to only contain the features we need. - -The files *.cfg contain the build profile used to build OpenLayers. -In order to add more functionality, new classes must be added in the -build profile, and then run the build command from the OpenLayers -distribution: - -1. Download OpenLayers source code from http://openlayers.org - -2. Modify the cfg file - -3. Go to build/ and execute:: - - python build.py {path-to-ckan.cfg} {output-file} - -These builds have been obtained using the Closure Compiler. Please refer -to the build/README.txt on the OpenLayers Source Code for more details. - - python build.py -c closure {path-to-ckan.cfg} {output-file} - - -The theme used for the OpenLayers controls is the "dark" theme made available -by Development Seed under the BSD License: - -https://github.com/developmentseed/openlayers_themes/blob/master/LICENSE.txt - -The default map marker is derived from an icon available at The Noun Project: - -http://thenounproject.com/ diff --git a/ckanext/spatial/public/js/vendor/openlayers/ckan.cfg b/ckanext/spatial/public/js/vendor/openlayers/ckan.cfg deleted file mode 100644 index 66bc79bc..00000000 --- a/ckanext/spatial/public/js/vendor/openlayers/ckan.cfg +++ /dev/null @@ -1,40 +0,0 @@ -[first] -OpenLayers/SingleFile.js -OpenLayers.js -OpenLayers/BaseTypes.js -OpenLayers/BaseTypes/Class.js -OpenLayers/Util.js -Rico/Corner.js - -[last] - -[include] -OpenLayers/Console.js -OpenLayers/Ajax.js -OpenLayers/Events.js -OpenLayers/Map.js -OpenLayers/Layer.js -OpenLayers/Layer/Grid.js -OpenLayers/Layer/HTTPRequest.js -OpenLayers/Layer/WMS.js -OpenLayers/Layer/WMS/Untiled.js -OpenLayers/Tile.js -OpenLayers/Tile/Image.js -OpenLayers/Control/Navigation.js -OpenLayers/Control/PanZoom.js -OpenLayers/Control/PanZoomBar.js -OpenLayers/Control/Scale.js -OpenLayers/Control/MousePosition.js -OpenLayers/Control/LayerSwitcher.js -OpenLayers/Format/XML.js -OpenLayers/Format/WMSCapabilities/v1_1_1.js -OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js -OpenLayers/Format/WMSCapabilities/v1_3_0.js - -[exclude] -Firebug/firebug.js -Firebug/firebugx.js -OpenLayers/Lang/de.js -OpenLayers/Lang/en-CA.js -OpenLayers/Lang/fr.js -OpenLayers/Lang/cs-CZ.js diff --git a/ckanext/spatial/public/js/vendor/openlayers/ckan_dataset_map.cfg b/ckanext/spatial/public/js/vendor/openlayers/ckan_dataset_map.cfg deleted file mode 100644 index f37b786c..00000000 --- a/ckanext/spatial/public/js/vendor/openlayers/ckan_dataset_map.cfg +++ /dev/null @@ -1,39 +0,0 @@ -[first] -OpenLayers/SingleFile.js -OpenLayers.js -OpenLayers/BaseTypes.js -OpenLayers/BaseTypes/Class.js -OpenLayers/Util.js - -[last] - -[include] -OpenLayers/Console.js -OpenLayers/Ajax.js -OpenLayers/Events.js -OpenLayers/Map.js -OpenLayers/Renderer/SVG.js -OpenLayers/Renderer/VML.js -OpenLayers/Layer.js -OpenLayers/Layer/XYZ.js -OpenLayers/Layer/SphericalMercator.js -OpenLayers/Layer/Vector.js -OpenLayers/Control/Navigation.js -OpenLayers/Control/PanZoom.js -OpenLayers/Control/PanZoomBar.js -OpenLayers/Control/ZoomPanel.js -OpenLayers/Control/Scale.js -OpenLayers/Control/MousePosition.js -OpenLayers/Control/LayerSwitcher.js -OpenLayers/Control/ArgParser.js -OpenLayers/Control/Attribution.js -OpenLayers/Format/GeoJSON.js - -[exclude] -Rico/Corner.js -Firebug/firebug.js -Firebug/firebugx.js -OpenLayers/Lang/de.js -OpenLayers/Lang/en-CA.js -OpenLayers/Lang/fr.js -OpenLayers/Lang/cs-CZ.js diff --git a/ckanext/spatial/public/js/vendor/openlayers/ckan_wms_preview.cfg b/ckanext/spatial/public/js/vendor/openlayers/ckan_wms_preview.cfg deleted file mode 100644 index 66bc79bc..00000000 --- a/ckanext/spatial/public/js/vendor/openlayers/ckan_wms_preview.cfg +++ /dev/null @@ -1,40 +0,0 @@ -[first] -OpenLayers/SingleFile.js -OpenLayers.js -OpenLayers/BaseTypes.js -OpenLayers/BaseTypes/Class.js -OpenLayers/Util.js -Rico/Corner.js - -[last] - -[include] -OpenLayers/Console.js -OpenLayers/Ajax.js -OpenLayers/Events.js -OpenLayers/Map.js -OpenLayers/Layer.js -OpenLayers/Layer/Grid.js -OpenLayers/Layer/HTTPRequest.js -OpenLayers/Layer/WMS.js -OpenLayers/Layer/WMS/Untiled.js -OpenLayers/Tile.js -OpenLayers/Tile/Image.js -OpenLayers/Control/Navigation.js -OpenLayers/Control/PanZoom.js -OpenLayers/Control/PanZoomBar.js -OpenLayers/Control/Scale.js -OpenLayers/Control/MousePosition.js -OpenLayers/Control/LayerSwitcher.js -OpenLayers/Format/XML.js -OpenLayers/Format/WMSCapabilities/v1_1_1.js -OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js -OpenLayers/Format/WMSCapabilities/v1_3_0.js - -[exclude] -Firebug/firebug.js -Firebug/firebugx.js -OpenLayers/Lang/de.js -OpenLayers/Lang/en-CA.js -OpenLayers/Lang/fr.js -OpenLayers/Lang/cs-CZ.js diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/blank.gif b/ckanext/spatial/public/js/vendor/openlayers/img/blank.gif deleted file mode 100644 index 2799b45c6591f1db05c8c00bd1fd0c5c01f57614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE79h#MpaUX6G7L;iE{qJ;0LYaF_y7O^ diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/east-mini.png b/ckanext/spatial/public/js/vendor/openlayers/img/east-mini.png deleted file mode 100644 index b5be557984ee5efed19b8a8327168ae21c871d1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt;>T+pp z;I!0LO$?iKyq!5(i}O}D-An!E&~@U1$=R*Bw;z72<&@dyx@PO^Rjc%rjO?xpmFzZq odY=80^RI{r*Y7QUx#2E@Q;^anjXXIuppzLqUHx3vIVCg!0AA*EU;qFB diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/layer-switcher-maximize.png b/ckanext/spatial/public/js/vendor/openlayers/img/layer-switcher-maximize.png deleted file mode 100644 index bb812f293971fa9768a6c56cea457f2f6323dff0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 303 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt*djT^vI! zdf)aN@--QVILMzkz-W?KVq9O8Vpv%8U1iNZW;VWqvQq0Gwq2g?JY!df^IDD8i_=S9 zy?33?2`RS( s3=3vDZ((?MAdfGQTVn&$w1tbzopr0HdaIPyhe` diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/layer-switcher-minimize.png b/ckanext/spatial/public/js/vendor/openlayers/img/layer-switcher-minimize.png deleted file mode 100644 index 3ff2b4ad5de1104ae062e6c562536c871d0deb93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt*pnT^vI! zdbdtA%sOHq;Cerg=^LX_Liugh(AnpF4=8xdzx_mRSKB*JjtvO*??x9(FY{ssX zV>7IlUa@@6S8Qzk;IEaBW~uLGAopNYH5-SbjQ6!|Z;vZYG+d_FyvmBH!z1*F;jzSt zD^B<=zdX4kgkhD{x7z=f>W%;PcF$2$n!2iQl~tFAsFQ`Cyg};bP3h*J?!_-VdQ5^n z`>o}3n~L}P-R4n;m!;O{D(+&gYk02fa^Hc8r6NN67wg9trPEF*f2{)go59o7&t;uc GLK6VwI+B3^ diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/north-mini.png b/ckanext/spatial/public/js/vendor/openlayers/img/north-mini.png deleted file mode 100644 index 3d569c914a4ac1ef7ad8c7b3dee5830827b734b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt;2i|8pyra(l+e^(8&y*u6{1-oD!MNS%G}c0*}aI zpqdg8X1tdsUjY=9C~=J_3C>R|DNig)We7;j%q!9Ja}7}_GuAUO|9ZBu0I24ur;B5V zg@5v&&(H7w4_-dc_{lT9bc_0b_J{Y^^SChE?ycwH04N|HB+NpsRfL3fFRUj%1wVr7|NWPR1>?XR!f z|83r=xSaixchg+!_y5y>{Qdp@Kd*G&Uvq;ON;@qIOWypkPe0fCe}CQIpZpA)YI;2T TO_&z~J;dPY>gTe~DWM4fq{Drr diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/slider.png b/ckanext/spatial/public/js/vendor/openlayers/img/slider.png deleted file mode 100644 index a46f986ce9f9730a3b7e44fb344053eeb0a54982..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^B0$W^!3HFa4)S;aDVAa<&kznEsNqQI07({jL>2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4C3(EQLjJSE^j$*{wv-6m{`lfX&c#^vb+90 Z~}U&Kt-E9T^vI! zdf!eqT&D0#TNl6d^E+GUB~Ph+90r#7AWz3+Z_=vKxV%TAj57H_@Q z7TURH>p=M)ehR~}U&Kt(G&T^vI! zde5F$_(e|*Md20$E(>AV{qekwpEmRTGgneUJ`z4O${hYj3kCML{iP>6^Y zy2otu|GWDEnH$XP3C+pMMJ24q9z7R&pJa2dvc2g`w1)5H9jjKY6`%6FGHLDEu0;`+ z&Z&{>Uhg{BB5+dV$z!GKaSCg{hH75@R%>6OpEWCOLeREjGj5A$F!=?quiyGYe}Nv0 n3G=%HdMqVudIy9eb~y3xSf*&JR<~w7(9H~~}U&Kt)NOE{-7< zy>BmVm|z{uKbLh*2~7aV&rKNs diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/zoom-panel.png b/ckanext/spatial/public/js/vendor/openlayers/img/zoom-panel.png deleted file mode 100644 index 2c75f188dcca33aa09a365d9d0ca9314d8b65d83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 641 zcmV-{0)G98P)Px%J4r-AR7l6|m%mHnU>L_gi59Ze!il<+4uzr))yc66Ug5}c89J6`6Es7X#+h)*?--+)hVG+LE_CNW4RCe1YReSgpQ{k>Un%d-O3*VmPW zmNvE7zVGk7L=ptS)5gZeuf-CLYcv`a03=CjEu=>?M=c0~r&vmRe>404HIfR2!e}8x zilU$>%6#)^ZEcNmxy<(VHtBR4fPTNv-Q67*7Z*G{Jj^CiRh4?ZPPf~o)oLNjGO{d_ z$z)Jfm9w)mR8{>u(f`1nY(*_vM^1PR4O%Tq-h$-WRk&PFo9jERD|c-+gm)(L({ZLqphv2R}J&| z`FTV&o)Z~mvsnQ0`TUE?~}U&Kt&ThT^vI! zdaq8}$jf9X!1DY7GZ(|jbqzu?LV^=6@+@N8rKsLGL1;z7`j+!PT>=UF?>YVI3}1Zl zCR@n?u^Y_UZ66uV9XQ$`TOgxR@!W)+??BO#jMM|AOIR8rxjmFVdQ&MBb@008r3a{vGU diff --git a/ckanext/spatial/public/js/vendor/openlayers/img/zoom-world-mini.png b/ckanext/spatial/public/js/vendor/openlayers/img/zoom-world-mini.png deleted file mode 100644 index 4420155e84ce02d175e67ab124e496a3eb3761e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmV++0^a?JP)QEepAIBBebjc8wZbDje`CsC`_e)tcOz75E- zj4aDvLBHZYW-=K%oet%4nQS%-!1a3Na5%77EV$qA-;Fd)V>+F3KA%~y*GQ6tBuV6Q zIW$dUHk+Yo+GnGBy-vU1XEYkI-EIM}EDHeFbpfz#n`X1g;NQQN2J|uGZ+kn;YOn&*u9(Wb~`qk4YqBIMvmifxm-S4yeFMb<2X)qYe5h&9*+rv zK+un~iJKLUi0642hJo+ Date: Mon, 12 Dec 2022 13:13:05 +0100 Subject: [PATCH 53/66] Upgrade leaflet libraries Leaflet to 1.9.3 and Leaflet.draw to 0.4.14. Leaflet.draw is no longer maintainer so we'll need to change the implementation in the future --- ckanext/spatial/public/css/spatial_query.css | 2 +- ckanext/spatial/public/js/spatial_query.js | 1 + .../leaflet.draw/{ => 0.4.14}/MIT-LICENSE.md | 2 +- .../leaflet.draw/0.4.14/images/layers-2x.png | Bin 0 -> 1259 bytes .../leaflet.draw/0.4.14/images/layers.png | Bin 0 -> 696 bytes .../0.4.14/images/marker-icon-2x.png | Bin 0 -> 2586 bytes .../0.4.14/images/marker-icon.png | Bin 0 -> 1466 bytes .../0.4.14/images/marker-shadow.png | Bin 0 -> 618 bytes .../0.4.14/images/spritesheet-2x.png | Bin 0 -> 3581 bytes .../0.4.14/images/spritesheet.png | Bin 0 -> 1906 bytes .../0.4.14/images/spritesheet.svg | 156 + .../leaflet.draw-src.css} | 44 +- .../{ => 0.4.14}/leaflet.draw-src.js | 1873 +- .../leaflet.draw/0.4.14/leaflet.draw-src.map | 1 + .../leaflet.draw/0.4.14/leaflet.draw.css | 10 + .../leaflet.draw/0.4.14/leaflet.draw.js | 10 + .../public/js/vendor/leaflet.draw/README.md | 512 - .../leaflet.draw/images/spritesheet-2x.png | Bin 2078 -> 0 bytes .../leaflet.draw/images/spritesheet.png | Bin 1056 -> 0 bytes .../leaflet.draw/images/spritesheet.svg | 27 - .../js/vendor/leaflet.draw/leaflet.draw.js | 9 - .../public/js/vendor/leaflet/1.9.3/LICENSE | 26 + .../vendor/leaflet/1.9.3/images/layers-2x.png | Bin 0 -> 1259 bytes .../js/vendor/leaflet/1.9.3/images/layers.png | Bin 0 -> 696 bytes .../leaflet/1.9.3/images/marker-icon-2x.png | Bin 0 -> 2464 bytes .../leaflet/1.9.3/images/marker-icon.png | Bin 0 -> 1466 bytes .../leaflet/1.9.3/images/marker-shadow.png | Bin 0 -> 618 bytes .../vendor/leaflet/1.9.3/leaflet-src.esm.js | 14356 +++++++++++++++ .../leaflet/1.9.3/leaflet-src.esm.js.map | 1 + .../js/vendor/leaflet/1.9.3/leaflet-src.js | 14449 ++++++++++++++++ .../vendor/leaflet/1.9.3/leaflet-src.js.map | 1 + .../js/vendor/leaflet/{ => 1.9.3}/leaflet.css | 341 +- .../public/js/vendor/leaflet/1.9.3/leaflet.js | 6 + .../js/vendor/leaflet/1.9.3/leaflet.js.map | 1 + .../spatial/public/js/vendor/leaflet/LICENSE | 23 - .../public/js/vendor/leaflet/README.md | 34 - .../js/vendor/leaflet/images/layers-2x.png | Bin 2898 -> 0 bytes .../js/vendor/leaflet/images/layers.png | Bin 1502 -> 0 bytes .../vendor/leaflet/images/marker-icon-2x.png | Bin 4033 -> 0 bytes .../js/vendor/leaflet/images/marker-icon.png | Bin 1747 -> 0 bytes .../vendor/leaflet/images/marker-shadow.png | Bin 797 -> 0 bytes .../public/js/vendor/leaflet/leaflet-src.js | 9168 ---------- .../public/js/vendor/leaflet/leaflet.js | 9 - ckanext/spatial/public/webassets.yml | 12 +- 44 files changed, 30849 insertions(+), 10225 deletions(-) rename ckanext/spatial/public/js/vendor/leaflet.draw/{ => 0.4.14}/MIT-LICENSE.md (94%) create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/layers-2x.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/layers.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/marker-icon-2x.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/marker-icon.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/marker-shadow.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/spritesheet-2x.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/spritesheet.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/spritesheet.svg rename ckanext/spatial/public/js/vendor/leaflet.draw/{leaflet.draw.css => 0.4.14/leaflet.draw-src.css} (90%) rename ckanext/spatial/public/js/vendor/leaflet.draw/{ => 0.4.14}/leaflet.draw-src.js (64%) create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.map create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.css create mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.js delete mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/README.md delete mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet-2x.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.svg delete mode 100644 ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.js create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/LICENSE create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/layers-2x.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/layers.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/marker-icon-2x.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/marker-icon.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/marker-shadow.png create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.esm.js create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.esm.js.map create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.js create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.js.map rename ckanext/spatial/public/js/vendor/leaflet/{ => 1.9.3}/leaflet.css (51%) create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet.js create mode 100644 ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet.js.map delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/LICENSE delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/README.md delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/images/layers-2x.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/images/layers.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/images/marker-icon-2x.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/images/marker-icon.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/images/marker-shadow.png delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/leaflet-src.js delete mode 100644 ckanext/spatial/public/js/vendor/leaflet/leaflet.js diff --git a/ckanext/spatial/public/css/spatial_query.css b/ckanext/spatial/public/css/spatial_query.css index bd3d0c84..2d5defcc 100644 --- a/ckanext/spatial/public/css/spatial_query.css +++ b/ckanext/spatial/public/css/spatial_query.css @@ -37,7 +37,7 @@ #dataset-map-edit-buttons { display: none; } -.leaflet-draw-toolbar a.leaflet-draw-draw-rectangle { +.leaflet-touch .leaflet-draw-toolbar a.leaflet-draw-draw-rectangle { background-image: url("../img/pencil.png"); background-repeat: no-repeat; background-position: 50% 50%; diff --git a/ckanext/spatial/public/js/spatial_query.js b/ckanext/spatial/public/js/spatial_query.js index 78fe286f..0f491370 100644 --- a/ckanext/spatial/public/js/spatial_query.js +++ b/ckanext/spatial/public/js/spatial_query.js @@ -103,6 +103,7 @@ this.ckan.module('spatial-query', function ($, _) { polyline: false, polygon: false, circle: false, + circlemarker: false, marker: false, rectangle: {shapeOptions: module.options.style} } diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/MIT-LICENSE.md b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/MIT-LICENSE.md similarity index 94% rename from ckanext/spatial/public/js/vendor/leaflet.draw/MIT-LICENSE.md rename to ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/MIT-LICENSE.md index 91da7e64..ed05bfca 100644 --- a/ckanext/spatial/public/js/vendor/leaflet.draw/MIT-LICENSE.md +++ b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/MIT-LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2012-2016 Jacob Toye and Leaflet +Copyright 2012-2017 Jon West, Jacob Toye, and Leaflet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/layers-2x.png b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/layers-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..200c333dca9652ac4cba004d609e5af4eee168c1 GIT binary patch literal 1259 zcmVFhCYNy;#0irRPomHqW|G1C*;4?@4#E?jH>?v@U%cy?3dQAc-DchXVErpOh~ z-jbon+tNbnl6hoEb;)TVk+%hTDDi_G%i3*RZ&15!$Fjr^f;Ke&A@|?=`2&+{zr+3a z{D*=t(`AXyS%X7N z%a#RZw6vD^t_rnM`L4E>m=U&R!A-&}nZIi$BOPvkhrCuUe@BN~-lRD)f44;J%TwgE zcze8u!PQ_NR7?o(NylLXVTfDO zxs5=@|GsYEsNo4M#nT%N!UE(?dnS)t2+{ELYAFp*3=iF=|EQnTp`#vlSXuGVraYo? z+RCzXo6h3qA8{KG?S4nE(lM+;Eb4nT3XV;7gcAxUi5m)`k5tv}cPy()8ZR3TLW3I- zAS^}cq-IJvL7a4RgR!yk@~RT%$lA7{L5ES*hyx)M4(yxI$Ub(4f)K|^v1>zvwQY!_ zIrWw8q9GS^!Dp~}+?mbnB6jDF8mVlbQ!jFKDY;w=7;XO{9bq7>LXGK24WA`;rL)_Z z)&j}pbV(;6gY;VMhbxgvn`X;6x}VUEE-7 z%)7j-%t8S=ZL3yc)HbXDAqJZvBTPoiW_A-+a8m3_Z?v{DN7Tnr#O_VUMT0UBt$;p` zDh6JbGHN8JJ*JN%y2%msb97@_S>9!%Egwk;?PEkU9ntz&3uR}%Fj5d$JHQbQb3}a{ zSzFT^#n=VInPpcAS}CNxj?_ zVscANk5Cfz(51EI1pz};AWWb|kgbYNb4wCEGUn3+eMUMV?1-{=I4TlmLJMot@rd07 zZuo2hk1ccu{YmGkcYdWAVdk{Z4Nm?^cTD&}jGm+Q1SYIXMwmG*oO*83&#>l%nbR`G zhh=lZ%xIb7kU3#;TBbfECrnC9P=-XpL|TG2BoZdj61*XiFbW8?1Z_wp%#;>${SUIy V$8qr;L*)Pf002ovPDHLkV1hYLS~36t literal 0 HcmV?d00001 diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/layers.png b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..1a72e5784b2b456eac5d7670738db80697af3377 GIT binary patch literal 696 zcmV;p0!RIcP)*@&l2<6p=!C&s@#ZL+%BQvF&b?w6S%wp=I>1QHj7AP5C)IWy#b znXXB;g;j=$a-tW89K%FbDceHVq&unY*Wx3L#=EGWH=rjqnp|4c_Ulec!ql3#G-5ZF zVlbBA@XP=)C8U&+Lrc)S4O5%1$&{(;7R^K(CSnvSr$v;+B$8q&7Bf|h$#PARo1^%M zf1H^nG-EiXVXr07OH(*8R)xa|FD;lXUlg_-%)~ZGsL2cX0NXaAzN2q%jqLRR6ruVk8`Jb7n#{`T;o@`F= z#3YcynIR^s83UNF3D!f5m#Mg)NJ24&Qfrqb&_z=yF;=B)#9Iq7u-@^O!(mW{D;qvr zPc)gVb%aowtS8m@ElL4A9G>w#ffQ~q{i&_i)*6f^)Sz|C?C>zb4Uo?H<-&Hz@a?J; z$ml@zGygWofb9$ZBj6aLjpLhsT2AzjOu=-*u_gSCULuqp7Vds@BKcX=lOk~^Pb;%&wG9p3o}FL;Zuhp5D3)R z2yY2yCGfH2=LJmOkQw^9n>daF6?Fz>oOD64$CM+_T`j0x%{zb|G zWolt{H|diO#S`|$6RM$ zYQuE4RW{2yZ`>fAt>jzyYyOB?)~HrQBlbbLT5yF%Xq8FEuzo80dd{%Q!{_)^mTE`^ z2$xe>TH$qiDJ+}(ajTp$Y*4vgGRrt^_?JwUO3+hm&{Mb<8aRtf7%F@*!JJv* zmcB*cag=-t4U&g79u1krRAKHHm?ZXHP8z-#KdAM9?vU7sxldD%A5;r0Rk~kblro}5 z9YhoJP18m~=v^kMBWPltYTV$TD;r4n^eZVWmDs^6;ZN_RG+a#^(N18a+%xd;JvScL zu54_hiMdFR4767cmcp!KOryQBQG{$|3e)h(z_sY-NRM>A$84n-CdxAt6V242bQmV| z86*uGCJtVTXCvLyz=eM@jE-Vz#IeA4DY~VhqL`R_>D;YIh9amQX~+l$Sfbohb*X)d zKiDG!?8t|64T_+_Jzbv6K)P|KG-6qDVGPYUwpPqb#c;-juz~ZW0bFF4JlB>cOB#?3 z9XJ~@0J1u{T_(66oVpmpLOkqOk6}qY=vN7820OS|_L-o5(4!i~Ivv=j{IKzS2m>C_ zhm9Npo09&0s*wy#K%InNpSW)yCZOhAFheUQtcXnn!x)WSjonNUm7@fguKPg0C3ESs~`Bd3Pyd$@XU8m z0JZWv0l=fZ{{jH?{!9Nt!mEGL|9_Oug?i>9H?4E!|Krk+(hy9WRiM;!>w8@J9&fq& z${#rK1z4j2$*KVGO=b{ivL6FFEPprv0No7|9RPB_H>dzW{;{(>P`XWmKn^Y#<8`e9 zc*;k@X>z(^khkvlh3UB1ICnF@RRHbZaQhkI;sl{txVGnBEzaFKZpw96Fm8qu^5@!a z+db!omc48o>}VvJr!j9Mpo^ZMPs2FKikZu-3edWhZ~5&Mp15G60gsVYic)|~eH4Q6 zF8d5^efqo~DD}CwRpRO|j91O-zygw(bv;<>V5MDzeC#nk zosJI@GCU;ylx)tp87H~!5Gl8^4UxdZ-ZLrRy7g=zwjIe|v>O(6W-QBuv-7h4HTLcz&ce9H!^9o^4XLD_t08@f%uD+tdxMAHzHi z6>y1>XBw|wNRu9u6j`13s*X9iz%Z1zep^?+<}$-U*uzd9$?LD0QWc+GSyhyvx<?!6YcvM{vC6CN2-dD>XyCsuOMe zdjA0H)tFMHvR%5Uqd_swkzDP0t5)bhy5xwusp(WsD}~`13N0NuN78MHcc03G_@3v- zZOvStb!W8+G+$o+mNh5)?USue0<9~5nql|l&C!mcb^cmUZGk2gF&p9IOMcs@2-WZX z+M_WESiwx34!IyuOY(`!=Sit;If5uuYqSJm`D>ogL1P7x5=v2W{zicaAxUs>WGzTn zQv?x3HR!VK$IB{-D-)cU&hLE;M2}umynSZBHRVLCW#WkaY>!>~#*V;;^Ck!H4Swwp zDHCGo7gMu}4-?)ga$s&da$6}|l&eSgpl~CnG5lbg z7&|&nHy^@(l0;d(4qw!>Pc+03BPqwvhV@DjJr)KAb74dUY>mzPErgW+cGhAfAE(Hx zg7S551PZuugrt1qVHk*xE*1`NeDO|ZnOO1ye(Ps{N=r+Q=S*|(%4dYb+TIr5*H@Ka z&IFce5q4snQ7O4sQm?Pxu??B#U>#Bu+HC!Ti{Sl150Y#4pk06Ac+lU@`2YRqk-uHH zZoIWi#kr-H+gi|P?w*2JMQ7U)c>*fCAPTksemc#0N4+Zgz+o*bN1@=(#&Q(RLz+r2 zQx|up>q>^w^^^t*`_3bp*JBDwCvP3iT>oMu+dLrW{Yd*GhC1Kx;_L$zF%*j;?iDxZ zrao$m-Bw;}qtlD8Ts>}{*(A|it9iEx_ZRY$yVv3y#q}J<;l}p;3_y0NqKJBW%sac- z#s<-=rSr4%CNFQcuf<8$A3ba|hx+!=-B0jwr*}bFG1p0OLTqz#DYd z16dVY=E5n{UkaA*7{FAF7c$=SE0gV@(AxW_6rfOFvBFyfQpO=ChwyqQo?nZOT`6__ zP3(sCcoy|xktOO{hUoSFKDM)^*yWXvlS$9yTyC~k^q#t~$$O;oU_E7XGiY~S^b+mS zVh=RZHn+0(T-ooM5xx%AW=ZUqv zgKQURIr-z7x5ejdVPYlT>F)dyou|#!MM#5qXK_BVQyz*bJ!*A&^rr((=SaeGlUNwV z01+e{DcnsPPIth+gTfMc34NrqGRM-T5f0=)<0vZ6?K`I0Z1Y3GdqxI|$iyh%qoeNX UQO-*oc+)|Q_08}VdXD6O0C*xx%>V!Z literal 0 HcmV?d00001 diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/marker-icon.png b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/marker-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..950edf24677ded147df13b26f91baa2b0fa70513 GIT binary patch literal 1466 zcmV;r1x5OaP)P001cn1^@s6z>|W`000GnNklGNuHDcIX17Zdjl&3`L?0sTjIws<{((Dh&g-s0<@jYQyl?D*X^?%13;ml^gy> ziMrY_^1WI=(g@LMizu=zCoA>C`6|QEq1eV92k*7m>G65*&@&6)aC&e}G zI)pf-Za|N`DT&Cn1J|o`19mumxW~hiKiKyc-P`S@q)rdTo84@QI@;0yXrG%9uhI>A zG5QHb6s4=<6xy{1 z@NMxEkryp{LS44%z$3lP^cX!9+2-;CTt3wM4(k*#C{aiIiLuB>jJj;KPhPzIC00bL zU3a#;aJld94lCW=`4&aAy8M7PY=HQ>O%$YEP4c4UY#CRxfgbE~(|uiI=YS8q;O9y6 zmIkXzR`}p7ti|PrM3a}WMnR=3NVnWdAAR>b9X@)DKL6=YsvmH%?I24wdq?Gh54_;# z$?_LvgjEdspdQlft#4CQ z`2Zyvy?*)N1Ftw|{_hakhG9WjS?Az@I@+IZ8JbWewR!XUK4&6346+d#~gsE0SY(LX8&JfY>Aj)RxGy96nwhs2rv zzW6pTnMpFkDSkT*a*6Dx|u@ds6ISVn0@^RmIsKZ5Y;bazbc;tTSq(kg(=481ODrPyNB6n z-$+U}(w$m6U6H$w17Bw+wDaFIe~GvNMYvnw31MpY0eQKT9l>SU``8k7w4)z!GZKMI z#_cEKq7k~i%nlK@6c-K?+R;B#5$?T#YpKD`t_4bAs^#E+@5QW$@OX3*`;(#{U^d-vY)&xEE>n5lYl&T?Amke9$Lam@{1K@O ze*LXqlKQHiv=gx+V^Cbb2?z@ISBQ*3amF;9UJ3SBg(N|710TLamQmYZ&Qjn2LuO<* zCZlB4n%@pc&7NNnY1}x+NWpHlq`OJEo|`aYN9<`RBUB+79g;>dgb6YlfN#kGL?lO_ z!6~M^7sOnbsUkKk<@Ysie&`G>ruxH&Mgy&8;i=A zB9OO!xR{AyODw>DS-q5YM{0ExFEAzt zm>RdS+ssW(-8|?xr0(?$vBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrP zs(rTFX@W#t$hXNvbgX`gK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z!4=W zi$giRj1{ zt;2w`VSCF#WE&*ev7jpsC=6175@(~nTE2;7M-L((0bH@yG}-TB$R~WXd?tA$s3|%y zA`9$sA(>F%J3ioz<-LJl*^o1|w84l>HBR`>3l9c8$5Xr@xCiIQ7{x$fMCzOk_-M=% z+{a_Q#;42`#KfUte@$NT77uaTz?b-fBe)1s5XE$yA79fm?KqM^VgLXD07*qoM6N<$ Ef<_J(9smFU literal 0 HcmV?d00001 diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/spritesheet-2x.png b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/images/spritesheet-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c45231aff86b1344333414cdfa7a2a74d38d5304 GIT binary patch literal 3581 zcmb_f`#;nBAOB2clxyUYb<>4Ynk1K8OH(Z&mzm3$Tyx3X$HJB_R+xiO7)ip%jA&_M zl!g=0DI?9L49i_)E<@k-{pI@)d|!{p`}%mjuaEcZ`Fy_bU9q_!y?ys~005+s7ZLUV zAm%Kpn@EU@^7{ODOi{5l!UE|iA+k6LAF`+|8G6wrLge4~CqXPU^I}Aus*#q?kq$V& zNc`1sUjUEC!vjKsBD}AL`oeMH{&}+|yG7mvB;u@NO#aLuF$Lxy3!R_SjbJ6a7tRHv zBz1~*mIVgf1VOjVip+7Z;f4r4FSwLrXXkj}z+=1B1NW~XOT>;Iw+;<}CA@r#lE0(? zd3i0IzewW74Wp>ECohsvqkk$E}Ms%B2GGcE&2IsxA>@@3X3m zDG^w?t(hOXlBT_cpS&2Dn|fFVwPcdu>G`b>)^_?a2;I}xzh~>4em_0}F`+6N=b))5 zLvEh|>p9CCdt9b&^Nrhjjm187ePKOPzjf=DLC&QTs(QE@DG$KNeuI&6ASNZCEEYc) z^=4&drTg>cr(@e`G~Lz)@iTjygDgj_!8H%>P9nM8^sHQdKsdqY=A2N?F-n=V2k=N+ zfT{pCVlg&|RwE;$y}1t`8uq<7bbQgI+mNCQf^{R4P0N-*8sv-e-K#wf)44&Bkv)x} z-`}1E>QMTXP8^-?#Rkp$tp3h{vasOc>3I`eGI`o2V^r3XUao;FTaV2oHxysdT<=-l zAV$OZEDRid@nB)L+Ah%PGbix1ApRKl<#nz!eQMlpHZo`z5XX)hB({%*YvdT{-Uudg z|FL-Ow6wHTIXgSMDMxC8REOLg)GnKxotT&~b#QRV$vM#IC?zAa>6dZn24tYVnVyz? zsRVDn(0*_jOKAvJ-MxD<_t7J!LNnKLR2t~j3T;nTuhEl8 zq+Wdb5#@StSLM2@pHQEk#jOkc{LH#SKlsbMi#)ToX5s;ufI`UB(}f$1pgvZdV7h-v zz2@Jrdp2$H9E>$~WyDvk zt*tp#xz&YWP<4(?*7(wbLLiYg>jn}mgiijyQ;=2i2Uj81L3JtlR_7o)yz zH)gs}C?qm!YZoO*2Jn&UCtw06YS4DCQRI&s0b_pLOIp=OH71JzkH&X$8vcHMA!cza z^MB>VmXx0ik5cSYLSKMfMtLklUNOh{e8;5^c8VLguc!6Bu3amua6D>ybIo{I0huvi zcKY<{xo?yCFrYi>>WXq%S?YUIgMLUlY^>Sz3K2r6EUfqcG`R2H*Ju0k6T5X8Wf(};_CV?wb+smDvO#Pu`cr_B=a3D zCk$?wcQ3w7ZodT(K(3?SKFfb+Ix;6dsd$?K-*h?4AfbsoAnVy>C)pQe>uB3hYcxFB z6gD?)^DsAU=Jm-H7O@oEJVr{UPYK4tnf}UZ#r7%tARkLX1v|w?wo`)j+`zahlb}W6 z&8)^H)VsuTygfZ1UqC!LjM2}vdIAcIHP;+=Jh!j?v|X!v(d?*tVYT8cuH{5Sn?0>Z z#oGk9`eA&A!A51x30pvdr$8UIhy3J6`it=32Rh?!fIfOC0WQ@d+Y>ub1sFl3s;P`l)3e7w zn%~@l5@{fQ3yB=pb3WmW!VVfu8J%@t^sB5_@kxV;Mr!%w9_N*iH|!txcrm5ynDR0$ z_6gxeV%?MEcAj2dFi}6)%`IO3?)NmMud$Sl^C;VUZLmwzk+Ssc%Z&olE*&`X$Zp!^8B9jJ~>PDhha_Ra+2GOEko*&K!KX z<$k*iSI40`>^Zb7aOPfYxxv;`E!vGw?Q#66*r98#lJ@7mu&%c*Z%AtndqyxTDk`Fl zS~461(~tn2EcKPloZjY?oqP;6ob$d9a~`K2w=xK4vaS> z>mxIMzF5SF_YGK&8+LtzC10-Z$dYqm3qH_wf&J%C8fkHkz>O~p%vP+jHDk-2wOk*?e6=`v!$6gD zXD08sxy5Q=GY-`Yuk}>`$mFcmkL60vGtL;unK8Al(&I^EV{dxB;}y>%T7%nOR4(|e zb{oizu%cIL9)pGU99zhW&y4TE_hv6~(WtM1+nWPXdiq{Y+Zoq^OB5cgMqZa;b$iAY zgL1EroM4GatZ8d#W@-%hUaW#mZa)VSwEaxQpPl8SD_uDY+mwJ;bm1NQ@?QxU>Sfm| zUHVjlPHKL(o1!IN&lq}1mTSOuTvAi7VTa|*LRPBI^X|0^9itfF_;u5?6It@KXl**+ z85lTO18x>q#!z_G&4D^1%13bqUyhVd4k{=p@PpY_4)C1Un(E23B*f@JCT7-a_i1@~ z)SqC0#2`_)pYcsa8W%($Rtye_f}Qt;a4NS^tV5-kV}8ftyMK&tSk%*silA!u`xW5O z&=3(ipI;KHRqgsw#4mdtd(Q`aRA77ktW_G(%G)n7dtsFsykw-Z^^G20@nsn{Klx~~ z5*+#AdCuPondXp zTH4~hI|Vabv^^)yZF`$o#WrheYxih(yfVo_`u6E^5bqP}k#WDhaEqZ`8>@80FafGH zIMg6TL1W7%Gtkqv89svd>uvnO^lUXQew###hytMi&4CjtGP`bZK|JwXxuX$j0i4%< z7q)Jbx9Xx@9a!k!W0SLuzti&H1YkJuM2bg4%+DnKg#*HH<^U2kNCR!*0%!SHBV@*L zJ4MF&f>eZL;x`)4@E{O~b1$6PNusXCZEAEp$d;DvBs8|E)c&yfqN1y=-V`H~3;Ox~ z3OVd4q)Y8?`}z7x!n+Txhmf0Gl?XHzJ3M(u=2tw?e=OgOv``~u99-C9kA+rHlPk9G zN^f%DTuX0bYhpd(&Z7$Rb93wSEkb;dik79qwY88bkxtrAO7r}FZ;o#{T3}H$|4530 zX?$CBE{VkxZkB-*NSSGvyHdKjOk#7ZZ;vmpepVzWKN&;T;iM&?HAROH^aw5aQCU3T z!H$W7f`UeYZb^w~pD@u`E)j{pub|O=@gha}NA2UAhE*1$A_luWw0n(Bp602PmQ(vEjZrBI>^%!!+W>I=~TZtF*Peo+n(heqw2o|zfU>yG^`j4kB5k|9F|j&*W=71 z=W_?-9ZS7Vt%W>>7E?d0Mf0xf{queA>-yf`?|on2KR(yzzCYI|^`awYx4gPM0ARPh z9U2D!?2!14kd+qaoK_uw@wFr3ti6k@IAUe5rHE^}P`k?!07!rSd0>c0^AqCce)2gy z**Szrj=mc12cn~+jRQ!55x!SL{ftAx{qt8&tBZg0?a|gQl!9fpN06qQip=Wv)D%Y- z+bg+w(aFW?lgyt1|9)ik>q%L+jrR?%LpEbnIpOt#PI47gtD8Xgkp?04z@vSC?o-yT zs)RLFQ&Mk)a7XZwPQ1fXhWF4FZ(?J55vMD5sr6?s^WZS?6c1xEs1ktlD89lbT4wr7L0U z?Ey%L8UfB3 z6Oo*h^2-)Lt*E^xr-tS4g2z6x4@=;H7tUP;O04r6_s<$_v*!eF15ymD~r9(3MS4qmRge#Kgp?GsW8{XrmeD>HT?B zYsDmcE>bm3*w)sDVF&?Dp~-{b&E1rimUhg+>dm#UI1$hEGX>wex1SLpMe9RhFR<5~;%m8wdW36OCQws-U? zbNLM@^k(Q8_sGqZ=lG#KD_po)L;L4B_Yzc8kXpSterHwlZVn25JZCp>)@&EDDq6c_LRD2YN?%BUQzu*j1W4nA z9ywrRV`C;B)G698=(YVU$kX1a9QR2JR1$cbY<8(uUpe?J6ROf%NZ|G8XPXUqoE5lY zN~-R9YVZdK2M5wk>hL*Djkt=fz&7)NFa!t{v(VA8eseemAE1UD2#Zo0-O;Xw1a{=e zCa0eZ)~<{M-duKg+j}+OpqI`5s~Sd>ra?76Qoz~SdD{hBC{sTGhb%y<${}DTVb1>W zy4h!1VdgNg+bn;0#2~Mc)9~QChvS926L9BiTz@yCOTe4GL4hYvyJ0y`n!2Cjg17|I z6Z|1UfGQFkCH9^uvf9jEue9D~IOsLR8WO&)?r#I#Jhs#P~1v1k(GWp2GNdUiy& zFkVOT{t}{T5Kh1T#hW`m6!3@N<=edW%?CKI=0nKdZO^E!Gp`B?xm+%y|Ks0XLrl`s z*)3Pl@rK#qbaIoDn!>Oc&yh2A)~7Fh7xYvnjD1O4b*Q;ydC#^%0o^lh2|yEs&m7VGd*3r@g*4vB#4OMn2LG^OP=)U z%Q2`;tltV!*h`;LiFz^LwGe6pF^+p_G;C@;AKtdqPD*7I-`(47h>#0xdIt$(MG`dk zj-;Zx30`0vhjSYitI?ZPm&*|5m*EUIG;JAAHMJ%<2 z>h;tap8Q9i@Ja!MZ@7fV8t!>s51Tn)nkiUcU-rtBOfOo<-aBWaoBOZeI|(OHD9+J- zO<4V#$CFY3^X8if#me{Q3N&4=al@ts2J{h`nPYpYt>>Bl03yB@A9L$QC!J0Y0hE1K zH_7C+ALn1T)^@1%3P9{($KnPze2vZH=3p&Ho=K>j1A6+-h~D9OBbsT9V5RH01*1Qf z5O1y6xoAFeQJ=owm&-n7Ts-hmyMyuc>jZCI1`=-7fR#HBJ!>a;3je&1G|Iqa;~@OU zFU2V&e{Ld(%d-`&j|a|4(L^E^+csfMgU~=v&b-S#u zudmR(Iq2qsS45tW+~pasy_~(gwzkGDEiENAPH9TgYTR28V$BxX8nc^wOf4;U9y~>R z=_cYoV~U4^X$~Ghm30w3SbBPl>WTiHVD#iOn_Aw7-7~XdSf_VAR{)g66#YCQJ%5%~ i{eP_FzxHA`84t@4c6%tZ%} + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.css b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.css similarity index 90% rename from ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.css rename to ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.css index 95cc2982..dffca06b 100644 --- a/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.css +++ b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.css @@ -26,7 +26,8 @@ background-image: url('images/spritesheet.png'); background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg'); background-repeat: no-repeat; - background-size: 270px 30px; + background-size: 300px 30px; + background-clip: padding-box; } .leaflet-retina .leaflet-draw-toolbar a { @@ -40,6 +41,17 @@ text-decoration: none; } +.leaflet-draw a .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + /* ================================================================== */ /* Toolbar actions menu /* ================================================================== */ @@ -60,13 +72,13 @@ } .leaflet-right .leaflet-draw-actions { - right:26px; - left:auto; + right: 26px; + left: auto; } .leaflet-touch .leaflet-right .leaflet-draw-actions { - right:32px; - left:auto; + right: 32px; + left: auto; } .leaflet-draw-actions li { @@ -79,17 +91,17 @@ .leaflet-draw-actions li:last-child a { -webkit-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; } .leaflet-right .leaflet-draw-actions li:last-child a { -webkit-border-radius: 0; - border-radius: 0; + border-radius: 0; } .leaflet-right .leaflet-draw-actions li:first-child a { -webkit-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; } .leaflet-draw-actions a { @@ -177,6 +189,14 @@ background-position: -120px -1px; } +.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker { + background-position: -273px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker { + background-position: -271px -1px; +} + /* ================================================================== */ /* Edit toolbar /* ================================================================== */ @@ -227,7 +247,7 @@ background: rgba(0, 0, 0, 0.5); border: 1px solid transparent; -webkit-border-radius: 4px; - border-radius: 4px; + border-radius: 4px; color: #fff; font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif; margin-left: 20px; @@ -281,10 +301,10 @@ /* ================================================================== */ .leaflet-edit-marker-selected { - background: rgba(254, 87, 161, 0.1); + background-color: rgba(254, 87, 161, 0.1); border: 4px dashed rgba(254, 87, 161, 0.6); -webkit-border-radius: 4px; - border-radius: 4px; + border-radius: 4px; box-sizing: content-box; } @@ -302,4 +322,4 @@ .leaflet-oldie .leaflet-draw-toolbar { border: 1px solid #999; -} \ No newline at end of file +} diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw-src.js b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.js similarity index 64% rename from ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw-src.js rename to ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.js index 2f1c1298..ae3e876f 100644 --- a/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw-src.js +++ b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.js @@ -1,17 +1,92 @@ /* - Leaflet.draw, a plugin that adds drawing and editing tools to Leaflet powered maps. - (c) 2012-2016, Jacob Toye, Smartrak, Leaflet + Leaflet.draw 0.4.14, a plugin that adds drawing and editing tools to Leaflet powered maps. + (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet - https://github.com/Leaflet/Leaflet.draw - http://leafletjs.com -*/ -(function (window, document, undefined) {/* + https://github.com/Leaflet/Leaflet.draw + http://leafletjs.com + */ +(function (window, document, undefined) {/** * Leaflet.draw assumes that you have already included the Leaflet library. */ +L.drawVersion = "0.4.14"; +/** + * @class L.Draw + * @aka Draw + * + * + * To add the draw toolbar set the option drawControl: true in the map options. + * + * @example + * ```js + * var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13); + * + * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + * attribution: '© OpenStreetMap contributors' + * }).addTo(map); + * ``` + * + * ### Adding the edit toolbar + * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map. + * + * ```js + * var map = L.map('map').setView([51.505, -0.09], 13); + * + * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + * attribution: '© OpenStreetMap contributors' + * }).addTo(map); + * + * // FeatureGroup is to store editable layers + * var drawnItems = new L.FeatureGroup(); + * map.addLayer(drawnItems); + * + * var drawControl = new L.Control.Draw({ + * edit: { + * featureGroup: drawnItems + * } + * }); + * map.addControl(drawControl); + * ``` + * + * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that + * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon. + * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon, + * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a + * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons). + */ +L.Draw = {}; -L.drawVersion = '0.3.0-dev'; - +/** + * @class L.drawLocal + * @aka L.drawLocal + * + * The core toolbar class of the API — it is used to create the toolbar ui + * + * @example + * ```js + * var modifiedDraw = L.drawLocal.extend({ + * draw: { + * toolbar: { + * buttons: { + * polygon: 'Draw an awesome polygon' + * } + * } + * } + * }); + * ``` + * + * The default state for the control is the draw toolbar just below the zoom control. + * This will allow map users to draw vectors and markers. + * **Please note the edit toolbar is not enabled by default.** + */ L.drawLocal = { + // format: { + // numeric: { + // delimiters: { + // thousands: ',', + // decimal: '.' + // } + // } + // }, draw: { toolbar: { // #TODO: this should be reorganized where actions are nested in actions @@ -33,7 +108,8 @@ L.drawLocal = { polygon: 'Draw a polygon', rectangle: 'Draw a rectangle', circle: 'Draw a circle', - marker: 'Draw a marker' + marker: 'Draw a marker', + circlemarker: 'Draw a circlemarker' } }, handlers: { @@ -43,6 +119,11 @@ L.drawLocal = { }, radius: 'Radius' }, + circlemarker: { + tooltip: { + start: 'Click map to place circle marker.' + } + }, marker: { tooltip: { start: 'Click map to place marker.' @@ -79,31 +160,35 @@ L.drawLocal = { toolbar: { actions: { save: { - title: 'Save changes.', + title: 'Save changes', text: 'Save' }, cancel: { - title: 'Cancel editing, discards all changes.', + title: 'Cancel editing, discards all changes', text: 'Cancel' + }, + clearAll: { + title: 'Clear all layers', + text: 'Clear All' } }, buttons: { - edit: 'Edit layers.', - editDisabled: 'No layers to edit.', - remove: 'Delete layers.', - removeDisabled: 'No layers to delete.' + edit: 'Edit layers', + editDisabled: 'No layers to edit', + remove: 'Delete layers', + removeDisabled: 'No layers to delete' } }, handlers: { edit: { tooltip: { - text: 'Drag handles, or marker to edit feature.', + text: 'Drag handles or markers to edit features.', subtext: 'Click cancel to undo changes.' } }, remove: { tooltip: { - text: 'Click on a feature to remove' + text: 'Click on a feature to remove.' } } } @@ -111,11 +196,195 @@ L.drawLocal = { }; -L.Draw = {}; +/** + * ### Events + * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different + * actions users can initiate. The following events will be triggered on the map: + * + * @class L.Draw.Event + * @aka Draw.Event + * + * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct. + * + * @example + * ```js + * map.on(L.Draw.Event.CREATED; function (e) { + * var type = e.layerType, + * layer = e.layer; + * + * if (type === 'marker') { + * // Do marker specific actions + * } + * + * // Do whatever else you need to. (save to db; add to map etc) + * map.addLayer(layer); + *}); + * ``` + */ +L.Draw.Event = {}; +/** + * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String + * + * Layer that was just created. + * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker` + * Triggered when a new vector or marker has been created. + * + */ +L.Draw.Event.CREATED = 'draw:created'; + +/** + * @event draw:edited: LayerGroup + * + * List of all layers just edited on the map. + * + * + * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved. + * + * @example + * ```js + * map.on('draw:edited', function (e) { + * var layers = e.layers; + * layers.eachLayer(function (layer) { + * //do whatever you want; most likely save back to db + * }); + * }); + * ``` + */ +L.Draw.Event.EDITED = 'draw:edited'; + +/** + * @event draw:deleted: LayerGroup + * + * List of all layers just removed from the map. + * + * Triggered when layers have been removed (and saved) from the FeatureGroup. + */ +L.Draw.Event.DELETED = 'draw:deleted'; + +/** + * @event draw:drawstart: String + * + * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker` + * + * Triggered when the user has chosen to draw a particular vector or marker. + */ +L.Draw.Event.DRAWSTART = 'draw:drawstart'; + +/** + * @event draw:drawstop: String + * + * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker` + * + * Triggered when the user has finished a particular vector or marker. + */ + +L.Draw.Event.DRAWSTOP = 'draw:drawstop'; + +/** + * @event draw:drawvertex: LayerGroup + * + * List of all layers just being added from the map. + * + * Triggered when a vertex is created on a polyline or polygon. + */ +L.Draw.Event.DRAWVERTEX = 'draw:drawvertex'; + +/** + * @event draw:editstart: String + * + * The type of edit this is. One of: `edit` + * + * Triggered when the user starts edit mode by clicking the edit tool button. + */ + +L.Draw.Event.EDITSTART = 'draw:editstart'; + +/** + * @event draw:editmove: ILayer + * + * Layer that was just moved. + * + * Triggered as the user moves a rectangle; circle or marker. + */ +L.Draw.Event.EDITMOVE = 'draw:editmove'; + +/** + * @event draw:editresize: ILayer + * + * Layer that was just moved. + * + * Triggered as the user resizes a rectangle or circle. + */ +L.Draw.Event.EDITRESIZE = 'draw:editresize'; + +/** + * @event draw:editvertex: LayerGroup + * + * List of all layers just being edited from the map. + * + * Triggered when a vertex is edited on a polyline or polygon. + */ +L.Draw.Event.EDITVERTEX = 'draw:editvertex'; + +/** + * @event draw:editstop: String + * + * The type of edit this is. One of: `edit` + * + * Triggered when the user has finshed editing (edit mode) and saves edits. + */ +L.Draw.Event.EDITSTOP = 'draw:editstop'; + +/** + * @event draw:deletestart: String + * + * The type of edit this is. One of: `remove` + * + * Triggered when the user starts remove mode by clicking the remove tool button. + */ +L.Draw.Event.DELETESTART = 'draw:deletestart'; + +/** + * @event draw:deletestop: String + * + * The type of edit this is. One of: `remove` + * + * Triggered when the user has finished removing shapes (remove mode) and saves. + */ +L.Draw.Event.DELETESTOP = 'draw:deletestop'; + +/** + * @event draw:toolbaropened: String + * + * Triggered when a toolbar is opened. + */ +L.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened'; + +/** + * @event draw:toolbarclosed: String + * + * Triggered when a toolbar is closed. + */ +L.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed'; + +/** + * @event draw:markercontext: String + * + * Triggered when a marker is right clicked. + */ +L.Draw.Event.MARKERCONTEXT = 'draw:markercontext'; + + +L.Draw = L.Draw || {}; + +/** + * @class L.Draw.Feature + * @aka Draw.Feature + */ L.Draw.Feature = L.Handler.extend({ - includes: L.Mixin.Events, + // @method initialize(): void initialize: function (map, options) { this._map = map; this._container = map._container; @@ -127,28 +396,45 @@ L.Draw.Feature = L.Handler.extend({ options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions); } L.setOptions(this, options); + + var version = L.version.split('.'); + //If Version is >= 1.2.0 + if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { + L.Draw.Feature.include(L.Evented.prototype); + } else { + L.Draw.Feature.include(L.Mixin.Events); + } }, + // @method enable(): void + // Enables this handler enable: function () { - if (this._enabled) { return; } + if (this._enabled) { + return; + } L.Handler.prototype.enable.call(this); - this.fire('enabled', { handler: this.type }); + this.fire('enabled', {handler: this.type}); - this._map.fire('draw:drawstart', { layerType: this.type }); + this._map.fire(L.Draw.Event.DRAWSTART, {layerType: this.type}); }, + // @method disable(): void disable: function () { - if (!this._enabled) { return; } + if (!this._enabled) { + return; + } L.Handler.prototype.disable.call(this); - this._map.fire('draw:drawstop', { layerType: this.type }); + this._map.fire(L.Draw.Event.DRAWSTOP, {layerType: this.type}); - this.fire('disabled', { handler: this.type }); + this.fire('disabled', {handler: this.type}); }, + // @method addHooks(): void + // Add's event listeners to this handler addHooks: function () { var map = this._map; @@ -157,12 +443,14 @@ L.Draw.Feature = L.Handler.extend({ map.getContainer().focus(); - this._tooltip = new L.Tooltip(this._map); + this._tooltip = new L.Draw.Tooltip(this._map); L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this); } }, + // @method removeHooks(): void + // Removes event listeners from this handler removeHooks: function () { if (this._map) { L.DomUtil.enableTextSelection(); @@ -174,23 +462,32 @@ L.Draw.Feature = L.Handler.extend({ } }, + // @method setOptions(object): void + // Sets new options to this handler setOptions: function (options) { L.setOptions(this, options); }, _fireCreatedEvent: function (layer) { - this._map.fire('draw:created', { layer: layer, layerType: this.type }); + this._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type}); }, // Cancel drawing when the escape key is pressed _cancelDrawing: function (e) { - this._map.fire('draw:canceled', { layerType: this.type }); if (e.keyCode === 27) { + this._map.fire('draw:canceled', {layerType: this.type}); this.disable(); } } }); + + +/** + * @class L.Draw.Polyline + * @aka Draw.Polyline + * @inherits L.Draw.Feature + */ L.Draw.Polyline = L.Draw.Feature.extend({ statics: { TYPE: 'polyline' @@ -217,7 +514,7 @@ L.Draw.Polyline = L.Draw.Feature.extend({ maxGuideLineLength: 4000, shapeOptions: { stroke: true, - color: '#f06eaa', + color: '#3388ff', weight: 4, opacity: 0.5, fill: false, @@ -225,10 +522,14 @@ L.Draw.Polyline = L.Draw.Feature.extend({ }, metric: true, // Whether to use the metric measurement system or imperial feet: true, // When not metric, to use feet instead of yards for display. + nautic: false, // When not metric, not feet use nautic mile for display showLength: true, // Whether to display distance in the tooltip - zIndexOffset: 2000 // This should be > than the highest z-index any map layers + zIndexOffset: 2000, // This should be > than the highest z-index any map layers + factor: 1, // To change distance calculation + maxPoints: 0 // Once this number of points are placed, finish shape }, + // @method initialize(): void initialize: function (map, options) { // if touch, switch to touch icon if (L.Browser.touch) { @@ -249,6 +550,8 @@ L.Draw.Polyline = L.Draw.Feature.extend({ L.Draw.Feature.prototype.initialize.call(this, map, options); }, + // @method addHooks(): void + // Add listener hooks to this handler addHooks: function () { L.Draw.Feature.prototype.addHooks.call(this); if (this._map) { @@ -278,26 +581,25 @@ L.Draw.Polyline = L.Draw.Feature.extend({ }); } - if (!L.Browser.touch) { - this._map.on('mouseup', this._onMouseUp, this); // Necessary for 0.7 compatibility - } - this._mouseMarker - .on('mousedown', this._onMouseDown, this) .on('mouseout', this._onMouseOut, this) - .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter + .on('mousedown', this._onMouseDown, this) + .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility .addTo(this._map); this._map .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility .on('mousemove', this._onMouseMove, this) .on('zoomlevelschange', this._onZoomEnd, this) - .on('click', this._onTouch, this) + .on('touchstart', this._onTouch, this) .on('zoomend', this._onZoomEnd, this); + } }, + // @method removeHooks(): void + // Remove listener hooks from this handler. removeHooks: function () { L.Draw.Feature.prototype.removeHooks.call(this); @@ -329,9 +631,12 @@ L.Draw.Polyline = L.Draw.Feature.extend({ .off('mousemove', this._onMouseMove, this) .off('zoomlevelschange', this._onZoomEnd, this) .off('zoomend', this._onZoomEnd, this) + .off('touchstart', this._onTouch, this) .off('click', this._onTouch, this); }, + // @method deleteLastVertex(): void + // Remove the last vertex from the polyline, removes polyline from map if only one point exists. deleteLastVertex: function () { if (this._markers.length <= 1) { return; @@ -339,7 +644,10 @@ L.Draw.Polyline = L.Draw.Feature.extend({ var lastMarker = this._markers.pop(), poly = this._poly, - latlng = this._poly.spliceLatLngs(poly.getLatLngs().length - 1, 1)[0]; + // Replaces .spliceLatLngs() + latlngs = poly.getLatLngs(), + latlng = latlngs.splice(-1, 1)[0]; + this._poly.setLatLngs(latlngs); this._markerGroup.removeLayer(lastMarker); @@ -350,10 +658,12 @@ L.Draw.Polyline = L.Draw.Feature.extend({ this._vertexChanged(latlng, false); }, + // @method addVertex(): void + // Add a vertex to the end of the polyline addVertex: function (latlng) { var markersLength = this._markers.length; - - if (markersLength > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) { + // markersLength must be greater than or equal to 2 before intersections can occur + if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) { this._showErrorTooltip(); return; } @@ -372,6 +682,8 @@ L.Draw.Polyline = L.Draw.Feature.extend({ this._vertexChanged(latlng, true); }, + // @method completeShape(): void + // Closes the polyline between the first and last points completeShape: function () { if (this._markers.length <= 1) { return; @@ -386,7 +698,8 @@ L.Draw.Polyline = L.Draw.Feature.extend({ }, _finishShape: function () { - var intersects = this._poly.newLatLngIntersects(this._poly.getLatLngs()[this._poly.getLatLngs().length - 1]); + var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs(); + var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]); if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { this._showErrorTooltip(); @@ -400,8 +713,8 @@ L.Draw.Polyline = L.Draw.Feature.extend({ } }, - //Called to verify the shape is valid when the user tries to finish it - //Return false if the shape is not valid + // Called to verify the shape is valid when the user tries to finish it + // Return false if the shape is not valid _shapeIsValid: function () { return true; }, @@ -432,7 +745,7 @@ L.Draw.Polyline = L.Draw.Feature.extend({ }, _vertexChanged: function (latlng, added) { - this._map.fire('draw:drawvertex', { layers: this._markerGroup }); + this._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup}); this._updateFinishHandler(); this._updateRunningMeasure(latlng, added); @@ -443,29 +756,63 @@ L.Draw.Polyline = L.Draw.Feature.extend({ }, _onMouseDown: function (e) { - var originalEvent = e.originalEvent; - this._mouseDownOrigin = L.point(originalEvent.clientX, originalEvent.clientY); + if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) { + this._onMouseMove(e); + this._clickHandled = true; + this._disableNewMarkers(); + var originalEvent = e.originalEvent; + var clientX = originalEvent.clientX; + var clientY = originalEvent.clientY; + this._startPoint.call(this, clientX, clientY); + } + }, + + _startPoint: function (clientX, clientY) { + this._mouseDownOrigin = L.point(clientX, clientY); }, _onMouseUp: function (e) { + var originalEvent = e.originalEvent; + var clientX = originalEvent.clientX; + var clientY = originalEvent.clientY; + this._endPoint.call(this, clientX, clientY, e); + this._clickHandled = null; + }, + + _endPoint: function (clientX, clientY, e) { if (this._mouseDownOrigin) { - // We detect clicks within a certain tolerance, otherwise let it - // be interpreted as a drag by the map - var distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY) + var dragCheckDistance = L.point(clientX, clientY) .distanceTo(this._mouseDownOrigin); - if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) { + var lastPtDistance = this._calculateFinishDistance(e.latlng); + if (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) { + this.addVertex(e.latlng); + this._finishShape(); + } else if (lastPtDistance < 10 && L.Browser.touch) { + this._finishShape(); + } else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) { this.addVertex(e.latlng); } + this._enableNewMarkers(); // after a short pause, enable new markers } this._mouseDownOrigin = null; }, + // ontouch prevented by clickHandled flag because some browsers fire both click/touch events, + // causing unwanted behavior _onTouch: function (e) { - // #TODO: use touchstart and touchend vs using click(touch start & end). - if (L.Browser.touch) { // #TODO: get rid of this once leaflet fixes their click/touch. - this._onMouseDown(e); - this._onMouseUp(e); + var originalEvent = e.originalEvent; + var clientX; + var clientY; + if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) { + clientX = originalEvent.touches[0].clientX; + clientY = originalEvent.touches[0].clientY; + this._disableNewMarkers(); + this._touchHandled = true; + this._startPoint.call(this, clientX, clientY); + this._endPoint.call(this, clientX, clientY, e); + this._touchHandled = null; } + this._clickHandled = null; }, _onMouseOut: function () { @@ -474,6 +821,34 @@ L.Draw.Polyline = L.Draw.Feature.extend({ } }, + // calculate if we are currently within close enough distance + // of the closing point (first point for shapes, last point for lines) + // this is semi-ugly code but the only reliable way i found to get the job done + // note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work + _calculateFinishDistance: function (potentialLatLng) { + var lastPtDistance; + if (this._markers.length > 0) { + var finishMarker; + if (this.type === L.Draw.Polyline.TYPE) { + finishMarker = this._markers[this._markers.length - 1]; + } else if (this.type === L.Draw.Polygon.TYPE) { + finishMarker = this._markers[0]; + } else { + return Infinity; + } + var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()), + potentialMarker = new L.Marker(potentialLatLng, { + icon: this.options.icon, + zIndexOffset: this.options.zIndexOffset * 2 + }); + var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng()); + lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint); + } else { + lastPtDistance = Infinity; + } + return lastPtDistance; + }, + _updateFinishHandler: function () { var markerCount = this._markers.length; // The last marker should have a click handler to close the polyline @@ -580,7 +955,6 @@ L.Draw.Polyline = L.Draw.Feature.extend({ _getTooltipText: function () { var showLength = this.options.showLength, labelText, distanceStr; - if (this._markers.length === 0) { labelText = { text: L.drawLocal.draw.handlers.polyline.tooltip.start @@ -611,7 +985,13 @@ L.Draw.Polyline = L.Draw.Feature.extend({ this._measurementRunningTotal = 0; } else { previousMarkerIndex = markersLength - (added ? 2 : 1); - distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()); + + // Calculate the distance based on the version + if (L.GeometryUtil.isVersion07x()) { + distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); + } else { + distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); + } this._measurementRunningTotal += distance * (added ? 1 : -1); } @@ -622,10 +1002,14 @@ L.Draw.Polyline = L.Draw.Feature.extend({ previousLatLng = this._markers[this._markers.length - 1].getLatLng(), distance; - // calculate the distance from the last fixed point to the mouse position - distance = this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng); + // Calculate the distance from the last fixed point to the mouse position based on the version + if (L.GeometryUtil.isVersion07x()) { + distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0; + } else { + distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0; + } - return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet); + return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision); }, _showErrorTooltip: function () { @@ -634,11 +1018,11 @@ L.Draw.Polyline = L.Draw.Feature.extend({ // Update tooltip this._tooltip .showAsError() - .updateContent({ text: this.options.drawError.message }); + .updateContent({text: this.options.drawError.message}); // Update shape this._updateGuideColor(this.options.drawError.color); - this._poly.setStyle({ color: this.options.drawError.color }); + this._poly.setStyle({color: this.options.drawError.color}); // Hide the error after 2 seconds this._clearHideErrorTimeout(); @@ -657,7 +1041,7 @@ L.Draw.Polyline = L.Draw.Feature.extend({ // Revert shape this._updateGuideColor(this.options.shapeOptions.color); - this._poly.setStyle({ color: this.options.shapeOptions.color }); + this._poly.setStyle({color: this.options.shapeOptions.color}); }, _clearHideErrorTimeout: function () { @@ -667,6 +1051,19 @@ L.Draw.Polyline = L.Draw.Feature.extend({ } }, + // disable new markers temporarily; + // this is to prevent duplicated touch/click events in some browsers + _disableNewMarkers: function () { + this._disableMarkers = true; + }, + + // see _disableNewMarkers + _enableNewMarkers: function () { + setTimeout(function () { + this._disableMarkers = false; + }.bind(this), 50); + }, + _cleanUpShape: function () { if (this._markers.length > 1) { this._markers[this._markers.length - 1].off('click', this._finishShape, this); @@ -680,6 +1077,12 @@ L.Draw.Polyline = L.Draw.Feature.extend({ }); + +/** + * @class L.Draw.Polygon + * @aka Draw.Polygon + * @inherits L.Draw.Polyline + */ L.Draw.Polygon = L.Draw.Polyline.extend({ statics: { TYPE: 'polygon' @@ -689,9 +1092,10 @@ L.Draw.Polygon = L.Draw.Polyline.extend({ options: { showArea: false, + showLength: false, shapeOptions: { stroke: true, - color: '#f06eaa', + color: '#3388ff', weight: 4, opacity: 0.5, fill: true, @@ -699,9 +1103,17 @@ L.Draw.Polygon = L.Draw.Polyline.extend({ fillOpacity: 0.2, clickable: true }, - metric: true // Whether to use the metric measurement system or imperial + // Whether to use the metric measurement system (truthy) or not (falsy). + // Also defines the units to use for the metric system as an array of + // strings (e.g. `['ha', 'm']`). + metric: true, + feet: true, // When not metric, to use feet instead of yards for display. + nautic: false, // When not metric, not feet use nautic mile for display + // Defines the precision for each type of unit (e.g. {km: 2, ft: 0} + precision: {} }, + // @method initialize(): void initialize: function (map, options) { L.Draw.Polyline.prototype.initialize.call(this, map, options); @@ -734,6 +1146,7 @@ L.Draw.Polygon = L.Draw.Polyline.extend({ text = L.drawLocal.draw.handlers.polygon.tooltip.start; } else if (this._markers.length < 3) { text = L.drawLocal.draw.handlers.polygon.tooltip.cont; + subtext = this._getMeasurementString(); } else { text = L.drawLocal.draw.handlers.polygon.tooltip.end; subtext = this._getMeasurementString(); @@ -746,13 +1159,23 @@ L.Draw.Polygon = L.Draw.Polyline.extend({ }, _getMeasurementString: function () { - var area = this._area; + var area = this._area, + measurementString = ''; + - if (!area) { + if (!area && !this.options.showLength) { return null; } - return L.GeometryUtil.readableArea(area, this.options.metric); + if (this.options.showLength) { + measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this); + } + + if (area) { + measurementString += '
' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision); + } + + return measurementString; }, _shapeIsValid: function () { @@ -786,19 +1209,27 @@ L.Draw.Polygon = L.Draw.Polyline.extend({ }); -L.SimpleShape = {}; +L.SimpleShape = {}; +/** + * @class L.Draw.SimpleShape + * @aka Draw.SimpleShape + * @inherits L.Draw.Feature + */ L.Draw.SimpleShape = L.Draw.Feature.extend({ options: { repeatMode: false }, + // @method initialize(): void initialize: function (map, options) { this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end; L.Draw.Feature.prototype.initialize.call(this, map, options); }, + // @method addHooks(): void + // Add listener hooks to this handler. addHooks: function () { L.Draw.Feature.prototype.addHooks.call(this); if (this._map) { @@ -811,16 +1242,26 @@ L.Draw.SimpleShape = L.Draw.Feature.extend({ //TODO refactor: move cursor to styles this._container.style.cursor = 'crosshair'; - this._tooltip.updateContent({ text: this._initialLabelText }); + this._tooltip.updateContent({text: this._initialLabelText}); this._map .on('mousedown', this._onMouseDown, this) .on('mousemove', this._onMouseMove, this) .on('touchstart', this._onMouseDown, this) .on('touchmove', this._onMouseMove, this); + + // we should prevent default, otherwise default behavior (scrolling) will fire, + // and that will cause document.touchend to fire and will stop the drawing + // (circle, rectangle) in touch mode. + // (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means, + // handler can't event.preventDefault + // check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners + document.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false}); } }, + // @method removeHooks(): void + // Remove listener hooks from this handler. removeHooks: function () { L.Draw.Feature.prototype.removeHooks.call(this); if (this._map) { @@ -840,6 +1281,8 @@ L.Draw.SimpleShape = L.Draw.Feature.extend({ L.DomEvent.off(document, 'mouseup', this._onMouseUp, this); L.DomEvent.off(document, 'touchend', this._onMouseUp, this); + document.removeEventListener('touchstart', L.DomEvent.preventDefault); + // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return if (this._shape) { this._map.removeLayer(this._shape); @@ -887,6 +1330,13 @@ L.Draw.SimpleShape = L.Draw.Feature.extend({ } }); + + +/** + * @class L.Draw.Rectangle + * @aka Draw.Rectangle + * @inherits L.Draw.SimpleShape + */ L.Draw.Rectangle = L.Draw.SimpleShape.extend({ statics: { TYPE: 'rectangle' @@ -895,17 +1345,19 @@ L.Draw.Rectangle = L.Draw.SimpleShape.extend({ options: { shapeOptions: { stroke: true, - color: '#f06eaa', + color: '#3388ff', weight: 4, opacity: 0.5, fill: true, fillColor: null, //same as color by default fillOpacity: 0.2, + showArea: true, clickable: true }, metric: true // Whether to use the metric measurement system or imperial }, + // @method initialize(): void initialize: function (map, options) { // Save the type so super can fire, need to do this as cannot do this.TYPE :( this.type = L.Draw.Rectangle.TYPE; @@ -915,6 +1367,30 @@ L.Draw.Rectangle = L.Draw.SimpleShape.extend({ L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, + // @method disable(): void + disable: function () { + if (!this._enabled) { + return; + } + + this._isCurrentlyTwoClickDrawing = false; + L.Draw.SimpleShape.prototype.disable.call(this); + }, + + _onMouseUp: function (e) { + if (!this._shape && !this._isCurrentlyTwoClickDrawing) { + this._isCurrentlyTwoClickDrawing = true; + return; + } + + // Make sure closing click is on map + if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) { + return; + } + + L.Draw.SimpleShape.prototype._onMouseUp.call(this); + }, + _drawShape: function (latlng) { if (!this._shape) { this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions); @@ -932,12 +1408,13 @@ L.Draw.Rectangle = L.Draw.SimpleShape.extend({ _getTooltipText: function () { var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this), shape = this._shape, + showArea = this.options.showArea, latLngs, area, subtext; if (shape) { - latLngs = this._shape.getLatLngs(); + latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(); area = L.GeometryUtil.geodesicArea(latLngs); - subtext = L.GeometryUtil.readableArea(area, this.options.metric); + subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : ''; } return { @@ -947,74 +1424,20 @@ L.Draw.Rectangle = L.Draw.SimpleShape.extend({ } }); - -L.Draw.Circle = L.Draw.SimpleShape.extend({ - statics: { - TYPE: 'circle' - }, - - options: { - shapeOptions: { - stroke: true, - color: '#f06eaa', - weight: 4, - opacity: 0.5, - fill: true, - fillColor: null, //same as color by default - fillOpacity: 0.2, - clickable: true - }, - showRadius: true, - metric: true, // Whether to use the metric measurement system or imperial - feet: true // When not metric, use feet instead of yards for display - }, - - initialize: function (map, options) { - // Save the type so super can fire, need to do this as cannot do this.TYPE :( - this.type = L.Draw.Circle.TYPE; - - this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; - - L.Draw.SimpleShape.prototype.initialize.call(this, map, options); - }, - - _drawShape: function (latlng) { - if (!this._shape) { - this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(latlng), this.options.shapeOptions); - this._map.addLayer(this._shape); - } else { - this._shape.setRadius(this._startLatLng.distanceTo(latlng)); - } - }, - - _fireCreatedEvent: function () { - var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions); - L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle); - }, - - _onMouseMove: function (e) { - var latlng = e.latlng, - showRadius = this.options.showRadius, - useMetric = this.options.metric, - radius; - - this._tooltip.updatePosition(latlng); - if (this._isDrawing) { - this._drawShape(latlng); - - // Get the new radius (rounded to 1 dp) - radius = this._shape.getRadius().toFixed(1); - - this._tooltip.updateContent({ - text: this._endLabelText, - subtext: showRadius ? L.drawLocal.draw.handlers.circle.radius + ': ' + - L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet) : '' - }); - } +function _hasAncestor(el, cls) { + while ((el = el.parentElement) && !el.classList.contains(cls)) { + ; } -}); + return el; +} + +/** + * @class L.Draw.Marker + * @aka Draw.Marker + * @inherits L.Draw.Feature + */ L.Draw.Marker = L.Draw.Feature.extend({ statics: { TYPE: 'marker' @@ -1026,18 +1449,23 @@ L.Draw.Marker = L.Draw.Feature.extend({ zIndexOffset: 2000 // This should be > than the highest z-index any markers }, + // @method initialize(): void initialize: function (map, options) { // Save the type so super can fire, need to do this as cannot do this.TYPE :( this.type = L.Draw.Marker.TYPE; + this._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start; + L.Draw.Feature.prototype.initialize.call(this, map, options); }, + // @method addHooks(): void + // Add listener hooks to this handler. addHooks: function () { L.Draw.Feature.prototype.addHooks.call(this); if (this._map) { - this._tooltip.updateContent({ text: L.drawLocal.draw.handlers.marker.tooltip.start }); + this._tooltip.updateContent({text: this._initialLabelText}); // Same mouseMarker as in Draw.Polyline if (!this._mouseMarker) { @@ -1061,15 +1489,18 @@ L.Draw.Marker = L.Draw.Feature.extend({ } }, + // @method removeHooks(): void + // Remove listener hooks from this handler. removeHooks: function () { L.Draw.Feature.prototype.removeHooks.call(this); if (this._map) { + this._map + .off('click', this._onClick, this) + .off('click', this._onTouch, this); if (this._marker) { this._marker.off('click', this._onClick, this); this._map - .off('click', this._onClick, this) - .off('click', this._onTouch, this) .removeLayer(this._marker); delete this._marker; } @@ -1089,10 +1520,7 @@ L.Draw.Marker = L.Draw.Feature.extend({ this._mouseMarker.setLatLng(latlng); if (!this._marker) { - this._marker = new L.Marker(latlng, { - icon: this.options.icon, - zIndexOffset: this.options.zIndexOffset - }); + this._marker = this._createMarker(latlng); // Bind to both marker and map to make sure we get the click event. this._marker.on('click', this._onClick, this); this._map @@ -1105,6 +1533,13 @@ L.Draw.Marker = L.Draw.Feature.extend({ } }, + _createMarker: function (latlng) { + return new L.Marker(latlng, { + icon: this.options.icon, + zIndexOffset: this.options.zIndexOffset + }); + }, + _onClick: function () { this._fireCreatedEvent(); @@ -1121,20 +1556,159 @@ L.Draw.Marker = L.Draw.Feature.extend({ }, _fireCreatedEvent: function () { - var marker = new L.Marker.Touch(this._marker.getLatLng(), { icon: this.options.icon }); + var marker = new L.Marker.Touch(this._marker.getLatLng(), {icon: this.options.icon}); L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker); } }); + +/** + * @class L.Draw.CircleMarker + * @aka Draw.CircleMarker + * @inherits L.Draw.Marker + */ +L.Draw.CircleMarker = L.Draw.Marker.extend({ + statics: { + TYPE: 'circlemarker' + }, + + options: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: true, + fillColor: null, //same as color by default + fillOpacity: 0.2, + clickable: true, + zIndexOffset: 2000 // This should be > than the highest z-index any markers + }, + + // @method initialize(): void + initialize: function (map, options) { + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.CircleMarker.TYPE; + + this._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start; + + L.Draw.Feature.prototype.initialize.call(this, map, options); + }, + + + _fireCreatedEvent: function () { + var circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options); + L.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker); + }, + + _createMarker: function (latlng) { + return new L.CircleMarker(latlng, this.options); + } +}); + + + +/** + * @class L.Draw.Circle + * @aka Draw.Circle + * @inherits L.Draw.SimpleShape + */ +L.Draw.Circle = L.Draw.SimpleShape.extend({ + statics: { + TYPE: 'circle' + }, + + options: { + shapeOptions: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: true, + fillColor: null, //same as color by default + fillOpacity: 0.2, + clickable: true + }, + showRadius: true, + metric: true, // Whether to use the metric measurement system or imperial + feet: true, // When not metric, use feet instead of yards for display + nautic: false // When not metric, not feet use nautic mile for display + }, + + // @method initialize(): void + initialize: function (map, options) { + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.Circle.TYPE; + + this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; + + L.Draw.SimpleShape.prototype.initialize.call(this, map, options); + }, + + _drawShape: function (latlng) { + // Calculate the distance based on the version + if (L.GeometryUtil.isVersion07x()) { + var distance = this._startLatLng.distanceTo(latlng); + } else { + var distance = this._map.distance(this._startLatLng, latlng); + } + + if (!this._shape) { + this._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions); + this._map.addLayer(this._shape); + } else { + this._shape.setRadius(distance); + } + }, + + _fireCreatedEvent: function () { + var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions); + L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle); + }, + + _onMouseMove: function (e) { + var latlng = e.latlng, + showRadius = this.options.showRadius, + useMetric = this.options.metric, + radius; + + this._tooltip.updatePosition(latlng); + if (this._isDrawing) { + this._drawShape(latlng); + + // Get the new radius (rounded to 1 dp) + radius = this._shape.getRadius().toFixed(1); + + var subtext = ''; + if (showRadius) { + subtext = L.drawLocal.draw.handlers.circle.radius + ': ' + + L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic); + } + this._tooltip.updateContent({ + text: this._endLabelText, + subtext: subtext + }); + } + } +}); + + + L.Edit = L.Edit || {}; +/** + * @class L.Edit.Marker + * @aka Edit.Marker + */ L.Edit.Marker = L.Handler.extend({ + // @method initialize(): void initialize: function (marker, options) { this._marker = marker; L.setOptions(this, options); }, + // @method addHooks(): void + // Add listener hooks to this handler addHooks: function () { var marker = this._marker; @@ -1143,6 +1717,8 @@ L.Edit.Marker = L.Handler.extend({ this._toggleMarkerHighlight(); }, + // @method removeHooks(): void + // Remove listener hooks from this handler removeHooks: function () { var marker = this._marker; @@ -1154,13 +1730,12 @@ L.Edit.Marker = L.Handler.extend({ _onDragEnd: function (e) { var layer = e.target; layer.edited = true; - this._map.fire('draw:editmove', {layer: layer}); + this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer}); }, _toggleMarkerHighlight: function () { var icon = this._marker._icon; - // Don't do anything if this layer is a marker but doesn't have an icon. Markers // should usually have icons. If using Leaflet.draw with Leaflet.markercluster there // is a chance that a marker doesn't. @@ -1205,15 +1780,17 @@ L.Marker.addInitHook(function () { }); + L.Edit = L.Edit || {}; -/* - * L.Edit.Poly is an editing handler for polylines and polygons. +/** + * @class L.Edit.Polyline + * @aka L.Edit.Poly + * @aka Edit.Poly */ L.Edit.Poly = L.Handler.extend({ - options: {}, - - initialize: function (poly, options) { + // @method initialize(): void + initialize: function (poly) { this.latlngs = [poly._latlngs]; if (poly._holes) { @@ -1221,17 +1798,27 @@ L.Edit.Poly = L.Handler.extend({ } this._poly = poly; - L.setOptions(this, options); this._poly.on('revert-edited', this._updateLatLngs, this); }, + // Compatibility method to normalize Poly* objects + // between 0.7.x and 1.0+ + _defaultShape: function () { + if (!L.Polyline._flat) { + return this._poly._latlngs; + } + return L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0]; + }, + _eachVertexHandler: function (callback) { for (var i = 0; i < this._verticesHandlers.length; i++) { callback(this._verticesHandlers[i]); } }, + // @method addHooks(): void + // Add listener hooks to this handler addHooks: function () { this._initHandlers(); this._eachVertexHandler(function (handler) { @@ -1239,12 +1826,16 @@ L.Edit.Poly = L.Handler.extend({ }); }, + // @method removeHooks(): void + // Remove listener hooks from this handler removeHooks: function () { this._eachVertexHandler(function (handler) { handler.removeHooks(); }); }, + // @method updateMarkers(): void + // Fire an update for each vertex handler updateMarkers: function () { this._eachVertexHandler(function (handler) { handler.updateMarkers(); @@ -1254,7 +1845,7 @@ L.Edit.Poly = L.Handler.extend({ _initHandlers: function () { this._verticesHandlers = []; for (var i = 0; i < this.latlngs.length; i++) { - this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this.options)); + this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly)); } }, @@ -1267,6 +1858,10 @@ L.Edit.Poly = L.Handler.extend({ }); +/** + * @class L.Edit.PolyVerticesEdit + * @aka Edit.PolyVerticesEdit + */ L.Edit.PolyVerticesEdit = L.Handler.extend({ options: { icon: new L.DivIcon({ @@ -1285,6 +1880,7 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ }, + // @method intialize(): void initialize: function (poly, latlngs, options) { // if touch, switch to touch icon if (L.Browser.touch) { @@ -1301,11 +1897,39 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ L.setOptions(this, options); }, + // Compatibility method to normalize Poly* objects + // between 0.7.x and 1.0+ + _defaultShape: function () { + if (!L.Polyline._flat) { + return this._latlngs; + } + return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + + // @method addHooks(): void + // Add listener hooks to this handler. addHooks: function () { var poly = this._poly; + var path = poly._path; if (!(poly instanceof L.Polygon)) { poly.options.fill = false; + if (poly.options.editing) { + poly.options.editing.fill = false; + } + } + + if (path) { + if (poly.options.editing.className) { + if (poly.options.original.className) { + poly.options.original.className.split(' ').forEach(function (className) { + L.DomUtil.removeClass(path, className); + }); + } + poly.options.editing.className.split(' ').forEach(function (className) { + L.DomUtil.addClass(path, className); + }); + } } poly.setStyle(poly.options.editing); @@ -1321,8 +1945,24 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ } }, + // @method removeHooks(): void + // Remove listener hooks from this handler. removeHooks: function () { var poly = this._poly; + var path = poly._path; + + if (path) { + if (poly.options.editing.className) { + poly.options.editing.className.split(' ').forEach(function (className) { + L.DomUtil.removeClass(path, className); + }); + if (poly.options.original.className) { + poly.options.original.className.split(' ').forEach(function (className) { + L.DomUtil.addClass(path, className); + }); + } + } + } poly.setStyle(poly.options.original); @@ -1333,6 +1973,8 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ } }, + // @method updateMarkers(): void + // Clear markers and update their location updateMarkers: function () { this._markerGroup.clearLayers(); this._initMarkers(); @@ -1344,13 +1986,14 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ } this._markers = []; - var latlngs = this._latlngs, + var latlngs = this._defaultShape(), i, j, len, marker; for (i = 0, len = latlngs.length; i < len; i++) { marker = this._createMarker(latlngs[i], i); marker.on('click', this._onMarkerClick, this); + marker.on('contextmenu', this._onContextMenu, this); this._markers.push(marker); } @@ -1384,8 +2027,8 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ .on('drag', this._onMarkerDrag, this) .on('dragend', this._fireEdit, this) .on('touchmove', this._onTouchMove, this) - .on('MSPointerMove', this._onTouchMove, this) .on('touchend', this._fireEdit, this) + .on('MSPointerMove', this._onTouchMove, this) .on('MSPointerUp', this._fireEdit, this); this._markerGroup.addLayer(marker); @@ -1398,8 +2041,9 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ }, _spliceLatLngs: function () { - var removed = [].splice.apply(this._latlngs, arguments); - this._poly._convertLatLngs(this._latlngs, true); + var latlngs = this._defaultShape(); + var removed = [].splice.apply(latlngs, arguments); + this._poly._convertLatLngs(latlngs, true); this._poly.redraw(); return removed; }, @@ -1426,7 +2070,7 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ _fireEdit: function () { this._poly.edited = true; this._poly.fire('edit'); - this._poly._map.fire('draw:editvertex', { layers: this._markerGroup }); + this._poly._map.fire(L.Draw.Event.EDITVERTEX, {layers: this._markerGroup, poly: this._poly}); }, _onMarkerDrag: function (e) { @@ -1449,7 +2093,15 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ if (!poly.options.poly.allowIntersection && poly.intersects()) { var originalColor = poly.options.color; - poly.setStyle({ color: this.options.drawError.color }); + poly.setStyle({color: this.options.drawError.color}); + + // Manually trigger 'dragend' behavior on marker we are about to remove + // WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484 + if (L.version.indexOf('0.7') !== 0) { + marker.dragging._draggable._onUp(e); + } + this._onMarkerClick(e); // Remove violating marker + // FIXME: Reset the marker to it's original position (instead of remove) if (tooltip) { tooltip.updateContent({ @@ -1459,18 +2111,21 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ // Reset everything back to normal after a second setTimeout(function () { - poly.setStyle({ color: originalColor }); + poly.setStyle({color: originalColor}); if (tooltip) { tooltip.updateContent({ - text: L.drawLocal.edit.handlers.edit.tooltip.text, - subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext + text: L.drawLocal.edit.handlers.edit.tooltip.text, + subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext }); } }, 1000); - this._onMarkerClick(e); // Reset the marker to it's original position } } - + //refresh the bounds when draging + this._poly._bounds._southWest = L.latLng(Infinity, Infinity); + this._poly._bounds._northEast = L.latLng(-Infinity, -Infinity); + var latlngs = this._poly.getLatLngs(); + this._poly._convertLatLngs(latlngs, true); this._poly.redraw(); this._poly.fire('editdrag'); }, @@ -1481,7 +2136,7 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ marker = e.target; // If removing this point would create an invalid polyline/polygon don't remove - if (this._latlngs.length < minPoints) { + if (this._defaultShape().length < minPoints) { return; } @@ -1513,6 +2168,13 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ this._fireEdit(); }, + _onContextMenu: function (e) { + var marker = e.target; + var poly = this._poly; + this._poly._map.fire(L.Draw.Event.MARKERCONTEXT, {marker: marker, layers: this._markerGroup, poly: this._poly}); + L.DomEvent.stopPropagation; + }, + _onTouchMove: function (e) { var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]), @@ -1627,7 +2289,7 @@ L.Polyline.addInitHook(function () { if (L.Edit.Poly) { - this.editing = new L.Edit.Poly(this, this.options.poly); + this.editing = new L.Edit.Poly(this); if (this.options.editable) { this.editing.enable(); @@ -1647,8 +2309,13 @@ L.Polyline.addInitHook(function () { }); }); -L.Edit = L.Edit || {}; + +L.Edit = L.Edit || {}; +/** + * @class L.Edit.SimpleShape + * @aka Edit.SimpleShape + */ L.Edit.SimpleShape = L.Handler.extend({ options: { moveIcon: new L.DivIcon({ @@ -1669,6 +2336,7 @@ L.Edit.SimpleShape = L.Handler.extend({ }), }, + // @method intialize(): void initialize: function (shape, options) { // if touch, switch to touch icon if (L.Browser.touch) { @@ -1680,6 +2348,8 @@ L.Edit.SimpleShape = L.Handler.extend({ L.Util.setOptions(this, options); }, + // @method addHooks(): void + // Add listener hooks to this handler addHooks: function () { var shape = this._shape; if (this._shape._map) { @@ -1696,6 +2366,8 @@ L.Edit.SimpleShape = L.Handler.extend({ } }, + // @method removeHooks(): void + // Remove listener hooks from this handler removeHooks: function () { var shape = this._shape; @@ -1716,6 +2388,8 @@ L.Edit.SimpleShape = L.Handler.extend({ this._map = null; }, + // @method updateMarkers(): void + // Remove the edit markers from this layer updateMarkers: function () { this._markerGroup.clearLayers(); this._initMarkers(); @@ -1821,7 +2495,7 @@ L.Edit.SimpleShape = L.Handler.extend({ var corners = this._getCorners(), marker = e.target, currentCornerIndex = marker._cornerIndex; - + marker.setOpacity(0); // Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart() @@ -1829,7 +2503,7 @@ L.Edit.SimpleShape = L.Handler.extend({ this._oppositeCorner = corners[(currentCornerIndex + 2) % 4]; this._toggleCornerMarkers(0, currentCornerIndex); } - + this._shape.fire('editstart'); }, @@ -1845,7 +2519,7 @@ L.Edit.SimpleShape = L.Handler.extend({ } this._shape.redraw(); - + // prevent touchcancel in IOS // e.preventDefault(); return false; @@ -1868,8 +2542,13 @@ L.Edit.SimpleShape = L.Handler.extend({ }); -L.Edit = L.Edit || {}; +L.Edit = L.Edit || {}; +/** + * @class L.Edit.Rectangle + * @aka Edit.Rectangle + * @inherits L.Edit.SimpleShape + */ L.Edit.Rectangle = L.Edit.SimpleShape.extend({ _createMoveMarker: function () { var bounds = this._shape.getBounds(), @@ -1923,7 +2602,7 @@ L.Edit.Rectangle = L.Edit.SimpleShape.extend({ }, _move: function (newCenter) { - var latlngs = this._shape.getLatLngs(), + var latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(), bounds = this._shape.getBounds(), center = bounds.getCenter(), offset, newLatLngs = []; @@ -1939,7 +2618,7 @@ L.Edit.Rectangle = L.Edit.SimpleShape.extend({ // Reposition the resize markers this._repositionCornerMarkers(); - this._map.fire('draw:editmove', {layer: this._shape}); + this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape}); }, _resize: function (latlng) { @@ -1952,7 +2631,7 @@ L.Edit.Rectangle = L.Edit.SimpleShape.extend({ bounds = this._shape.getBounds(); this._moveMarker.setLatLng(bounds.getCenter()); - this._map.fire('draw:editresize', {layer: this._shape}); + this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape}); }, _getCorners: function () { @@ -1991,15 +2670,71 @@ L.Rectangle.addInitHook(function () { }); -L.Edit = L.Edit || {}; -L.Edit.Circle = L.Edit.SimpleShape.extend({ +L.Edit = L.Edit || {}; +/** + * @class L.Edit.CircleMarker + * @aka Edit.Circle + * @inherits L.Edit.SimpleShape + */ +L.Edit.CircleMarker = L.Edit.SimpleShape.extend({ _createMoveMarker: function () { var center = this._shape.getLatLng(); this._moveMarker = this._createMarker(center, this.options.moveIcon); }, + _createResizeMarker: function () { + // To avoid an undefined check in L.Edit.SimpleShape.removeHooks + this._resizeMarkers = []; + }, + + _move: function (latlng) { + if (this._resizeMarkers.length) { + var resizemarkerPoint = this._getResizeMarkerPoint(latlng); + // Move the resize marker + this._resizeMarkers[0].setLatLng(resizemarkerPoint); + } + + // Move the circle + this._shape.setLatLng(latlng); + + this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape}); + }, +}); + +L.CircleMarker.addInitHook(function () { + if (L.Edit.CircleMarker) { + this.editing = new L.Edit.CircleMarker(this); + + if (this.options.editable) { + this.editing.enable(); + } + } + + this.on('add', function () { + if (this.editing && this.editing.enabled()) { + this.editing.addHooks(); + } + }); + + this.on('remove', function () { + if (this.editing && this.editing.enabled()) { + this.editing.removeHooks(); + } + }); +}); + + + +L.Edit = L.Edit || {}; +/** + * @class L.Edit.Circle + * @aka Edit.Circle + * @inherits L.Edit.CircleMarker + */ +L.Edit.Circle = L.Edit.CircleMarker.extend({ + _createResizeMarker: function () { var center = this._shape.getLatLng(), resizemarkerPoint = this._getResizeMarkerPoint(center); @@ -2015,25 +2750,26 @@ L.Edit.Circle = L.Edit.SimpleShape.extend({ return this._map.unproject([point.x + delta, point.y - delta]); }, - _move: function (latlng) { - var resizemarkerPoint = this._getResizeMarkerPoint(latlng); - - // Move the resize marker - this._resizeMarkers[0].setLatLng(resizemarkerPoint); - - // Move the circle - this._shape.setLatLng(latlng); - - this._map.fire('draw:editmove', {layer: this._shape}); - }, - _resize: function (latlng) { - var moveLatLng = this._moveMarker.getLatLng(), + var moveLatLng = this._moveMarker.getLatLng(); + + // Calculate the radius based on the version + if (L.GeometryUtil.isVersion07x()) { radius = moveLatLng.distanceTo(latlng); + } else { + radius = this._map.distance(moveLatLng, latlng); + } + this._shape.setRadius(radius); + + this._map._editTooltip.updateContent({ + text: L.drawLocal.edit.handlers.edit.tooltip.subtext + '
' + L.drawLocal.edit.handlers.edit.tooltip.text, + subtext: L.drawLocal.draw.handlers.circle.radius + ': ' + + L.GeometryUtil.readableDistance(radius, true, this.options.feet, this.options.nautic) + }); this._shape.setRadius(radius); - this._map.fire('draw:editresize', {layer: this._shape}); + this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape}); } }); @@ -2059,18 +2795,28 @@ L.Circle.addInitHook(function () { }); }); + + L.Map.mergeOptions({ touchExtend: true }); +/** + * @class L.Map.TouchExtend + * @aka TouchExtend + */ L.Map.TouchExtend = L.Handler.extend({ + // @method initialize(): void + // Sets TouchExtend private accessor variables initialize: function (map) { this._map = map; this._container = map._container; this._pane = map._panes.overlayPane; }, + // @method addHooks(): void + // Adds dom listener events to the map container addHooks: function () { L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this); L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this); @@ -2087,6 +2833,8 @@ L.Map.TouchExtend = L.Handler.extend({ } }, + // @method removeHooks(): void + // Removes dom listener events from the map container removeHooks: function () { L.DomEvent.off(this._container, 'touchstart', this._onTouchStart); L.DomEvent.off(this._container, 'touchend', this._onTouchEnd); @@ -2115,9 +2863,9 @@ L.Map.TouchExtend = L.Handler.extend({ touchEvent = e.touches[0]; } else if (e.pointerType === 'touch') { touchEvent = e; - if (!this._filterClick(e)) { - return; - } + if (!this._filterClick(e)) { + return; + } } else { return; } @@ -2136,22 +2884,22 @@ L.Map.TouchExtend = L.Handler.extend({ }); }, - /** Borrowed from Leaflet and modified for bool ops **/ - _filterClick: function (e) { - var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), - elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); + /** Borrowed from Leaflet and modified for bool ops **/ + _filterClick: function (e) { + var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), + elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); - // are they closer together than 500ms yet more than 100ms? - // Android typically triggers them ~300ms apart while multiple listeners - // on the same event should be triggered far faster; - // or check if click is simulated on the element, and if it is, reject any non-simulated events - if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { - L.DomEvent.stop(e); - return false; - } - L.DomEvent._lastClick = timeStamp; - return true; - }, + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + L.DomEvent.stop(e); + return false; + } + L.DomEvent._lastClick = timeStamp; + return true; + }, _onTouchStart: function (e) { if (!this._map._loaded) { @@ -2231,13 +2979,28 @@ L.Map.TouchExtend = L.Handler.extend({ L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend); -// This isn't full Touch support. This is just to get makers to also support dom touch events after creation -// #TODO: find a better way of getting markers to support touch. + +/** + * @class L.Marker.Touch + * @aka Marker.Touch + * + * This isn't full Touch support. This is just to get markers to also support dom touch events after creation + * #TODO: find a better way of getting markers to support touch. + */ L.Marker.Touch = L.Marker.extend({ - // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js - // with the addition of the touch event son line 15. _initInteraction: function () { + if (!this.addInteractiveTarget) { + // 0.7.x support + return this._initInteractionLegacy(); + } + // TODO this may need be updated to re-add touch events for 1.0+ + return L.Marker.prototype._initInteraction.apply(this); + }, + + // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js + // with the addition of the touch events + _initInteractionLegacy: function () { if (!this.options.clickable) { return; @@ -2246,9 +3009,19 @@ L.Marker.Touch = L.Marker.extend({ // TODO refactor into something shared with Map/Path/etc. to DRY it up var icon = this._icon, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu', 'touchstart', 'touchend', 'touchmove']; + events = ['dblclick', + 'mousedown', + 'mouseover', + 'mouseout', + 'contextmenu', + 'touchstart', + 'touchend', + 'touchmove']; if (this._detectIE) { - events.concat(['MSPointerDown', 'MSPointerUp', 'MSPointerMove', 'MSPointerCancel']); + events.concat(['MSPointerDown', + 'MSPointerUp', + 'MSPointerMove', + 'MSPointerCancel']); } else { events.concat(['touchcancel']); } @@ -2269,6 +3042,7 @@ L.Marker.Touch = L.Marker.extend({ } } }, + _detectIE: function () { var ua = window.navigator.userAgent; @@ -2297,108 +3071,221 @@ L.Marker.Touch = L.Marker.extend({ }); -/* - * L.LatLngUtil contains different utility functions for LatLngs. - */ +/** + * @class L.LatLngUtil + * @aka LatLngUtil + */ L.LatLngUtil = { // Clones a LatLngs[], returns [][] + + // @method cloneLatLngs(LatLngs[]): L.LatLngs[] + // Clone the latLng point or points or nested points and return an array with those points cloneLatLngs: function (latlngs) { var clone = []; for (var i = 0, l = latlngs.length; i < l; i++) { - clone.push(this.cloneLatLng(latlngs[i])); + // Check for nested array (Polyline/Polygon) + if (Array.isArray(latlngs[i])) { + clone.push(L.LatLngUtil.cloneLatLngs(latlngs[i])); + } else { + clone.push(this.cloneLatLng(latlngs[i])); + } } return clone; }, + // @method cloneLatLng(LatLng): L.LatLng + // Clone the latLng and return a new LatLng object. cloneLatLng: function (latlng) { return L.latLng(latlng.lat, latlng.lng); } }; -L.GeometryUtil = L.extend(L.GeometryUtil || {}, { - // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270 - geodesicArea: function (latLngs) { - var pointsCount = latLngs.length, - area = 0.0, - d2r = L.LatLng.DEG_TO_RAD, - p1, p2; - - if (pointsCount > 2) { - for (var i = 0; i < pointsCount; i++) { - p1 = latLngs[i]; - p2 = latLngs[(i + 1) % pointsCount]; - area += ((p2.lng - p1.lng) * d2r) * + + +(function () { + + var defaultPrecision = { + km: 2, + ha: 2, + m: 0, + mi: 2, + ac: 2, + yd: 0, + ft: 0, + nm: 2 + }; + + + /** + * @class L.GeometryUtil + * @aka GeometryUtil + */ + L.GeometryUtil = L.extend(L.GeometryUtil || {}, { + // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270 + + // @method geodesicArea(): number + geodesicArea: function (latLngs) { + var pointsCount = latLngs.length, + area = 0.0, + d2r = Math.PI / 180, + p1, p2; + + if (pointsCount > 2) { + for (var i = 0; i < pointsCount; i++) { + p1 = latLngs[i]; + p2 = latLngs[(i + 1) % pointsCount]; + area += ((p2.lng - p1.lng) * d2r) * (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r)); + } + area = area * 6378137.0 * 6378137.0 / 2.0; } - area = area * 6378137.0 * 6378137.0 / 2.0; - } - - return Math.abs(area); - }, - readableArea: function (area, isMetric) { - var areaStr; + return Math.abs(area); + }, - if (isMetric) { - if (area >= 10000) { - areaStr = (area * 0.0001).toFixed(2) + ' ha'; - } else { - areaStr = area.toFixed(2) + ' m²'; + // @method formattedNumber(n, precision): string + // Returns n in specified number format (if defined) and precision + formattedNumber: function (n, precision) { + var formatted = parseFloat(n).toFixed(precision), + format = L.drawLocal.format && L.drawLocal.format.numeric, + delimiters = format && format.delimiters, + thousands = delimiters && delimiters.thousands, + decimal = delimiters && delimiters.decimal; + + if (thousands || decimal) { + var splitValue = formatted.split('.'); + formatted = thousands ? splitValue[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousands) : splitValue[0]; + decimal = decimal || '.'; + if (splitValue.length > 1) { + formatted = formatted + decimal + splitValue[1]; + } } - } else { - area /= 0.836127; // Square yards in 1 meter - if (area >= 3097600) { //3097600 square yards in 1 square mile - areaStr = (area / 3097600).toFixed(2) + ' mi²'; - } else if (area >= 4840) {//48040 square yards in 1 acre - areaStr = (area / 4840).toFixed(2) + ' acres'; + return formatted; + }, + + // @method readableArea(area, isMetric, precision): string + // Returns a readable area string in yards or metric. + // The value will be rounded as defined by the precision option object. + readableArea: function (area, isMetric, precision) { + var areaStr, + units, + precision = L.Util.extend({}, defaultPrecision, precision); + + if (isMetric) { + units = ['ha', 'm']; + type = typeof isMetric; + if (type === 'string') { + units = [isMetric]; + } else if (type !== 'boolean') { + units = isMetric; + } + + if (area >= 1000000 && units.indexOf('km') !== -1) { + areaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²'; + } else if (area >= 10000 && units.indexOf('ha') !== -1) { + areaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha'; + } else { + areaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²'; + } } else { - areaStr = Math.ceil(area) + ' yd²'; + area /= 0.836127; // Square yards in 1 meter + + if (area >= 3097600) { //3097600 square yards in 1 square mile + areaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²'; + } else if (area >= 4840) { //4840 square yards in 1 acre + areaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres'; + } else { + areaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²'; + } } - } - - return areaStr; - }, - readableDistance: function (distance, isMetric, useFeet) { - var distanceStr; + return areaStr; + }, - if (isMetric) { - // show metres when distance is < 1km, then show km - if (distance > 1000) { - distanceStr = (distance / 1000).toFixed(2) + ' km'; + // @method readableDistance(distance, units): string + // Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string + // + // @alternative + // @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string + // Converts metric distance to distance string. + // The value will be rounded as defined by the precision option object. + readableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) { + var distanceStr, + units, + precision = L.Util.extend({}, defaultPrecision, precision); + + if (isMetric) { + units = typeof isMetric == 'string' ? isMetric : 'metric'; + } else if (isFeet) { + units = 'feet'; + } else if (isNauticalMile) { + units = 'nauticalMile'; } else { - distanceStr = Math.ceil(distance) + ' m'; + units = 'yards'; } - } else { - distance *= 1.09361; - if (distance > 1760) { - distanceStr = (distance / 1760).toFixed(2) + ' miles'; - } else { - var suffix = ' yd'; - if (useFeet) { - distance = distance * 3; - suffix = ' ft'; - } - distanceStr = Math.ceil(distance) + suffix; + switch (units) { + case 'metric': + // show metres when distance is < 1km, then show km + if (distance > 1000) { + distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km'; + } else { + distanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m'; + } + break; + case 'feet': + distance *= 1.09361 * 3; + distanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft'; + + break; + case 'nauticalMile': + distance *= 0.53996; + distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm'; + break; + case 'yards': + default: + distance *= 1.09361; + + if (distance > 1760) { + distanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles'; + } else { + distanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd'; + } + break; } - } + return distanceStr; + }, - return distanceStr; - } -}); + // @method isVersion07x(): boolean + // Returns true if the Leaflet version is 0.7.x, false otherwise. + isVersion07x: function () { + var version = L.version.split('.'); + //If Version is == 0.7.* + return parseInt(version[0], 10) === 0 && parseInt(version[1], 10) === 7; + }, + }); + +})(); + +/** + * @class L.LineUtil + * @aka Util + * @aka L.Utils + */ L.Util.extend(L.LineUtil, { + + // @method segmentsIntersect(): boolean // Checks to see if two line segments intersect. Does not handle degenerate cases. // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) { - return this._checkCounterclockwise(p, p2, p3) !== - this._checkCounterclockwise(p1, p2, p3) && - this._checkCounterclockwise(p, p1, p2) !== - this._checkCounterclockwise(p, p1, p3); + return this._checkCounterclockwise(p, p2, p3) !== + this._checkCounterclockwise(p1, p2, p3) && + this._checkCounterclockwise(p, p1, p2) !== + this._checkCounterclockwise(p, p1, p3); }, // check to see if points are in counterclockwise order @@ -2407,11 +3294,19 @@ L.Util.extend(L.LineUtil, { } }); + + +/** + * @class L.Polyline + * @aka Polyline + */ L.Polyline.include({ + + // @method intersects(): boolean // Check to see if this polyline has any linesegments that intersect. // NOTE: does not support detecting intersection for degenerate cases. intersects: function () { - var points = this._originalPoints, + var points = this._getProjectedPoints(), len = points ? points.length : 0, i, p, p1; @@ -2432,6 +3327,7 @@ L.Polyline.include({ return false; }, + // @method newLatLngIntersects(): boolean // Check for intersection if new latlng was added to this polyline. // NOTE: does not support detecting intersection for degenerate cases. newLatLngIntersects: function (latlng, skipFirst) { @@ -2443,11 +3339,12 @@ L.Polyline.include({ return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst); }, + // @method newPointIntersects(): boolean // Check for intersection if new point was added to this polyline. // newPoint must be a layer point. // NOTE: does not support detecting intersection for degenerate cases. newPointIntersects: function (newPoint, skipFirst) { - var points = this._originalPoints, + var points = this._getProjectedPoints(), len = points ? points.length : 0, lastPoint = points ? points[len - 1] : null, // The previous previous line segment. Previous line segment doesn't need testing. @@ -2463,18 +3360,18 @@ L.Polyline.include({ // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these). // Cannot have intersection when < 3 line segments (< 4 points) _tooFewPointsForIntersection: function (extraPoints) { - var points = this._originalPoints, + var points = this._getProjectedPoints(), len = points ? points.length : 0; // Increment length by extraPoints if present len += extraPoints || 0; - return !this._originalPoints || len <= 3; + return !points || len <= 3; }, // Checks a line segment intersections with any line segments before its predecessor. // Don't need to check the predecessor as will never intersect. _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) { - var points = this._originalPoints, + var points = this._getProjectedPoints(), p2, p3; minIndex = minIndex || 0; @@ -2490,15 +3387,35 @@ L.Polyline.include({ } return false; + }, + + _getProjectedPoints: function () { + if (!this._defaultShape) { + return this._originalPoints; + } + var points = [], + _shape = this._defaultShape(); + + for (var i = 0; i < _shape.length; i++) { + points.push(this._map.latLngToLayerPoint(_shape[i])); + } + return points; } }); + +/** + * @class L.Polygon + * @aka Polygon + */ L.Polygon.include({ + + // @method intersects(): boolean // Checks a polygon for any intersecting line segments. Ignores holes. intersects: function () { var polylineIntersects, - points = this._originalPoints, + points = this._getProjectedPoints(), len, firstPoint, lastPoint, maxIndex; if (this._tooFewPointsForIntersection()) { @@ -2522,14 +3439,23 @@ L.Polygon.include({ } }); + + +/** + * @class L.Control.Draw + * @aka L.Draw + */ L.Control.Draw = L.Control.extend({ + // Options options: { position: 'topleft', draw: {}, edit: false }, + // @method initialize(): void + // Initializes draw control, toolbars from the options initialize: function (options) { if (L.version < '0.7') { throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/'); @@ -2562,6 +3488,8 @@ L.Control.Draw = L.Control.extend({ L.toolbar = this; //set global var for editing the toolbar }, + // @method onAdd(): container + // Adds the toolbar container to the map onAdd: function (map) { var container = L.DomUtil.create('div', 'leaflet-draw'), addedTopClass = false, @@ -2589,6 +3517,8 @@ L.Control.Draw = L.Control.extend({ return container; }, + // @method onRemove(): void + // Removes the toolbars from the map toolbar container onRemove: function () { for (var toolbarId in this._toolbars) { if (this._toolbars.hasOwnProperty(toolbarId)) { @@ -2597,6 +3527,8 @@ L.Control.Draw = L.Control.extend({ } }, + // @method setDrawingOptions(options): void + // Sets options to all toolbar instances setDrawingOptions: function (options) { for (var toolbarId in this._toolbars) { if (this._toolbars[toolbarId] instanceof L.DrawToolbar) { @@ -2629,27 +3561,91 @@ L.Map.addInitHook(function () { }); + +/** + * @class L.Draw.Toolbar + * @aka Toolbar + * + * The toolbar class of the API — it is used to create the ui + * This will be depreciated + * + * @example + * + * ```js + * var toolbar = L.Toolbar(); + * toolbar.addToolbar(map); + * ``` + * + * ### Disabling a toolbar + * + * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false. + * + * ```js + * var drawControl = new L.Control.Draw({ + * draw: false, + * edit: { + * featureGroup: editableLayers + * } + * }); + * ``` + * + * ### Disabling a toolbar item + * + * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and + * markers. It also turns off the ability to edit layers. + * + * ```js + * var drawControl = new L.Control.Draw({ + * draw: { + * polygon: false, + * marker: false + * }, + * edit: { + * featureGroup: editableLayers, + * edit: false + * } + * }); + * ``` + */ L.Toolbar = L.Class.extend({ - includes: [L.Mixin.Events], + // @section Methods for modifying the toolbar + // @method initialize(options): void + // Toolbar constructor initialize: function (options) { L.setOptions(this, options); this._modes = {}; this._actionButtons = []; this._activeMode = null; + + var version = L.version.split('.'); + //If Version is >= 1.2.0 + if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { + L.Toolbar.include(L.Evented.prototype); + } else { + L.Toolbar.include(L.Mixin.Events); + } }, + // @method enabled(): boolean + // Gets a true/false of whether the toolbar is enabled enabled: function () { return this._activeMode !== null; }, + // @method disable(): void + // Disables the toolbar disable: function () { - if (!this.enabled()) { return; } + if (!this.enabled()) { + return; + } this._activeMode.handler.disable(); }, + // @method addToolbar(map): L.DomUtil + // Adds the toolbar to the map and returns the toolbar dom element addToolbar: function (map) { var container = L.DomUtil.create('div', 'leaflet-draw-section'), buttonIndex = 0, @@ -2690,6 +3686,8 @@ L.Toolbar = L.Class.extend({ return container; }, + // @method removeToolbar(): void + // Removes the toolbar and drops the handler event listeners removeToolbar: function () { // Dispose each handler for (var handlerId in this._modes) { @@ -2747,36 +3745,57 @@ L.Toolbar = L.Class.extend({ .on('disabled', this._handlerDeactivated, this); }, + /* Detect iOS based on browser User Agent, based on: + * http://stackoverflow.com/a/9039885 */ + _detectIOS: function () { + var iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream); + return iOS; + }, + _createButton: function (options) { var link = L.DomUtil.create('a', options.className || '', options.container); + // Screen reader tag + var sr = L.DomUtil.create('span', 'sr-only', options.container); + link.href = '#'; + link.appendChild(sr); + + if (options.title) { + link.title = options.title; + sr.innerHTML = options.title; + } if (options.text) { link.innerHTML = options.text; + sr.innerHTML = options.text; } - if (options.title) { - link.title = options.title; - } + /* iOS does not use click events */ + var buttonEvent = this._detectIOS() ? 'touchstart' : 'click'; L.DomEvent .on(link, 'click', L.DomEvent.stopPropagation) .on(link, 'mousedown', L.DomEvent.stopPropagation) .on(link, 'dblclick', L.DomEvent.stopPropagation) + .on(link, 'touchstart', L.DomEvent.stopPropagation) .on(link, 'click', L.DomEvent.preventDefault) - .on(link, 'click', options.callback, options.context); + .on(link, buttonEvent, options.callback, options.context); return link; }, _disposeButton: function (button, callback) { + /* iOS does not use click events */ + var buttonEvent = this._detectIOS() ? 'touchstart' : 'click'; + L.DomEvent .off(button, 'click', L.DomEvent.stopPropagation) .off(button, 'mousedown', L.DomEvent.stopPropagation) .off(button, 'dblclick', L.DomEvent.stopPropagation) + .off(button, 'touchstart', L.DomEvent.stopPropagation) .off(button, 'click', L.DomEvent.preventDefault) - .off(button, 'click', callback); + .off(button, buttonEvent, callback); }, _handlerActivated: function (e) { @@ -2864,6 +3883,7 @@ L.Toolbar = L.Class.extend({ } this._actionsContainer.style.display = 'block'; + this._map.fire(L.Draw.Event.TOOLBAROPENED); }, _hideActionsToolbar: function () { @@ -2873,21 +3893,47 @@ L.Toolbar = L.Class.extend({ L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top'); L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); + this._map.fire(L.Draw.Event.TOOLBARCLOSED); } }); -L.Tooltip = L.Class.extend({ + +L.Draw = L.Draw || {}; +/** + * @class L.Draw.Tooltip + * @aka Tooltip + * + * The tooltip class — it is used to display the tooltip while drawing + * This will be depreciated + * + * @example + * + * ```js + * var tooltip = L.Draw.Tooltip(); + * ``` + * + */ +L.Draw.Tooltip = L.Class.extend({ + + // @section Methods for modifying draw state + + // @method initialize(map): void + // Tooltip constructor initialize: function (map) { this._map = map; this._popupPane = map._panes.popupPane; + this._visible = false; - this._container = map.options.drawControlTooltips ? L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null; + this._container = map.options.drawControlTooltips ? + L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null; this._singleLineLabel = false; this._map.on('mouseout', this._onMouseOut, this); }, + // @method dispose(): void + // Remove Tooltip DOM and unbind events dispose: function () { this._map.off('mouseout', this._onMouseOut, this); @@ -2897,6 +3943,8 @@ L.Tooltip = L.Class.extend({ } }, + // @method updateContent(labelText): this + // Changes the tooltip text to string in function call updateContent: function (labelText) { if (!this._container) { return this; @@ -2914,24 +3962,39 @@ L.Tooltip = L.Class.extend({ } this._container.innerHTML = - (labelText.subtext.length > 0 ? '' + labelText.subtext + '' + '
' : '') + + (labelText.subtext.length > 0 ? + '' + labelText.subtext + '' + '
' : '') + '' + labelText.text + ''; + if (!labelText.text && !labelText.subtext) { + this._visible = false; + this._container.style.visibility = 'hidden'; + } else { + this._visible = true; + this._container.style.visibility = 'inherit'; + } + return this; }, + // @method updatePosition(latlng): this + // Changes the location of the tooltip updatePosition: function (latlng) { var pos = this._map.latLngToLayerPoint(latlng), tooltipContainer = this._container; if (this._container) { - tooltipContainer.style.visibility = 'inherit'; + if (this._visible) { + tooltipContainer.style.visibility = 'inherit'; + } L.DomUtil.setPosition(tooltipContainer, pos); } return this; }, + // @method showAsError(): this + // Applies error class to tooltip showAsError: function () { if (this._container) { L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip'); @@ -2939,6 +4002,8 @@ L.Tooltip = L.Class.extend({ return this; }, + // @method removeError(): this + // Removes the error class from the tooltip removeError: function () { if (this._container) { L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip'); @@ -2954,6 +4019,11 @@ L.Tooltip = L.Class.extend({ }); + +/** + * @class L.DrawToolbar + * @aka Toolbar + */ L.DrawToolbar = L.Toolbar.extend({ statics: { @@ -2965,9 +4035,11 @@ L.DrawToolbar = L.Toolbar.extend({ polygon: {}, rectangle: {}, circle: {}, - marker: {} + marker: {}, + circlemarker: {} }, + // @method initialize(): void initialize: function (options) { // Ensure that the options are merged correctly since L.extend is only shallow for (var type in this.options) { @@ -2982,6 +4054,8 @@ L.DrawToolbar = L.Toolbar.extend({ L.Toolbar.prototype.initialize.call(this, options); }, + // @method getModeHandlers(): object + // Get mode handlers information getModeHandlers: function (map) { return [ { @@ -3008,11 +4082,17 @@ L.DrawToolbar = L.Toolbar.extend({ enabled: this.options.marker, handler: new L.Draw.Marker(map, this.options.marker), title: L.drawLocal.draw.toolbar.buttons.marker + }, + { + enabled: this.options.circlemarker, + handler: new L.Draw.CircleMarker(map, this.options.circlemarker), + title: L.drawLocal.draw.toolbar.buttons.circlemarker } ]; }, - // Get the actions part of the toolbar + // @method getActions(): object + // Get action information getActions: function (handler) { return [ { @@ -3038,6 +4118,8 @@ L.DrawToolbar = L.Toolbar.extend({ ]; }, + // @method setOptions(): void + // Sets the options to the toolbar setOptions: function (options) { L.setOptions(this, options); @@ -3050,10 +4132,14 @@ L.DrawToolbar = L.Toolbar.extend({ }); -/*L.Map.mergeOptions({ - editControl: true -});*/ +/*L.Map.mergeOptions({ + editControl: true + });*/ +/** + * @class L.EditToolbar + * @aka EditToolbar + */ L.EditToolbar = L.Toolbar.extend({ statics: { TYPE: 'edit' @@ -3077,6 +4163,7 @@ L.EditToolbar = L.Toolbar.extend({ featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */ }, + // @method intialize(): void initialize: function (options) { // Need to set this manually since null is an acceptable value here if (options.edit) { @@ -3100,6 +4187,8 @@ L.EditToolbar = L.Toolbar.extend({ this._selectedFeatureCount = 0; }, + // @method getModeHandlers(): object + // Get mode handlers information getModeHandlers: function (map) { var featureGroup = this.options.featureGroup; return [ @@ -3108,7 +4197,7 @@ L.EditToolbar = L.Toolbar.extend({ handler: new L.EditToolbar.Edit(map, { featureGroup: featureGroup, selectedPathOptions: this.options.edit.selectedPathOptions, - poly : this.options.poly + poly: this.options.poly }), title: L.drawLocal.edit.toolbar.buttons.edit }, @@ -3122,8 +4211,10 @@ L.EditToolbar = L.Toolbar.extend({ ]; }, - getActions: function () { - return [ + // @method getActions(): object + // Get actions information + getActions: function (handler) { + var actions = [ { title: L.drawLocal.edit.toolbar.actions.save.title, text: L.drawLocal.edit.toolbar.actions.save.text, @@ -3137,8 +4228,21 @@ L.EditToolbar = L.Toolbar.extend({ context: this } ]; + + if (handler.removeAllLayers) { + actions.push({ + title: L.drawLocal.edit.toolbar.actions.clearAll.title, + text: L.drawLocal.edit.toolbar.actions.clearAll.text, + callback: this._clearAllLayers, + context: this + }); + } + + return actions; }, + // @method addToolbar(map): L.DomUtil + // Adds the toolbar to the map addToolbar: function (map) { var container = L.Toolbar.prototype.addToolbar.call(this, map); @@ -3149,14 +4253,20 @@ L.EditToolbar = L.Toolbar.extend({ return container; }, + // @method removeToolbar(): void + // Removes the toolbar from the map removeToolbar: function () { this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this); L.Toolbar.prototype.removeToolbar.call(this); }, + // @method disable(): void + // Disables the toolbar disable: function () { - if (!this.enabled()) { return; } + if (!this.enabled()) { + return; + } this._activeMode.handler.revertLayers(); @@ -3170,6 +4280,13 @@ L.EditToolbar = L.Toolbar.extend({ } }, + _clearAllLayers: function () { + this._activeMode.handler.removeAllLayers(); + if (this._activeMode) { + this._activeMode.handler.disable(); + } + }, + _checkDisabled: function () { var featureGroup = this.options.featureGroup, hasLayers = featureGroup.getLayers().length !== 0, @@ -3187,8 +4304,8 @@ L.EditToolbar = L.Toolbar.extend({ button.setAttribute( 'title', hasLayers ? - L.drawLocal.edit.toolbar.buttons.edit - : L.drawLocal.edit.toolbar.buttons.editDisabled + L.drawLocal.edit.toolbar.buttons.edit + : L.drawLocal.edit.toolbar.buttons.editDisabled ); } @@ -3204,21 +4321,25 @@ L.EditToolbar = L.Toolbar.extend({ button.setAttribute( 'title', hasLayers ? - L.drawLocal.edit.toolbar.buttons.remove - : L.drawLocal.edit.toolbar.buttons.removeDisabled + L.drawLocal.edit.toolbar.buttons.remove + : L.drawLocal.edit.toolbar.buttons.removeDisabled ); } } }); + +/** + * @class L.EditToolbar.Edit + * @aka EditToolbar.Edit + */ L.EditToolbar.Edit = L.Handler.extend({ statics: { TYPE: 'edit' }, - includes: L.Mixin.Events, - + // @method intialize(): void initialize: function (map, options) { L.Handler.prototype.initialize.call(this, map); @@ -3235,17 +4356,27 @@ L.EditToolbar.Edit = L.Handler.extend({ // Save the type so super can fire, need to do this as cannot do this.TYPE :( this.type = L.EditToolbar.Edit.TYPE; + + var version = L.version.split('.'); + //If Version is >= 1.2.0 + if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { + L.EditToolbar.Edit.include(L.Evented.prototype); + } else { + L.EditToolbar.Edit.include(L.Mixin.Events); + } }, + // @method enable(): void + // Enable the edit toolbar enable: function () { if (this._enabled || !this._hasAvailableLayers()) { return; } this.fire('enabled', {handler: this.type}); - //this disable other handlers + //this disable other handlers - this._map.fire('draw:editstart', { handler: this.type }); - //allow drawLayer to be updated before beginning edition. + this._map.fire(L.Draw.Event.EDITSTART, {handler: this.type}); + //allow drawLayer to be updated before beginning edition. L.Handler.prototype.enable.call(this); this._featureGroup @@ -3253,16 +4384,22 @@ L.EditToolbar.Edit = L.Handler.extend({ .on('layerremove', this._disableLayerEdit, this); }, + // @method disable(): void + // Disable the edit toolbar disable: function () { - if (!this._enabled) { return; } + if (!this._enabled) { + return; + } this._featureGroup .off('layeradd', this._enableLayerEdit, this) .off('layerremove', this._disableLayerEdit, this); L.Handler.prototype.disable.call(this); - this._map.fire('draw:editstop', { handler: this.type }); + this._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type}); this.fire('disabled', {handler: this.type}); }, + // @method addHooks(): void + // Add listener hooks for this handler addHooks: function () { var map = this._map; @@ -3271,7 +4408,7 @@ L.EditToolbar.Edit = L.Handler.extend({ this._featureGroup.eachLayer(this._enableLayerEdit, this); - this._tooltip = new L.Tooltip(this._map); + this._tooltip = new L.Draw.Tooltip(this._map); this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.edit.tooltip.text, subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext @@ -3286,11 +4423,12 @@ L.EditToolbar.Edit = L.Handler.extend({ .on('mousemove', this._onMouseMove, this) .on('touchmove', this._onMouseMove, this) .on('MSPointerMove', this._onMouseMove, this) - .on('click', this._editStyle, this) - .on('draw:editvertex', this._updateTooltip, this); + .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); } }, + // @method removeHooks(): void + // Remove listener hooks for this handler removeHooks: function () { if (this._map) { // Clean up selected layers. @@ -3306,17 +4444,20 @@ L.EditToolbar.Edit = L.Handler.extend({ .off('mousemove', this._onMouseMove, this) .off('touchmove', this._onMouseMove, this) .off('MSPointerMove', this._onMouseMove, this) - .off('click', this._editStyle, this) - .off('draw:editvertex', this._updateTooltip, this); + .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); } }, + // @method revertLayers(): void + // Revert each layer's geometry changes revertLayers: function () { this._featureGroup.eachLayer(function (layer) { this._revertLayer(layer); }, this); }, + // @method save(): void + // Save the layer geometries save: function () { var editedLayers = new L.LayerGroup(); this._featureGroup.eachLayer(function (layer) { @@ -3325,7 +4466,7 @@ L.EditToolbar.Edit = L.Handler.extend({ layer.edited = false; } }); - this._map.fire('draw:edited', {layers: editedLayers}); + this._map.fire(L.Draw.Event.EDITED, {layers: editedLayers}); }, _backupLayer: function (layer) { @@ -3342,7 +4483,7 @@ L.EditToolbar.Edit = L.Handler.extend({ latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()), radius: layer.getRadius() }; - } else if (layer instanceof L.Marker) { // Marker + } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker this._uneditedLayerProps[id] = { latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()) }; @@ -3371,11 +4512,11 @@ L.EditToolbar.Edit = L.Handler.extend({ } else if (layer instanceof L.Circle) { layer.setLatLng(this._uneditedLayerProps[id].latlng); layer.setRadius(this._uneditedLayerProps[id].radius); - } else if (layer instanceof L.Marker) { // Marker + } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker layer.setLatLng(this._uneditedLayerProps[id].latlng); } - layer.fire('revert-edited', { layer: layer }); + layer.fire('revert-edited', {layer: layer}); } }, @@ -3406,7 +4547,10 @@ L.EditToolbar.Edit = L.Handler.extend({ } - if (this.isMarker) { + if (layer instanceof L.Marker) { + if (layer.editing) { + layer.editing.enable(); + } layer.dragging.enable(); layer .on('dragend', this._onMarkerDragEnd) @@ -3424,7 +4568,9 @@ L.EditToolbar.Edit = L.Handler.extend({ var layer = e.layer || e.target || e; layer.edited = false; - layer.editing.disable(); + if (layer.editing) { + layer.editing.disable(); + } delete layer.options.editing; delete layer.options.original; @@ -3457,6 +4603,12 @@ L.EditToolbar.Edit = L.Handler.extend({ this._tooltip.updatePosition(e.latlng); }, + _onMarkerDragEnd: function (e) { + var layer = e.target; + layer.edited = true; + this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer}); + }, + _onTouchMove: function (e) { var touchEvent = e.originalEvent.changedTouches[0], layerPoint = this._map.mouseEventToLayerPoint(touchEvent), @@ -3470,13 +4622,17 @@ L.EditToolbar.Edit = L.Handler.extend({ }); + +/** + * @class L.EditToolbar.Delete + * @aka EditToolbar.Delete + */ L.EditToolbar.Delete = L.Handler.extend({ statics: { TYPE: 'remove' // not delete as delete is reserved in js }, - includes: L.Mixin.Events, - + // @method intialize(): void initialize: function (map, options) { L.Handler.prototype.initialize.call(this, map); @@ -3491,15 +4647,26 @@ L.EditToolbar.Delete = L.Handler.extend({ // Save the type so super can fire, need to do this as cannot do this.TYPE :( this.type = L.EditToolbar.Delete.TYPE; + + var version = L.version.split('.'); + //If Version is >= 1.2.0 + if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { + L.EditToolbar.Delete.include(L.Evented.prototype); + } else { + L.EditToolbar.Delete.include(L.Mixin.Events); + } + }, + // @method enable(): void + // Enable the delete toolbar enable: function () { if (this._enabled || !this._hasAvailableLayers()) { return; } - this.fire('enabled', { handler: this.type}); + this.fire('enabled', {handler: this.type}); - this._map.fire('draw:deletestart', { handler: this.type }); + this._map.fire(L.Draw.Event.DELETESTART, {handler: this.type}); L.Handler.prototype.enable.call(this); @@ -3508,8 +4675,12 @@ L.EditToolbar.Delete = L.Handler.extend({ .on('layerremove', this._disableLayerDelete, this); }, + // @method disable(): void + // Disable the delete toolbar disable: function () { - if (!this._enabled) { return; } + if (!this._enabled) { + return; + } this._deletableLayers .off('layeradd', this._enableLayerDelete, this) @@ -3517,11 +4688,13 @@ L.EditToolbar.Delete = L.Handler.extend({ L.Handler.prototype.disable.call(this); - this._map.fire('draw:deletestop', { handler: this.type }); + this._map.fire(L.Draw.Event.DELETESTOP, {handler: this.type}); - this.fire('disabled', { handler: this.type}); + this.fire('disabled', {handler: this.type}); }, + // @method addHooks(): void + // Add listener hooks to this handler addHooks: function () { var map = this._map; @@ -3531,13 +4704,15 @@ L.EditToolbar.Delete = L.Handler.extend({ this._deletableLayers.eachLayer(this._enableLayerDelete, this); this._deletedLayers = new L.LayerGroup(); - this._tooltip = new L.Tooltip(this._map); - this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text }); + this._tooltip = new L.Draw.Tooltip(this._map); + this._tooltip.updateContent({text: L.drawLocal.edit.handlers.remove.tooltip.text}); this._map.on('mousemove', this._onMouseMove, this); } }, + // @method removeHooks(): void + // Remove listener hooks from this handler removeHooks: function () { if (this._map) { this._deletableLayers.eachLayer(this._disableLayerDelete, this); @@ -3550,16 +4725,30 @@ L.EditToolbar.Delete = L.Handler.extend({ } }, + // @method revertLayers(): void + // Revert the deleted layers back to their prior state. revertLayers: function () { // Iterate of the deleted layers and add them back into the featureGroup this._deletedLayers.eachLayer(function (layer) { this._deletableLayers.addLayer(layer); - layer.fire('revert-deleted', { layer: layer }); + layer.fire('revert-deleted', {layer: layer}); }, this); }, + // @method save(): void + // Save deleted layers save: function () { - this._map.fire('draw:deleted', { layers: this._deletedLayers }); + this._map.fire(L.Draw.Event.DELETED, {layers: this._deletedLayers}); + }, + + // @method removeAllLayers(): void + // Remove all delateable layers + removeAllLayers: function () { + // Iterate of the delateable layers and add remove them + this._deletableLayers.eachLayer(function (layer) { + this._removeLayer({layer: layer}); + }, this); + this.save(); }, _enableLayerDelete: function (e) { @@ -3597,4 +4786,6 @@ L.EditToolbar.Delete = L.Handler.extend({ }); -}(window, document)); \ No newline at end of file + +}(window, document)); +//# sourceMappingURL=leaflet.draw-src.map \ No newline at end of file diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.map b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.map new file mode 100644 index 00000000..d6815acd --- /dev/null +++ b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw-src.map @@ -0,0 +1 @@ +{"version":3,"sources":["src/Leaflet.draw.js","src/Leaflet.Draw.Event.js","src/draw/handler/Draw.Feature.js","src/draw/handler/Draw.Polyline.js","src/draw/handler/Draw.Polygon.js","src/draw/handler/Draw.SimpleShape.js","src/draw/handler/Draw.Rectangle.js","src/draw/handler/Draw.Marker.js","src/draw/handler/Draw.CircleMarker.js","src/draw/handler/Draw.Circle.js","src/edit/handler/Edit.Marker.js","src/edit/handler/Edit.Poly.js","src/edit/handler/Edit.SimpleShape.js","src/edit/handler/Edit.Rectangle.js","src/edit/handler/Edit.CircleMarker.js","src/edit/handler/Edit.Circle.js","src/ext/TouchEvents.js","src/ext/LatLngUtil.js","src/ext/GeometryUtil.js","src/ext/LineUtil.Intersect.js","src/ext/Polyline.Intersect.js","src/ext/Polygon.Intersect.js","src/Control.Draw.js","src/Toolbar.js","src/Tooltip.js","src/draw/DrawToolbar.js","src/edit/EditToolbar.js","src/edit/handler/EditToolbar.Edit.js","src/edit/handler/EditToolbar.Delete.js"],"names":[],"mappings":";;;;;;;yCAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC7LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AChLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACxGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AChlBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACjIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACvHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AClGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC/HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC1CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACnFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACpFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC/gBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACrOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC7HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACrDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACnEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AChRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC5BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACrKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACtBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC1GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACtHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC9UA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACtHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AClMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AClSA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sourcesContent":["/**\n * Leaflet.draw assumes that you have already included the Leaflet library.\n */\nL.drawVersion = \"0.4.14\";\n/**\n * @class L.Draw\n * @aka Draw\n *\n *\n * To add the draw toolbar set the option drawControl: true in the map options.\n *\n * @example\n * ```js\n * var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13);\n *\n * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {\n * attribution: '© OpenStreetMap contributors'\n * }).addTo(map);\n * ```\n *\n * ### Adding the edit toolbar\n * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map.\n *\n * ```js\n * var map = L.map('map').setView([51.505, -0.09], 13);\n *\n * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {\n * attribution: '© OpenStreetMap contributors'\n * }).addTo(map);\n *\n * // FeatureGroup is to store editable layers\n * var drawnItems = new L.FeatureGroup();\n * map.addLayer(drawnItems);\n *\n * var drawControl = new L.Control.Draw({\n * edit: {\n * featureGroup: drawnItems\n * }\n * });\n * map.addControl(drawControl);\n * ```\n *\n * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that\n * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon.\n * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon,\n * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a\n * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons).\n */\nL.Draw = {};\n\n/**\n * @class L.drawLocal\n * @aka L.drawLocal\n *\n * The core toolbar class of the API — it is used to create the toolbar ui\n *\n * @example\n * ```js\n * var modifiedDraw = L.drawLocal.extend({\n * draw: {\n * toolbar: {\n * buttons: {\n * polygon: 'Draw an awesome polygon'\n * }\n * }\n * }\n * });\n * ```\n *\n * The default state for the control is the draw toolbar just below the zoom control.\n * This will allow map users to draw vectors and markers.\n * **Please note the edit toolbar is not enabled by default.**\n */\nL.drawLocal = {\n\t// format: {\n\t// \tnumeric: {\n\t// \t\tdelimiters: {\n\t// \t\t\tthousands: ',',\n\t// \t\t\tdecimal: '.'\n\t// \t\t}\n\t// \t}\n\t// },\n\tdraw: {\n\t\ttoolbar: {\n\t\t\t// #TODO: this should be reorganized where actions are nested in actions\n\t\t\t// ex: actions.undo or actions.cancel\n\t\t\tactions: {\n\t\t\t\ttitle: 'Cancel drawing',\n\t\t\t\ttext: 'Cancel'\n\t\t\t},\n\t\t\tfinish: {\n\t\t\t\ttitle: 'Finish drawing',\n\t\t\t\ttext: 'Finish'\n\t\t\t},\n\t\t\tundo: {\n\t\t\t\ttitle: 'Delete last point drawn',\n\t\t\t\ttext: 'Delete last point'\n\t\t\t},\n\t\t\tbuttons: {\n\t\t\t\tpolyline: 'Draw a polyline',\n\t\t\t\tpolygon: 'Draw a polygon',\n\t\t\t\trectangle: 'Draw a rectangle',\n\t\t\t\tcircle: 'Draw a circle',\n\t\t\t\tmarker: 'Draw a marker',\n\t\t\t\tcirclemarker: 'Draw a circlemarker'\n\t\t\t}\n\t\t},\n\t\thandlers: {\n\t\t\tcircle: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click and drag to draw circle.'\n\t\t\t\t},\n\t\t\t\tradius: 'Radius'\n\t\t\t},\n\t\t\tcirclemarker: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click map to place circle marker.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tmarker: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click map to place marker.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tpolygon: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click to start drawing shape.',\n\t\t\t\t\tcont: 'Click to continue drawing shape.',\n\t\t\t\t\tend: 'Click first point to close this shape.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tpolyline: {\n\t\t\t\terror: 'Error: shape edges cannot cross!',\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click to start drawing line.',\n\t\t\t\t\tcont: 'Click to continue drawing line.',\n\t\t\t\t\tend: 'Click last point to finish line.'\n\t\t\t\t}\n\t\t\t},\n\t\t\trectangle: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click and drag to draw rectangle.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tsimpleshape: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tend: 'Release mouse to finish drawing.'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\tedit: {\n\t\ttoolbar: {\n\t\t\tactions: {\n\t\t\t\tsave: {\n\t\t\t\t\ttitle: 'Save changes',\n\t\t\t\t\ttext: 'Save'\n\t\t\t\t},\n\t\t\t\tcancel: {\n\t\t\t\t\ttitle: 'Cancel editing, discards all changes',\n\t\t\t\t\ttext: 'Cancel'\n\t\t\t\t},\n\t\t\t\tclearAll: {\n\t\t\t\t\ttitle: 'Clear all layers',\n\t\t\t\t\ttext: 'Clear All'\n\t\t\t\t}\n\t\t\t},\n\t\t\tbuttons: {\n\t\t\t\tedit: 'Edit layers',\n\t\t\t\teditDisabled: 'No layers to edit',\n\t\t\t\tremove: 'Delete layers',\n\t\t\t\tremoveDisabled: 'No layers to delete'\n\t\t\t}\n\t\t},\n\t\thandlers: {\n\t\t\tedit: {\n\t\t\t\ttooltip: {\n\t\t\t\t\ttext: 'Drag handles or markers to edit features.',\n\t\t\t\t\tsubtext: 'Click cancel to undo changes.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tremove: {\n\t\t\t\ttooltip: {\n\t\t\t\t\ttext: 'Click on a feature to remove.'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n","/**\n * ### Events\n * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different\n * actions users can initiate. The following events will be triggered on the map:\n *\n * @class L.Draw.Event\n * @aka Draw.Event\n *\n * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct.\n *\n * @example\n * ```js\n * map.on(L.Draw.Event.CREATED; function (e) {\n * var type = e.layerType,\n * layer = e.layer;\n *\n * if (type === 'marker') {\n * // Do marker specific actions\n * }\n *\n * // Do whatever else you need to. (save to db; add to map etc)\n * map.addLayer(layer);\n *});\n * ```\n */\nL.Draw.Event = {};\n/**\n * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String\n *\n * Layer that was just created.\n * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`\n * Triggered when a new vector or marker has been created.\n *\n */\nL.Draw.Event.CREATED = 'draw:created';\n\n/**\n * @event draw:edited: LayerGroup\n *\n * List of all layers just edited on the map.\n *\n *\n * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved.\n *\n * @example\n * ```js\n * map.on('draw:edited', function (e) {\n * var layers = e.layers;\n * layers.eachLayer(function (layer) {\n * //do whatever you want; most likely save back to db\n * });\n * });\n * ```\n */\nL.Draw.Event.EDITED = 'draw:edited';\n\n/**\n * @event draw:deleted: LayerGroup\n *\n * List of all layers just removed from the map.\n *\n * Triggered when layers have been removed (and saved) from the FeatureGroup.\n */\nL.Draw.Event.DELETED = 'draw:deleted';\n\n/**\n * @event draw:drawstart: String\n *\n * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker`\n *\n * Triggered when the user has chosen to draw a particular vector or marker.\n */\nL.Draw.Event.DRAWSTART = 'draw:drawstart';\n\n/**\n * @event draw:drawstop: String\n *\n * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`\n *\n * Triggered when the user has finished a particular vector or marker.\n */\n\nL.Draw.Event.DRAWSTOP = 'draw:drawstop';\n\n/**\n * @event draw:drawvertex: LayerGroup\n *\n * List of all layers just being added from the map.\n *\n * Triggered when a vertex is created on a polyline or polygon.\n */\nL.Draw.Event.DRAWVERTEX = 'draw:drawvertex';\n\n/**\n * @event draw:editstart: String\n *\n * The type of edit this is. One of: `edit`\n *\n * Triggered when the user starts edit mode by clicking the edit tool button.\n */\n\nL.Draw.Event.EDITSTART = 'draw:editstart';\n\n/**\n * @event draw:editmove: ILayer\n *\n * Layer that was just moved.\n *\n * Triggered as the user moves a rectangle; circle or marker.\n */\nL.Draw.Event.EDITMOVE = 'draw:editmove';\n\n/**\n * @event draw:editresize: ILayer\n *\n * Layer that was just moved.\n *\n * Triggered as the user resizes a rectangle or circle.\n */\nL.Draw.Event.EDITRESIZE = 'draw:editresize';\n\n/**\n * @event draw:editvertex: LayerGroup\n *\n * List of all layers just being edited from the map.\n *\n * Triggered when a vertex is edited on a polyline or polygon.\n */\nL.Draw.Event.EDITVERTEX = 'draw:editvertex';\n\n/**\n * @event draw:editstop: String\n *\n * The type of edit this is. One of: `edit`\n *\n * Triggered when the user has finshed editing (edit mode) and saves edits.\n */\nL.Draw.Event.EDITSTOP = 'draw:editstop';\n\n/**\n * @event draw:deletestart: String\n *\n * The type of edit this is. One of: `remove`\n *\n * Triggered when the user starts remove mode by clicking the remove tool button.\n */\nL.Draw.Event.DELETESTART = 'draw:deletestart';\n\n/**\n * @event draw:deletestop: String\n *\n * The type of edit this is. One of: `remove`\n *\n * Triggered when the user has finished removing shapes (remove mode) and saves.\n */\nL.Draw.Event.DELETESTOP = 'draw:deletestop';\n\n/**\n * @event draw:toolbaropened: String\n *\n * Triggered when a toolbar is opened.\n */\nL.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened';\n\n/**\n * @event draw:toolbarclosed: String\n *\n * Triggered when a toolbar is closed.\n */\nL.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed';\n\n/**\n * @event draw:markercontext: String\n *\n * Triggered when a marker is right clicked.\n */\nL.Draw.Event.MARKERCONTEXT = 'draw:markercontext';","L.Draw = L.Draw || {};\n\n/**\n * @class L.Draw.Feature\n * @aka Draw.Feature\n */\nL.Draw.Feature = L.Handler.extend({\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\tthis._map = map;\n\t\tthis._container = map._container;\n\t\tthis._overlayPane = map._panes.overlayPane;\n\t\tthis._popupPane = map._panes.popupPane;\n\n\t\t// Merge default shapeOptions options with custom shapeOptions\n\t\tif (options && options.shapeOptions) {\n\t\t\toptions.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);\n\t\t}\n\t\tL.setOptions(this, options);\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.Draw.Feature.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.Draw.Feature.include(L.Mixin.Events);\n\t\t}\n\t},\n\n\t// @method enable(): void\n\t// Enables this handler\n\tenable: function () {\n\t\tif (this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tL.Handler.prototype.enable.call(this);\n\n\t\tthis.fire('enabled', {handler: this.type});\n\n\t\tthis._map.fire(L.Draw.Event.DRAWSTART, {layerType: this.type});\n\t},\n\n\t// @method disable(): void\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tL.Handler.prototype.disable.call(this);\n\n\t\tthis._map.fire(L.Draw.Event.DRAWSTOP, {layerType: this.type});\n\n\t\tthis.fire('disabled', {handler: this.type});\n\t},\n\n\t// @method addHooks(): void\n\t// Add's event listeners to this handler\n\taddHooks: function () {\n\t\tvar map = this._map;\n\n\t\tif (map) {\n\t\t\tL.DomUtil.disableTextSelection();\n\n\t\t\tmap.getContainer().focus();\n\n\t\t\tthis._tooltip = new L.Draw.Tooltip(this._map);\n\n\t\t\tL.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Removes event listeners from this handler\n\tremoveHooks: function () {\n\t\tif (this._map) {\n\t\t\tL.DomUtil.enableTextSelection();\n\n\t\t\tthis._tooltip.dispose();\n\t\t\tthis._tooltip = null;\n\n\t\t\tL.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);\n\t\t}\n\t},\n\n\t// @method setOptions(object): void\n\t// Sets new options to this handler\n\tsetOptions: function (options) {\n\t\tL.setOptions(this, options);\n\t},\n\n\t_fireCreatedEvent: function (layer) {\n\t\tthis._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type});\n\t},\n\n\t// Cancel drawing when the escape key is pressed\n\t_cancelDrawing: function (e) {\n\t\tif (e.keyCode === 27) {\n\t\t\tthis._map.fire('draw:canceled', {layerType: this.type});\n\t\t\tthis.disable();\n\t\t}\n\t}\n});\n","/**\n * @class L.Draw.Polyline\n * @aka Draw.Polyline\n * @inherits L.Draw.Feature\n */\nL.Draw.Polyline = L.Draw.Feature.extend({\n\tstatics: {\n\t\tTYPE: 'polyline'\n\t},\n\n\tPoly: L.Polyline,\n\n\toptions: {\n\t\tallowIntersection: true,\n\t\trepeatMode: false,\n\t\tdrawError: {\n\t\t\tcolor: '#b00b00',\n\t\t\ttimeout: 2500\n\t\t},\n\t\ticon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon'\n\t\t}),\n\t\ttouchIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'\n\t\t}),\n\t\tguidelineDistance: 20,\n\t\tmaxGuideLineLength: 4000,\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: false,\n\t\t\tclickable: true\n\t\t},\n\t\tmetric: true, // Whether to use the metric measurement system or imperial\n\t\tfeet: true, // When not metric, to use feet instead of yards for display.\n\t\tnautic: false, // When not metric, not feet use nautic mile for display\n\t\tshowLength: true, // Whether to display distance in the tooltip\n\t\tzIndexOffset: 2000, // This should be > than the highest z-index any map layers\n\t\tfactor: 1, // To change distance calculation\n\t\tmaxPoints: 0 // Once this number of points are placed, finish shape\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// if touch, switch to touch icon\n\t\tif (L.Browser.touch) {\n\t\t\tthis.options.icon = this.options.touchIcon;\n\t\t}\n\n\t\t// Need to set this here to ensure the correct message is used.\n\t\tthis.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;\n\n\t\t// Merge default drawError options with custom options\n\t\tif (options && options.drawError) {\n\t\t\toptions.drawError = L.Util.extend({}, this.options.drawError, options.drawError);\n\t\t}\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Polyline.TYPE;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tL.Draw.Feature.prototype.addHooks.call(this);\n\t\tif (this._map) {\n\t\t\tthis._markers = [];\n\n\t\t\tthis._markerGroup = new L.LayerGroup();\n\t\t\tthis._map.addLayer(this._markerGroup);\n\n\t\t\tthis._poly = new L.Polyline([], this.options.shapeOptions);\n\n\t\t\tthis._tooltip.updateContent(this._getTooltipText());\n\n\t\t\t// Make a transparent marker that will used to catch click events. These click\n\t\t\t// events will create the vertices. We need to do this so we can ensure that\n\t\t\t// we can create vertices over other map layers (markers, vector layers). We\n\t\t\t// also do not want to trigger any click handlers of objects we are clicking on\n\t\t\t// while drawing.\n\t\t\tif (!this._mouseMarker) {\n\t\t\t\tthis._mouseMarker = L.marker(this._map.getCenter(), {\n\t\t\t\t\ticon: L.divIcon({\n\t\t\t\t\t\tclassName: 'leaflet-mouse-marker',\n\t\t\t\t\t\ticonAnchor: [20, 20],\n\t\t\t\t\t\ticonSize: [40, 40]\n\t\t\t\t\t}),\n\t\t\t\t\topacity: 0,\n\t\t\t\t\tzIndexOffset: this.options.zIndexOffset\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis._mouseMarker\n\t\t\t\t.on('mouseout', this._onMouseOut, this)\n\t\t\t\t.on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter\n\t\t\t\t.on('mousedown', this._onMouseDown, this)\n\t\t\t\t.on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility\n\t\t\t\t.addTo(this._map);\n\n\t\t\tthis._map\n\t\t\t\t.on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility\n\t\t\t\t.on('mousemove', this._onMouseMove, this)\n\t\t\t\t.on('zoomlevelschange', this._onZoomEnd, this)\n\t\t\t\t.on('touchstart', this._onTouch, this)\n\t\t\t\t.on('zoomend', this._onZoomEnd, this);\n\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tL.Draw.Feature.prototype.removeHooks.call(this);\n\n\t\tthis._clearHideErrorTimeout();\n\n\t\tthis._cleanUpShape();\n\n\t\t// remove markers from map\n\t\tthis._map.removeLayer(this._markerGroup);\n\t\tdelete this._markerGroup;\n\t\tdelete this._markers;\n\n\t\tthis._map.removeLayer(this._poly);\n\t\tdelete this._poly;\n\n\t\tthis._mouseMarker\n\t\t\t.off('mousedown', this._onMouseDown, this)\n\t\t\t.off('mouseout', this._onMouseOut, this)\n\t\t\t.off('mouseup', this._onMouseUp, this)\n\t\t\t.off('mousemove', this._onMouseMove, this);\n\t\tthis._map.removeLayer(this._mouseMarker);\n\t\tdelete this._mouseMarker;\n\n\t\t// clean up DOM\n\t\tthis._clearGuides();\n\n\t\tthis._map\n\t\t\t.off('mouseup', this._onMouseUp, this)\n\t\t\t.off('mousemove', this._onMouseMove, this)\n\t\t\t.off('zoomlevelschange', this._onZoomEnd, this)\n\t\t\t.off('zoomend', this._onZoomEnd, this)\n\t\t\t.off('touchstart', this._onTouch, this)\n\t\t\t.off('click', this._onTouch, this);\n\t},\n\n\t// @method deleteLastVertex(): void\n\t// Remove the last vertex from the polyline, removes polyline from map if only one point exists.\n\tdeleteLastVertex: function () {\n\t\tif (this._markers.length <= 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar lastMarker = this._markers.pop(),\n\t\t\tpoly = this._poly,\n\t\t\t// Replaces .spliceLatLngs()\n\t\t\tlatlngs = poly.getLatLngs(),\n\t\t\tlatlng = latlngs.splice(-1, 1)[0];\n\t\tthis._poly.setLatLngs(latlngs);\n\n\t\tthis._markerGroup.removeLayer(lastMarker);\n\n\t\tif (poly.getLatLngs().length < 2) {\n\t\t\tthis._map.removeLayer(poly);\n\t\t}\n\n\t\tthis._vertexChanged(latlng, false);\n\t},\n\n\t// @method addVertex(): void\n\t// Add a vertex to the end of the polyline\n\taddVertex: function (latlng) {\n\t\tvar markersLength = this._markers.length;\n\t\t// markersLength must be greater than or equal to 2 before intersections can occur\n\t\tif (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {\n\t\t\tthis._showErrorTooltip();\n\t\t\treturn;\n\t\t}\n\t\telse if (this._errorShown) {\n\t\t\tthis._hideErrorTooltip();\n\t\t}\n\n\t\tthis._markers.push(this._createMarker(latlng));\n\n\t\tthis._poly.addLatLng(latlng);\n\n\t\tif (this._poly.getLatLngs().length === 2) {\n\t\t\tthis._map.addLayer(this._poly);\n\t\t}\n\n\t\tthis._vertexChanged(latlng, true);\n\t},\n\n\t// @method completeShape(): void\n\t// Closes the polyline between the first and last points\n\tcompleteShape: function () {\n\t\tif (this._markers.length <= 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._fireCreatedEvent();\n\t\tthis.disable();\n\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t},\n\n\t_finishShape: function () {\n\t\tvar latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs();\n\t\tvar intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]);\n\n\t\tif ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {\n\t\t\tthis._showErrorTooltip();\n\t\t\treturn;\n\t\t}\n\n\t\tthis._fireCreatedEvent();\n\t\tthis.disable();\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t},\n\n\t// Called to verify the shape is valid when the user tries to finish it\n\t// Return false if the shape is not valid\n\t_shapeIsValid: function () {\n\t\treturn true;\n\t},\n\n\t_onZoomEnd: function () {\n\t\tif (this._markers !== null) {\n\t\t\tthis._updateGuide();\n\t\t}\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar newPos = this._map.mouseEventToLayerPoint(e.originalEvent);\n\t\tvar latlng = this._map.layerPointToLatLng(newPos);\n\n\t\t// Save latlng\n\t\t// should this be moved to _updateGuide() ?\n\t\tthis._currentLatLng = latlng;\n\n\t\tthis._updateTooltip(latlng);\n\n\t\t// Update the guide line\n\t\tthis._updateGuide(newPos);\n\n\t\t// Update the mouse marker position\n\t\tthis._mouseMarker.setLatLng(latlng);\n\n\t\tL.DomEvent.preventDefault(e.originalEvent);\n\t},\n\n\t_vertexChanged: function (latlng, added) {\n\t\tthis._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup});\n\t\tthis._updateFinishHandler();\n\n\t\tthis._updateRunningMeasure(latlng, added);\n\n\t\tthis._clearGuides();\n\n\t\tthis._updateTooltip();\n\t},\n\n\t_onMouseDown: function (e) {\n\t\tif (!this._clickHandled && !this._touchHandled && !this._disableMarkers) {\n\t\t\tthis._onMouseMove(e);\n\t\t\tthis._clickHandled = true;\n\t\t\tthis._disableNewMarkers();\n\t\t\tvar originalEvent = e.originalEvent;\n\t\t\tvar clientX = originalEvent.clientX;\n\t\t\tvar clientY = originalEvent.clientY;\n\t\t\tthis._startPoint.call(this, clientX, clientY);\n\t\t}\n\t},\n\n\t_startPoint: function (clientX, clientY) {\n\t\tthis._mouseDownOrigin = L.point(clientX, clientY);\n\t},\n\n\t_onMouseUp: function (e) {\n\t\tvar originalEvent = e.originalEvent;\n\t\tvar clientX = originalEvent.clientX;\n\t\tvar clientY = originalEvent.clientY;\n\t\tthis._endPoint.call(this, clientX, clientY, e);\n\t\tthis._clickHandled = null;\n\t},\n\n\t_endPoint: function (clientX, clientY, e) {\n\t\tif (this._mouseDownOrigin) {\n\t\t\tvar dragCheckDistance = L.point(clientX, clientY)\n\t\t\t\t.distanceTo(this._mouseDownOrigin);\n\t\t\tvar lastPtDistance = this._calculateFinishDistance(e.latlng);\n\t\t\tif (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) {\n\t\t\t\tthis.addVertex(e.latlng);\n\t\t\t\tthis._finishShape();\n\t\t\t} else if (lastPtDistance < 10 && L.Browser.touch) {\n\t\t\t\tthis._finishShape();\n\t\t\t} else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) {\n\t\t\t\tthis.addVertex(e.latlng);\n\t\t\t}\n\t\t\tthis._enableNewMarkers(); // after a short pause, enable new markers\n\t\t}\n\t\tthis._mouseDownOrigin = null;\n\t},\n\n\t// ontouch prevented by clickHandled flag because some browsers fire both click/touch events,\n\t// causing unwanted behavior\n\t_onTouch: function (e) {\n\t\tvar originalEvent = e.originalEvent;\n\t\tvar clientX;\n\t\tvar clientY;\n\t\tif (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) {\n\t\t\tclientX = originalEvent.touches[0].clientX;\n\t\t\tclientY = originalEvent.touches[0].clientY;\n\t\t\tthis._disableNewMarkers();\n\t\t\tthis._touchHandled = true;\n\t\t\tthis._startPoint.call(this, clientX, clientY);\n\t\t\tthis._endPoint.call(this, clientX, clientY, e);\n\t\t\tthis._touchHandled = null;\n\t\t}\n\t\tthis._clickHandled = null;\n\t},\n\n\t_onMouseOut: function () {\n\t\tif (this._tooltip) {\n\t\t\tthis._tooltip._onMouseOut.call(this._tooltip);\n\t\t}\n\t},\n\n\t// calculate if we are currently within close enough distance\n\t// of the closing point (first point for shapes, last point for lines)\n\t// this is semi-ugly code but the only reliable way i found to get the job done\n\t// note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work\n\t_calculateFinishDistance: function (potentialLatLng) {\n\t\tvar lastPtDistance;\n\t\tif (this._markers.length > 0) {\n\t\t\tvar finishMarker;\n\t\t\tif (this.type === L.Draw.Polyline.TYPE) {\n\t\t\t\tfinishMarker = this._markers[this._markers.length - 1];\n\t\t\t} else if (this.type === L.Draw.Polygon.TYPE) {\n\t\t\t\tfinishMarker = this._markers[0];\n\t\t\t} else {\n\t\t\t\treturn Infinity;\n\t\t\t}\n\t\t\tvar lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()),\n\t\t\t\tpotentialMarker = new L.Marker(potentialLatLng, {\n\t\t\t\t\ticon: this.options.icon,\n\t\t\t\t\tzIndexOffset: this.options.zIndexOffset * 2\n\t\t\t\t});\n\t\t\tvar potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng());\n\t\t\tlastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint);\n\t\t} else {\n\t\t\tlastPtDistance = Infinity;\n\t\t}\n\t\treturn lastPtDistance;\n\t},\n\n\t_updateFinishHandler: function () {\n\t\tvar markerCount = this._markers.length;\n\t\t// The last marker should have a click handler to close the polyline\n\t\tif (markerCount > 1) {\n\t\t\tthis._markers[markerCount - 1].on('click', this._finishShape, this);\n\t\t}\n\n\t\t// Remove the old marker click handler (as only the last point should close the polyline)\n\t\tif (markerCount > 2) {\n\t\t\tthis._markers[markerCount - 2].off('click', this._finishShape, this);\n\t\t}\n\t},\n\n\t_createMarker: function (latlng) {\n\t\tvar marker = new L.Marker(latlng, {\n\t\t\ticon: this.options.icon,\n\t\t\tzIndexOffset: this.options.zIndexOffset * 2\n\t\t});\n\n\t\tthis._markerGroup.addLayer(marker);\n\n\t\treturn marker;\n\t},\n\n\t_updateGuide: function (newPos) {\n\t\tvar markerCount = this._markers ? this._markers.length : 0;\n\n\t\tif (markerCount > 0) {\n\t\t\tnewPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);\n\n\t\t\t// draw the guide line\n\t\t\tthis._clearGuides();\n\t\t\tthis._drawGuide(\n\t\t\t\tthis._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),\n\t\t\t\tnewPos\n\t\t\t);\n\t\t}\n\t},\n\n\t_updateTooltip: function (latLng) {\n\t\tvar text = this._getTooltipText();\n\n\t\tif (latLng) {\n\t\t\tthis._tooltip.updatePosition(latLng);\n\t\t}\n\n\t\tif (!this._errorShown) {\n\t\t\tthis._tooltip.updateContent(text);\n\t\t}\n\t},\n\n\t_drawGuide: function (pointA, pointB) {\n\t\tvar length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),\n\t\t\tguidelineDistance = this.options.guidelineDistance,\n\t\t\tmaxGuideLineLength = this.options.maxGuideLineLength,\n\t\t\t// Only draw a guideline with a max length\n\t\t\ti = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,\n\t\t\tfraction,\n\t\t\tdashPoint,\n\t\t\tdash;\n\n\t\t//create the guides container if we haven't yet\n\t\tif (!this._guidesContainer) {\n\t\t\tthis._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);\n\t\t}\n\n\t\t//draw a dash every GuildeLineDistance\n\t\tfor (; i < length; i += this.options.guidelineDistance) {\n\t\t\t//work out fraction along line we are\n\t\t\tfraction = i / length;\n\n\t\t\t//calculate new x,y point\n\t\t\tdashPoint = {\n\t\t\t\tx: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),\n\t\t\t\ty: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))\n\t\t\t};\n\n\t\t\t//add guide dash to guide container\n\t\t\tdash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);\n\t\t\tdash.style.backgroundColor =\n\t\t\t\t!this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;\n\n\t\t\tL.DomUtil.setPosition(dash, dashPoint);\n\t\t}\n\t},\n\n\t_updateGuideColor: function (color) {\n\t\tif (this._guidesContainer) {\n\t\t\tfor (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {\n\t\t\t\tthis._guidesContainer.childNodes[i].style.backgroundColor = color;\n\t\t\t}\n\t\t}\n\t},\n\n\t// removes all child elements (guide dashes) from the guides container\n\t_clearGuides: function () {\n\t\tif (this._guidesContainer) {\n\t\t\twhile (this._guidesContainer.firstChild) {\n\t\t\t\tthis._guidesContainer.removeChild(this._guidesContainer.firstChild);\n\t\t\t}\n\t\t}\n\t},\n\n\t_getTooltipText: function () {\n\t\tvar showLength = this.options.showLength,\n\t\t\tlabelText, distanceStr;\n\t\tif (this._markers.length === 0) {\n\t\t\tlabelText = {\n\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.tooltip.start\n\t\t\t};\n\t\t} else {\n\t\t\tdistanceStr = showLength ? this._getMeasurementString() : '';\n\n\t\t\tif (this._markers.length === 1) {\n\t\t\t\tlabelText = {\n\t\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.tooltip.cont,\n\t\t\t\t\tsubtext: distanceStr\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tlabelText = {\n\t\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.tooltip.end,\n\t\t\t\t\tsubtext: distanceStr\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn labelText;\n\t},\n\n\t_updateRunningMeasure: function (latlng, added) {\n\t\tvar markersLength = this._markers.length,\n\t\t\tpreviousMarkerIndex, distance;\n\n\t\tif (this._markers.length === 1) {\n\t\t\tthis._measurementRunningTotal = 0;\n\t\t} else {\n\t\t\tpreviousMarkerIndex = markersLength - (added ? 2 : 1);\n\n\t\t\t// Calculate the distance based on the version\n\t\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\t\tdistance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);\n\t\t\t} else {\n\t\t\t\tdistance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);\n\t\t\t}\n\n\t\t\tthis._measurementRunningTotal += distance * (added ? 1 : -1);\n\t\t}\n\t},\n\n\t_getMeasurementString: function () {\n\t\tvar currentLatLng = this._currentLatLng,\n\t\t\tpreviousLatLng = this._markers[this._markers.length - 1].getLatLng(),\n\t\t\tdistance;\n\n\t\t// Calculate the distance from the last fixed point to the mouse position based on the version\n\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\tdistance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;\n\t\t} else {\n\t\t\tdistance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;\n\t\t}\n\n\t\treturn L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision);\n\t},\n\n\t_showErrorTooltip: function () {\n\t\tthis._errorShown = true;\n\n\t\t// Update tooltip\n\t\tthis._tooltip\n\t\t\t.showAsError()\n\t\t\t.updateContent({text: this.options.drawError.message});\n\n\t\t// Update shape\n\t\tthis._updateGuideColor(this.options.drawError.color);\n\t\tthis._poly.setStyle({color: this.options.drawError.color});\n\n\t\t// Hide the error after 2 seconds\n\t\tthis._clearHideErrorTimeout();\n\t\tthis._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);\n\t},\n\n\t_hideErrorTooltip: function () {\n\t\tthis._errorShown = false;\n\n\t\tthis._clearHideErrorTimeout();\n\n\t\t// Revert tooltip\n\t\tthis._tooltip\n\t\t\t.removeError()\n\t\t\t.updateContent(this._getTooltipText());\n\n\t\t// Revert shape\n\t\tthis._updateGuideColor(this.options.shapeOptions.color);\n\t\tthis._poly.setStyle({color: this.options.shapeOptions.color});\n\t},\n\n\t_clearHideErrorTimeout: function () {\n\t\tif (this._hideErrorTimeout) {\n\t\t\tclearTimeout(this._hideErrorTimeout);\n\t\t\tthis._hideErrorTimeout = null;\n\t\t}\n\t},\n\n\t// disable new markers temporarily;\n\t// this is to prevent duplicated touch/click events in some browsers\n\t_disableNewMarkers: function () {\n\t\tthis._disableMarkers = true;\n\t},\n\n\t// see _disableNewMarkers\n\t_enableNewMarkers: function () {\n\t\tsetTimeout(function () {\n\t\t\tthis._disableMarkers = false;\n\t\t}.bind(this), 50);\n\t},\n\n\t_cleanUpShape: function () {\n\t\tif (this._markers.length > 1) {\n\t\t\tthis._markers[this._markers.length - 1].off('click', this._finishShape, this);\n\t\t}\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);\n\t\tL.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);\n\t}\n});\n","/**\n * @class L.Draw.Polygon\n * @aka Draw.Polygon\n * @inherits L.Draw.Polyline\n */\nL.Draw.Polygon = L.Draw.Polyline.extend({\n\tstatics: {\n\t\tTYPE: 'polygon'\n\t},\n\n\tPoly: L.Polygon,\n\n\toptions: {\n\t\tshowArea: false,\n\t\tshowLength: false,\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: true,\n\t\t\tfillColor: null, //same as color by default\n\t\t\tfillOpacity: 0.2,\n\t\t\tclickable: true\n\t\t},\n\t\t// Whether to use the metric measurement system (truthy) or not (falsy).\n\t\t// Also defines the units to use for the metric system as an array of\n\t\t// strings (e.g. `['ha', 'm']`).\n\t\tmetric: true,\n\t\tfeet: true, // When not metric, to use feet instead of yards for display.\n\t\tnautic: false, // When not metric, not feet use nautic mile for display\n\t\t// Defines the precision for each type of unit (e.g. {km: 2, ft: 0}\n\t\tprecision: {}\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\tL.Draw.Polyline.prototype.initialize.call(this, map, options);\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Polygon.TYPE;\n\t},\n\n\t_updateFinishHandler: function () {\n\t\tvar markerCount = this._markers.length;\n\n\t\t// The first marker should have a click handler to close the polygon\n\t\tif (markerCount === 1) {\n\t\t\tthis._markers[0].on('click', this._finishShape, this);\n\t\t}\n\n\t\t// Add and update the double click handler\n\t\tif (markerCount > 2) {\n\t\t\tthis._markers[markerCount - 1].on('dblclick', this._finishShape, this);\n\t\t\t// Only need to remove handler if has been added before\n\t\t\tif (markerCount > 3) {\n\t\t\t\tthis._markers[markerCount - 2].off('dblclick', this._finishShape, this);\n\t\t\t}\n\t\t}\n\t},\n\n\t_getTooltipText: function () {\n\t\tvar text, subtext;\n\n\t\tif (this._markers.length === 0) {\n\t\t\ttext = L.drawLocal.draw.handlers.polygon.tooltip.start;\n\t\t} else if (this._markers.length < 3) {\n\t\t\ttext = L.drawLocal.draw.handlers.polygon.tooltip.cont;\n\t\t\tsubtext = this._getMeasurementString();\n\t\t} else {\n\t\t\ttext = L.drawLocal.draw.handlers.polygon.tooltip.end;\n\t\t\tsubtext = this._getMeasurementString();\n\t\t}\n\n\t\treturn {\n\t\t\ttext: text,\n\t\t\tsubtext: subtext\n\t\t};\n\t},\n\n\t_getMeasurementString: function () {\n\t\tvar area = this._area,\n\t\t\tmeasurementString = '';\n\n\n\t\tif (!area && !this.options.showLength) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (this.options.showLength) {\n\t\t\tmeasurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this);\n\t\t}\n\n\t\tif (area) {\n\t\t\tmeasurementString += '
' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision);\n\t\t}\n\n\t\treturn measurementString;\n\t},\n\n\t_shapeIsValid: function () {\n\t\treturn this._markers.length >= 3;\n\t},\n\n\t_vertexChanged: function (latlng, added) {\n\t\tvar latLngs;\n\n\t\t// Check to see if we should show the area\n\t\tif (!this.options.allowIntersection && this.options.showArea) {\n\t\t\tlatLngs = this._poly.getLatLngs();\n\n\t\t\tthis._area = L.GeometryUtil.geodesicArea(latLngs);\n\t\t}\n\n\t\tL.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);\n\t},\n\n\t_cleanUpShape: function () {\n\t\tvar markerCount = this._markers.length;\n\n\t\tif (markerCount > 0) {\n\t\t\tthis._markers[0].off('click', this._finishShape, this);\n\n\t\t\tif (markerCount > 2) {\n\t\t\t\tthis._markers[markerCount - 1].off('dblclick', this._finishShape, this);\n\t\t\t}\n\t\t}\n\t}\n});\n","L.SimpleShape = {};\n/**\n * @class L.Draw.SimpleShape\n * @aka Draw.SimpleShape\n * @inherits L.Draw.Feature\n */\nL.Draw.SimpleShape = L.Draw.Feature.extend({\n\toptions: {\n\t\trepeatMode: false\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\tthis._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler.\n\taddHooks: function () {\n\t\tL.Draw.Feature.prototype.addHooks.call(this);\n\t\tif (this._map) {\n\t\t\tthis._mapDraggable = this._map.dragging.enabled();\n\n\t\t\tif (this._mapDraggable) {\n\t\t\t\tthis._map.dragging.disable();\n\t\t\t}\n\n\t\t\t//TODO refactor: move cursor to styles\n\t\t\tthis._container.style.cursor = 'crosshair';\n\n\t\t\tthis._tooltip.updateContent({text: this._initialLabelText});\n\n\t\t\tthis._map\n\t\t\t\t.on('mousedown', this._onMouseDown, this)\n\t\t\t\t.on('mousemove', this._onMouseMove, this)\n\t\t\t\t.on('touchstart', this._onMouseDown, this)\n\t\t\t\t.on('touchmove', this._onMouseMove, this);\n\n\t\t\t// we should prevent default, otherwise default behavior (scrolling) will fire,\n\t\t\t// and that will cause document.touchend to fire and will stop the drawing\n\t\t\t// (circle, rectangle) in touch mode.\n\t\t\t// (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means,\n\t\t\t// handler can't event.preventDefault\n\t\t\t// check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners\n\t\t\tdocument.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false});\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tL.Draw.Feature.prototype.removeHooks.call(this);\n\t\tif (this._map) {\n\t\t\tif (this._mapDraggable) {\n\t\t\t\tthis._map.dragging.enable();\n\t\t\t}\n\n\t\t\t//TODO refactor: move cursor to styles\n\t\t\tthis._container.style.cursor = '';\n\n\t\t\tthis._map\n\t\t\t\t.off('mousedown', this._onMouseDown, this)\n\t\t\t\t.off('mousemove', this._onMouseMove, this)\n\t\t\t\t.off('touchstart', this._onMouseDown, this)\n\t\t\t\t.off('touchmove', this._onMouseMove, this);\n\n\t\t\tL.DomEvent.off(document, 'mouseup', this._onMouseUp, this);\n\t\t\tL.DomEvent.off(document, 'touchend', this._onMouseUp, this);\n\n\t\t\tdocument.removeEventListener('touchstart', L.DomEvent.preventDefault);\n\n\t\t\t// If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return\n\t\t\tif (this._shape) {\n\t\t\t\tthis._map.removeLayer(this._shape);\n\t\t\t\tdelete this._shape;\n\t\t\t}\n\t\t}\n\t\tthis._isDrawing = false;\n\t},\n\n\t_getTooltipText: function () {\n\t\treturn {\n\t\t\ttext: this._endLabelText\n\t\t};\n\t},\n\n\t_onMouseDown: function (e) {\n\t\tthis._isDrawing = true;\n\t\tthis._startLatLng = e.latlng;\n\n\t\tL.DomEvent\n\t\t\t.on(document, 'mouseup', this._onMouseUp, this)\n\t\t\t.on(document, 'touchend', this._onMouseUp, this)\n\t\t\t.preventDefault(e.originalEvent);\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar latlng = e.latlng;\n\n\t\tthis._tooltip.updatePosition(latlng);\n\t\tif (this._isDrawing) {\n\t\t\tthis._tooltip.updateContent(this._getTooltipText());\n\t\t\tthis._drawShape(latlng);\n\t\t}\n\t},\n\n\t_onMouseUp: function () {\n\t\tif (this._shape) {\n\t\t\tthis._fireCreatedEvent();\n\t\t}\n\n\t\tthis.disable();\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t}\n});\n","/**\n * @class L.Draw.Rectangle\n * @aka Draw.Rectangle\n * @inherits L.Draw.SimpleShape\n */\nL.Draw.Rectangle = L.Draw.SimpleShape.extend({\n\tstatics: {\n\t\tTYPE: 'rectangle'\n\t},\n\n\toptions: {\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: true,\n\t\t\tfillColor: null, //same as color by default\n\t\t\tfillOpacity: 0.2,\n\t\t\tshowArea: true,\n\t\t\tclickable: true\n\t\t},\n\t\tmetric: true // Whether to use the metric measurement system or imperial\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Rectangle.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;\n\n\t\tL.Draw.SimpleShape.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method disable(): void\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._isCurrentlyTwoClickDrawing = false;\n\t\tL.Draw.SimpleShape.prototype.disable.call(this);\n\t},\n\n\t_onMouseUp: function (e) {\n\t\tif (!this._shape && !this._isCurrentlyTwoClickDrawing) {\n\t\t\tthis._isCurrentlyTwoClickDrawing = true;\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure closing click is on map\n\t\tif (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) {\n\t\t\treturn;\n\t\t}\n\n\t\tL.Draw.SimpleShape.prototype._onMouseUp.call(this);\n\t},\n\n\t_drawShape: function (latlng) {\n\t\tif (!this._shape) {\n\t\t\tthis._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);\n\t\t\tthis._map.addLayer(this._shape);\n\t\t} else {\n\t\t\tthis._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));\n\t\t}\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);\n\t\tL.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);\n\t},\n\n\t_getTooltipText: function () {\n\t\tvar tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),\n\t\t\tshape = this._shape,\n\t\t\tshowArea = this.options.showArea,\n\t\t\tlatLngs, area, subtext;\n\n\t\tif (shape) {\n\t\t\tlatLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();\n\t\t\tarea = L.GeometryUtil.geodesicArea(latLngs);\n\t\t\tsubtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : '';\n\t\t}\n\n\t\treturn {\n\t\t\ttext: tooltipText.text,\n\t\t\tsubtext: subtext\n\t\t};\n\t}\n});\n\nfunction _hasAncestor(el, cls) {\n\twhile ((el = el.parentElement) && !el.classList.contains(cls)) {\n\t\t;\n\t}\n\treturn el;\n}\n","/**\n * @class L.Draw.Marker\n * @aka Draw.Marker\n * @inherits L.Draw.Feature\n */\nL.Draw.Marker = L.Draw.Feature.extend({\n\tstatics: {\n\t\tTYPE: 'marker'\n\t},\n\n\toptions: {\n\t\ticon: new L.Icon.Default(),\n\t\trepeatMode: false,\n\t\tzIndexOffset: 2000 // This should be > than the highest z-index any markers\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Marker.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler.\n\taddHooks: function () {\n\t\tL.Draw.Feature.prototype.addHooks.call(this);\n\n\t\tif (this._map) {\n\t\t\tthis._tooltip.updateContent({text: this._initialLabelText});\n\n\t\t\t// Same mouseMarker as in Draw.Polyline\n\t\t\tif (!this._mouseMarker) {\n\t\t\t\tthis._mouseMarker = L.marker(this._map.getCenter(), {\n\t\t\t\t\ticon: L.divIcon({\n\t\t\t\t\t\tclassName: 'leaflet-mouse-marker',\n\t\t\t\t\t\ticonAnchor: [20, 20],\n\t\t\t\t\t\ticonSize: [40, 40]\n\t\t\t\t\t}),\n\t\t\t\t\topacity: 0,\n\t\t\t\t\tzIndexOffset: this.options.zIndexOffset\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis._mouseMarker\n\t\t\t\t.on('click', this._onClick, this)\n\t\t\t\t.addTo(this._map);\n\n\t\t\tthis._map.on('mousemove', this._onMouseMove, this);\n\t\t\tthis._map.on('click', this._onTouch, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tL.Draw.Feature.prototype.removeHooks.call(this);\n\n\t\tif (this._map) {\n\t\t\tthis._map\n\t\t\t\t.off('click', this._onClick, this)\n\t\t\t\t.off('click', this._onTouch, this);\n\t\t\tif (this._marker) {\n\t\t\t\tthis._marker.off('click', this._onClick, this);\n\t\t\t\tthis._map\n\t\t\t\t\t.removeLayer(this._marker);\n\t\t\t\tdelete this._marker;\n\t\t\t}\n\n\t\t\tthis._mouseMarker.off('click', this._onClick, this);\n\t\t\tthis._map.removeLayer(this._mouseMarker);\n\t\t\tdelete this._mouseMarker;\n\n\t\t\tthis._map.off('mousemove', this._onMouseMove, this);\n\t\t}\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar latlng = e.latlng;\n\n\t\tthis._tooltip.updatePosition(latlng);\n\t\tthis._mouseMarker.setLatLng(latlng);\n\n\t\tif (!this._marker) {\n\t\t\tthis._marker = this._createMarker(latlng);\n\t\t\t// Bind to both marker and map to make sure we get the click event.\n\t\t\tthis._marker.on('click', this._onClick, this);\n\t\t\tthis._map\n\t\t\t\t.on('click', this._onClick, this)\n\t\t\t\t.addLayer(this._marker);\n\t\t}\n\t\telse {\n\t\t\tlatlng = this._mouseMarker.getLatLng();\n\t\t\tthis._marker.setLatLng(latlng);\n\t\t}\n\t},\n\n\t_createMarker: function (latlng) {\n\t\treturn new L.Marker(latlng, {\n\t\t\ticon: this.options.icon,\n\t\t\tzIndexOffset: this.options.zIndexOffset\n\t\t});\n\t},\n\n\t_onClick: function () {\n\t\tthis._fireCreatedEvent();\n\n\t\tthis.disable();\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t},\n\n\t_onTouch: function (e) {\n\t\t// called on click & tap, only really does any thing on tap\n\t\tthis._onMouseMove(e); // creates & places marker\n\t\tthis._onClick(); // permanently places marker & ends interaction\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar marker = new L.Marker.Touch(this._marker.getLatLng(), {icon: this.options.icon});\n\t\tL.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);\n\t}\n});\n","/**\n * @class L.Draw.CircleMarker\n * @aka Draw.CircleMarker\n * @inherits L.Draw.Marker\n */\nL.Draw.CircleMarker = L.Draw.Marker.extend({\n\tstatics: {\n\t\tTYPE: 'circlemarker'\n\t},\n\n\toptions: {\n\t\tstroke: true,\n\t\tcolor: '#3388ff',\n\t\tweight: 4,\n\t\topacity: 0.5,\n\t\tfill: true,\n\t\tfillColor: null, //same as color by default\n\t\tfillOpacity: 0.2,\n\t\tclickable: true,\n\t\tzIndexOffset: 2000 // This should be > than the highest z-index any markers\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.CircleMarker.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\n\t_fireCreatedEvent: function () {\n\t\tvar circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options);\n\t\tL.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker);\n\t},\n\n\t_createMarker: function (latlng) {\n\t\treturn new L.CircleMarker(latlng, this.options);\n\t}\n});\n","/**\n * @class L.Draw.Circle\n * @aka Draw.Circle\n * @inherits L.Draw.SimpleShape\n */\nL.Draw.Circle = L.Draw.SimpleShape.extend({\n\tstatics: {\n\t\tTYPE: 'circle'\n\t},\n\n\toptions: {\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: true,\n\t\t\tfillColor: null, //same as color by default\n\t\t\tfillOpacity: 0.2,\n\t\t\tclickable: true\n\t\t},\n\t\tshowRadius: true,\n\t\tmetric: true, // Whether to use the metric measurement system or imperial\n\t\tfeet: true, // When not metric, use feet instead of yards for display\n\t\tnautic: false // When not metric, not feet use nautic mile for display\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Circle.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;\n\n\t\tL.Draw.SimpleShape.prototype.initialize.call(this, map, options);\n\t},\n\n\t_drawShape: function (latlng) {\n\t\t// Calculate the distance based on the version\n\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\tvar distance = this._startLatLng.distanceTo(latlng);\n\t\t} else {\n\t\t\tvar distance = this._map.distance(this._startLatLng, latlng);\n\t\t}\n\n\t\tif (!this._shape) {\n\t\t\tthis._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions);\n\t\t\tthis._map.addLayer(this._shape);\n\t\t} else {\n\t\t\tthis._shape.setRadius(distance);\n\t\t}\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);\n\t\tL.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar latlng = e.latlng,\n\t\t\tshowRadius = this.options.showRadius,\n\t\t\tuseMetric = this.options.metric,\n\t\t\tradius;\n\n\t\tthis._tooltip.updatePosition(latlng);\n\t\tif (this._isDrawing) {\n\t\t\tthis._drawShape(latlng);\n\n\t\t\t// Get the new radius (rounded to 1 dp)\n\t\t\tradius = this._shape.getRadius().toFixed(1);\n\n\t\t\tvar subtext = '';\n\t\t\tif (showRadius) {\n\t\t\t\tsubtext = L.drawLocal.draw.handlers.circle.radius + ': ' +\n\t\t\t\t\tL.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic);\n\t\t\t}\n\t\t\tthis._tooltip.updateContent({\n\t\t\t\ttext: this._endLabelText,\n\t\t\t\tsubtext: subtext\n\t\t\t});\n\t\t}\n\t}\n});\n","L.Edit = L.Edit || {};\n\n/**\n * @class L.Edit.Marker\n * @aka Edit.Marker\n */\nL.Edit.Marker = L.Handler.extend({\n\t// @method initialize(): void\n\tinitialize: function (marker, options) {\n\t\tthis._marker = marker;\n\t\tL.setOptions(this, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tvar marker = this._marker;\n\n\t\tmarker.dragging.enable();\n\t\tmarker.on('dragend', this._onDragEnd, marker);\n\t\tthis._toggleMarkerHighlight();\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tvar marker = this._marker;\n\n\t\tmarker.dragging.disable();\n\t\tmarker.off('dragend', this._onDragEnd, marker);\n\t\tthis._toggleMarkerHighlight();\n\t},\n\n\t_onDragEnd: function (e) {\n\t\tvar layer = e.target;\n\t\tlayer.edited = true;\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});\n\t},\n\n\t_toggleMarkerHighlight: function () {\n\t\tvar icon = this._marker._icon;\n\n\t\t// Don't do anything if this layer is a marker but doesn't have an icon. Markers\n\t\t// should usually have icons. If using Leaflet.draw with Leaflet.markercluster there\n\t\t// is a chance that a marker doesn't.\n\t\tif (!icon) {\n\t\t\treturn;\n\t\t}\n\n\t\t// This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)\n\t\ticon.style.display = 'none';\n\n\t\tif (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {\n\t\t\tL.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');\n\t\t\t// Offset as the border will make the icon move.\n\t\t\tthis._offsetMarker(icon, -4);\n\n\t\t} else {\n\t\t\tL.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');\n\t\t\t// Offset as the border will make the icon move.\n\t\t\tthis._offsetMarker(icon, 4);\n\t\t}\n\n\t\ticon.style.display = '';\n\t},\n\n\t_offsetMarker: function (icon, offset) {\n\t\tvar iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,\n\t\t\ticonMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;\n\n\t\ticon.style.marginTop = iconMarginTop + 'px';\n\t\ticon.style.marginLeft = iconMarginLeft + 'px';\n\t}\n});\n\nL.Marker.addInitHook(function () {\n\tif (L.Edit.Marker) {\n\t\tthis.editing = new L.Edit.Marker(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n});\n","L.Edit = L.Edit || {};\n\n/**\n * @class L.Edit.Polyline\n * @aka L.Edit.Poly\n * @aka Edit.Poly\n */\nL.Edit.Poly = L.Handler.extend({\n\t// @method initialize(): void\n\tinitialize: function (poly) {\n\n\t\tthis.latlngs = [poly._latlngs];\n\t\tif (poly._holes) {\n\t\t\tthis.latlngs = this.latlngs.concat(poly._holes);\n\t\t}\n\n\t\tthis._poly = poly;\n\n\t\tthis._poly.on('revert-edited', this._updateLatLngs, this);\n\t},\n\n\t// Compatibility method to normalize Poly* objects\n\t// between 0.7.x and 1.0+\n\t_defaultShape: function () {\n\t\tif (!L.Polyline._flat) {\n\t\t\treturn this._poly._latlngs;\n\t\t}\n\t\treturn L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0];\n\t},\n\n\t_eachVertexHandler: function (callback) {\n\t\tfor (var i = 0; i < this._verticesHandlers.length; i++) {\n\t\t\tcallback(this._verticesHandlers[i]);\n\t\t}\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tthis._initHandlers();\n\t\tthis._eachVertexHandler(function (handler) {\n\t\t\thandler.addHooks();\n\t\t});\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tthis._eachVertexHandler(function (handler) {\n\t\t\thandler.removeHooks();\n\t\t});\n\t},\n\n\t// @method updateMarkers(): void\n\t// Fire an update for each vertex handler\n\tupdateMarkers: function () {\n\t\tthis._eachVertexHandler(function (handler) {\n\t\t\thandler.updateMarkers();\n\t\t});\n\t},\n\n\t_initHandlers: function () {\n\t\tthis._verticesHandlers = [];\n\t\tfor (var i = 0; i < this.latlngs.length; i++) {\n\t\t\tthis._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly));\n\t\t}\n\t},\n\n\t_updateLatLngs: function (e) {\n\t\tthis.latlngs = [e.layer._latlngs];\n\t\tif (e.layer._holes) {\n\t\t\tthis.latlngs = this.latlngs.concat(e.layer._holes);\n\t\t}\n\t}\n\n});\n\n/**\n * @class L.Edit.PolyVerticesEdit\n * @aka Edit.PolyVerticesEdit\n */\nL.Edit.PolyVerticesEdit = L.Handler.extend({\n\toptions: {\n\t\ticon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon'\n\t\t}),\n\t\ttouchIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'\n\t\t}),\n\t\tdrawError: {\n\t\t\tcolor: '#b00b00',\n\t\t\ttimeout: 1000\n\t\t}\n\n\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (poly, latlngs, options) {\n\t\t// if touch, switch to touch icon\n\t\tif (L.Browser.touch) {\n\t\t\tthis.options.icon = this.options.touchIcon;\n\t\t}\n\t\tthis._poly = poly;\n\n\t\tif (options && options.drawError) {\n\t\t\toptions.drawError = L.Util.extend({}, this.options.drawError, options.drawError);\n\t\t}\n\n\t\tthis._latlngs = latlngs;\n\n\t\tL.setOptions(this, options);\n\t},\n\n\t// Compatibility method to normalize Poly* objects\n\t// between 0.7.x and 1.0+\n\t_defaultShape: function () {\n\t\tif (!L.Polyline._flat) {\n\t\t\treturn this._latlngs;\n\t\t}\n\t\treturn L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler.\n\taddHooks: function () {\n\t\tvar poly = this._poly;\n\t\tvar path = poly._path;\n\n\t\tif (!(poly instanceof L.Polygon)) {\n\t\t\tpoly.options.fill = false;\n\t\t\tif (poly.options.editing) {\n\t\t\t\tpoly.options.editing.fill = false;\n\t\t\t}\n\t\t}\n\n\t\tif (path) {\n\t\t\tif (poly.options.editing.className) {\n\t\t\t\tif (poly.options.original.className) {\n\t\t\t\t\tpoly.options.original.className.split(' ').forEach(function (className) {\n\t\t\t\t\t\tL.DomUtil.removeClass(path, className);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tpoly.options.editing.className.split(' ').forEach(function (className) {\n\t\t\t\t\tL.DomUtil.addClass(path, className);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tpoly.setStyle(poly.options.editing);\n\n\t\tif (this._poly._map) {\n\n\t\t\tthis._map = this._poly._map; // Set map\n\n\t\t\tif (!this._markerGroup) {\n\t\t\t\tthis._initMarkers();\n\t\t\t}\n\t\t\tthis._poly._map.addLayer(this._markerGroup);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tvar poly = this._poly;\n\t\tvar path = poly._path;\n\n\t\tif (path) {\n\t\t\tif (poly.options.editing.className) {\n\t\t\t\tpoly.options.editing.className.split(' ').forEach(function (className) {\n\t\t\t\t\tL.DomUtil.removeClass(path, className);\n\t\t\t\t});\n\t\t\t\tif (poly.options.original.className) {\n\t\t\t\t\tpoly.options.original.className.split(' ').forEach(function (className) {\n\t\t\t\t\t\tL.DomUtil.addClass(path, className);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpoly.setStyle(poly.options.original);\n\n\t\tif (poly._map) {\n\t\t\tpoly._map.removeLayer(this._markerGroup);\n\t\t\tdelete this._markerGroup;\n\t\t\tdelete this._markers;\n\t\t}\n\t},\n\n\t// @method updateMarkers(): void\n\t// Clear markers and update their location\n\tupdateMarkers: function () {\n\t\tthis._markerGroup.clearLayers();\n\t\tthis._initMarkers();\n\t},\n\n\t_initMarkers: function () {\n\t\tif (!this._markerGroup) {\n\t\t\tthis._markerGroup = new L.LayerGroup();\n\t\t}\n\t\tthis._markers = [];\n\n\t\tvar latlngs = this._defaultShape(),\n\t\t\ti, j, len, marker;\n\n\t\tfor (i = 0, len = latlngs.length; i < len; i++) {\n\n\t\t\tmarker = this._createMarker(latlngs[i], i);\n\t\t\tmarker.on('click', this._onMarkerClick, this);\n\t\t\tmarker.on('contextmenu', this._onContextMenu, this);\n\t\t\tthis._markers.push(marker);\n\t\t}\n\n\t\tvar markerLeft, markerRight;\n\n\t\tfor (i = 0, j = len - 1; i < len; j = i++) {\n\t\t\tif (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmarkerLeft = this._markers[j];\n\t\t\tmarkerRight = this._markers[i];\n\n\t\t\tthis._createMiddleMarker(markerLeft, markerRight);\n\t\t\tthis._updatePrevNext(markerLeft, markerRight);\n\t\t}\n\t},\n\n\t_createMarker: function (latlng, index) {\n\t\t// Extending L.Marker in TouchEvents.js to include touch.\n\t\tvar marker = new L.Marker.Touch(latlng, {\n\t\t\tdraggable: true,\n\t\t\ticon: this.options.icon,\n\t\t});\n\n\t\tmarker._origLatLng = latlng;\n\t\tmarker._index = index;\n\n\t\tmarker\n\t\t\t.on('dragstart', this._onMarkerDragStart, this)\n\t\t\t.on('drag', this._onMarkerDrag, this)\n\t\t\t.on('dragend', this._fireEdit, this)\n\t\t\t.on('touchmove', this._onTouchMove, this)\n\t\t\t.on('touchend', this._fireEdit, this)\n\t\t\t.on('MSPointerMove', this._onTouchMove, this)\n\t\t\t.on('MSPointerUp', this._fireEdit, this);\n\n\t\tthis._markerGroup.addLayer(marker);\n\n\t\treturn marker;\n\t},\n\n\t_onMarkerDragStart: function () {\n\t\tthis._poly.fire('editstart');\n\t},\n\n\t_spliceLatLngs: function () {\n\t\tvar latlngs = this._defaultShape();\n\t\tvar removed = [].splice.apply(latlngs, arguments);\n\t\tthis._poly._convertLatLngs(latlngs, true);\n\t\tthis._poly.redraw();\n\t\treturn removed;\n\t},\n\n\t_removeMarker: function (marker) {\n\t\tvar i = marker._index;\n\n\t\tthis._markerGroup.removeLayer(marker);\n\t\tthis._markers.splice(i, 1);\n\t\tthis._spliceLatLngs(i, 1);\n\t\tthis._updateIndexes(i, -1);\n\n\t\tmarker\n\t\t\t.off('dragstart', this._onMarkerDragStart, this)\n\t\t\t.off('drag', this._onMarkerDrag, this)\n\t\t\t.off('dragend', this._fireEdit, this)\n\t\t\t.off('touchmove', this._onMarkerDrag, this)\n\t\t\t.off('touchend', this._fireEdit, this)\n\t\t\t.off('click', this._onMarkerClick, this)\n\t\t\t.off('MSPointerMove', this._onTouchMove, this)\n\t\t\t.off('MSPointerUp', this._fireEdit, this);\n\t},\n\n\t_fireEdit: function () {\n\t\tthis._poly.edited = true;\n\t\tthis._poly.fire('edit');\n\t\tthis._poly._map.fire(L.Draw.Event.EDITVERTEX, {layers: this._markerGroup, poly: this._poly});\n\t},\n\n\t_onMarkerDrag: function (e) {\n\t\tvar marker = e.target;\n\t\tvar poly = this._poly;\n\n\t\tL.extend(marker._origLatLng, marker._latlng);\n\n\t\tif (marker._middleLeft) {\n\t\t\tmarker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));\n\t\t}\n\t\tif (marker._middleRight) {\n\t\t\tmarker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));\n\t\t}\n\n\t\tif (poly.options.poly) {\n\t\t\tvar tooltip = poly._map._editTooltip; // Access the tooltip\n\n\t\t\t// If we don't allow intersections and the polygon intersects\n\t\t\tif (!poly.options.poly.allowIntersection && poly.intersects()) {\n\n\t\t\t\tvar originalColor = poly.options.color;\n\t\t\t\tpoly.setStyle({color: this.options.drawError.color});\n\n\t\t\t\t// Manually trigger 'dragend' behavior on marker we are about to remove\n\t\t\t\t// WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484\n\t\t\t\tif (L.version.indexOf('0.7') !== 0) {\n\t\t\t\t\tmarker.dragging._draggable._onUp(e);\n\t\t\t\t}\n\t\t\t\tthis._onMarkerClick(e); // Remove violating marker\n\t\t\t\t// FIXME: Reset the marker to it's original position (instead of remove)\n\n\t\t\t\tif (tooltip) {\n\t\t\t\t\ttooltip.updateContent({\n\t\t\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.error\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Reset everything back to normal after a second\n\t\t\t\tsetTimeout(function () {\n\t\t\t\t\tpoly.setStyle({color: originalColor});\n\t\t\t\t\tif (tooltip) {\n\t\t\t\t\t\ttooltip.updateContent({\n\t\t\t\t\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\t\t\t\t\tsubtext: L.drawLocal.edit.handlers.edit.tooltip.subtext\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}, 1000);\n\t\t\t}\n\t\t}\n\t\t//refresh the bounds when draging\n\t\tthis._poly._bounds._southWest = L.latLng(Infinity, Infinity);\n\t\tthis._poly._bounds._northEast = L.latLng(-Infinity, -Infinity);\n\t\tvar latlngs = this._poly.getLatLngs();\n\t\tthis._poly._convertLatLngs(latlngs, true);\n\t\tthis._poly.redraw();\n\t\tthis._poly.fire('editdrag');\n\t},\n\n\t_onMarkerClick: function (e) {\n\n\t\tvar minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,\n\t\t\tmarker = e.target;\n\n\t\t// If removing this point would create an invalid polyline/polygon don't remove\n\t\tif (this._defaultShape().length < minPoints) {\n\t\t\treturn;\n\t\t}\n\n\t\t// remove the marker\n\t\tthis._removeMarker(marker);\n\n\t\t// update prev/next links of adjacent markers\n\t\tthis._updatePrevNext(marker._prev, marker._next);\n\n\t\t// remove ghost markers near the removed marker\n\t\tif (marker._middleLeft) {\n\t\t\tthis._markerGroup.removeLayer(marker._middleLeft);\n\t\t}\n\t\tif (marker._middleRight) {\n\t\t\tthis._markerGroup.removeLayer(marker._middleRight);\n\t\t}\n\n\t\t// create a ghost marker in place of the removed one\n\t\tif (marker._prev && marker._next) {\n\t\t\tthis._createMiddleMarker(marker._prev, marker._next);\n\n\t\t} else if (!marker._prev) {\n\t\t\tmarker._next._middleLeft = null;\n\n\t\t} else if (!marker._next) {\n\t\t\tmarker._prev._middleRight = null;\n\t\t}\n\n\t\tthis._fireEdit();\n\t},\n\n\t_onContextMenu: function (e) {\n\t\tvar marker = e.target;\n\t\tvar poly = this._poly;\n\t\tthis._poly._map.fire(L.Draw.Event.MARKERCONTEXT, {marker: marker, layers: this._markerGroup, poly: this._poly});\n\t\tL.DomEvent.stopPropagation;\n\t},\n\n\t_onTouchMove: function (e) {\n\n\t\tvar layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint),\n\t\t\tmarker = e.target;\n\n\t\tL.extend(marker._origLatLng, latlng);\n\n\t\tif (marker._middleLeft) {\n\t\t\tmarker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));\n\t\t}\n\t\tif (marker._middleRight) {\n\t\t\tmarker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));\n\t\t}\n\n\t\tthis._poly.redraw();\n\t\tthis.updateMarkers();\n\t},\n\n\t_updateIndexes: function (index, delta) {\n\t\tthis._markerGroup.eachLayer(function (marker) {\n\t\t\tif (marker._index > index) {\n\t\t\t\tmarker._index += delta;\n\t\t\t}\n\t\t});\n\t},\n\n\t_createMiddleMarker: function (marker1, marker2) {\n\t\tvar latlng = this._getMiddleLatLng(marker1, marker2),\n\t\t\tmarker = this._createMarker(latlng),\n\t\t\tonClick,\n\t\t\tonDragStart,\n\t\t\tonDragEnd;\n\n\t\tmarker.setOpacity(0.6);\n\n\t\tmarker1._middleRight = marker2._middleLeft = marker;\n\n\t\tonDragStart = function () {\n\t\t\tmarker.off('touchmove', onDragStart, this);\n\t\t\tvar i = marker2._index;\n\n\t\t\tmarker._index = i;\n\n\t\t\tmarker\n\t\t\t\t.off('click', onClick, this)\n\t\t\t\t.on('click', this._onMarkerClick, this);\n\n\t\t\tlatlng.lat = marker.getLatLng().lat;\n\t\t\tlatlng.lng = marker.getLatLng().lng;\n\t\t\tthis._spliceLatLngs(i, 0, latlng);\n\t\t\tthis._markers.splice(i, 0, marker);\n\n\t\t\tmarker.setOpacity(1);\n\n\t\t\tthis._updateIndexes(i, 1);\n\t\t\tmarker2._index++;\n\t\t\tthis._updatePrevNext(marker1, marker);\n\t\t\tthis._updatePrevNext(marker, marker2);\n\n\t\t\tthis._poly.fire('editstart');\n\t\t};\n\n\t\tonDragEnd = function () {\n\t\t\tmarker.off('dragstart', onDragStart, this);\n\t\t\tmarker.off('dragend', onDragEnd, this);\n\t\t\tmarker.off('touchmove', onDragStart, this);\n\n\t\t\tthis._createMiddleMarker(marker1, marker);\n\t\t\tthis._createMiddleMarker(marker, marker2);\n\t\t};\n\n\t\tonClick = function () {\n\t\t\tonDragStart.call(this);\n\t\t\tonDragEnd.call(this);\n\t\t\tthis._fireEdit();\n\t\t};\n\n\t\tmarker\n\t\t\t.on('click', onClick, this)\n\t\t\t.on('dragstart', onDragStart, this)\n\t\t\t.on('dragend', onDragEnd, this)\n\t\t\t.on('touchmove', onDragStart, this);\n\n\t\tthis._markerGroup.addLayer(marker);\n\t},\n\n\t_updatePrevNext: function (marker1, marker2) {\n\t\tif (marker1) {\n\t\t\tmarker1._next = marker2;\n\t\t}\n\t\tif (marker2) {\n\t\t\tmarker2._prev = marker1;\n\t\t}\n\t},\n\n\t_getMiddleLatLng: function (marker1, marker2) {\n\t\tvar map = this._poly._map,\n\t\t\tp1 = map.project(marker1.getLatLng()),\n\t\t\tp2 = map.project(marker2.getLatLng());\n\n\t\treturn map.unproject(p1._add(p2)._divideBy(2));\n\t}\n});\n\nL.Polyline.addInitHook(function () {\n\n\t// Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit\n\tif (this.editing) {\n\t\treturn;\n\t}\n\n\tif (L.Edit.Poly) {\n\n\t\tthis.editing = new L.Edit.Poly(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n\n\tthis.on('add', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.addHooks();\n\t\t}\n\t});\n\n\tthis.on('remove', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.removeHooks();\n\t\t}\n\t});\n});\n","L.Edit = L.Edit || {};\n/**\n * @class L.Edit.SimpleShape\n * @aka Edit.SimpleShape\n */\nL.Edit.SimpleShape = L.Handler.extend({\n\toptions: {\n\t\tmoveIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'\n\t\t}),\n\t\tresizeIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'\n\t\t}),\n\t\ttouchMoveIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon'\n\t\t}),\n\t\ttouchResizeIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon'\n\t\t}),\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (shape, options) {\n\t\t// if touch, switch to touch icon\n\t\tif (L.Browser.touch) {\n\t\t\tthis.options.moveIcon = this.options.touchMoveIcon;\n\t\t\tthis.options.resizeIcon = this.options.touchResizeIcon;\n\t\t}\n\n\t\tthis._shape = shape;\n\t\tL.Util.setOptions(this, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tvar shape = this._shape;\n\t\tif (this._shape._map) {\n\t\t\tthis._map = this._shape._map;\n\t\t\tshape.setStyle(shape.options.editing);\n\n\t\t\tif (shape._map) {\n\t\t\t\tthis._map = shape._map;\n\t\t\t\tif (!this._markerGroup) {\n\t\t\t\t\tthis._initMarkers();\n\t\t\t\t}\n\t\t\t\tthis._map.addLayer(this._markerGroup);\n\t\t\t}\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tvar shape = this._shape;\n\n\t\tshape.setStyle(shape.options.original);\n\n\t\tif (shape._map) {\n\t\t\tthis._unbindMarker(this._moveMarker);\n\n\t\t\tfor (var i = 0, l = this._resizeMarkers.length; i < l; i++) {\n\t\t\t\tthis._unbindMarker(this._resizeMarkers[i]);\n\t\t\t}\n\t\t\tthis._resizeMarkers = null;\n\n\t\t\tthis._map.removeLayer(this._markerGroup);\n\t\t\tdelete this._markerGroup;\n\t\t}\n\n\t\tthis._map = null;\n\t},\n\n\t// @method updateMarkers(): void\n\t// Remove the edit markers from this layer\n\tupdateMarkers: function () {\n\t\tthis._markerGroup.clearLayers();\n\t\tthis._initMarkers();\n\t},\n\n\t_initMarkers: function () {\n\t\tif (!this._markerGroup) {\n\t\t\tthis._markerGroup = new L.LayerGroup();\n\t\t}\n\n\t\t// Create center marker\n\t\tthis._createMoveMarker();\n\n\t\t// Create edge marker\n\t\tthis._createResizeMarker();\n\t},\n\n\t_createMoveMarker: function () {\n\t\t// Children override\n\t},\n\n\t_createResizeMarker: function () {\n\t\t// Children override\n\t},\n\n\t_createMarker: function (latlng, icon) {\n\t\t// Extending L.Marker in TouchEvents.js to include touch.\n\t\tvar marker = new L.Marker.Touch(latlng, {\n\t\t\tdraggable: true,\n\t\t\ticon: icon,\n\t\t\tzIndexOffset: 10\n\t\t});\n\n\t\tthis._bindMarker(marker);\n\n\t\tthis._markerGroup.addLayer(marker);\n\n\t\treturn marker;\n\t},\n\n\t_bindMarker: function (marker) {\n\t\tmarker\n\t\t\t.on('dragstart', this._onMarkerDragStart, this)\n\t\t\t.on('drag', this._onMarkerDrag, this)\n\t\t\t.on('dragend', this._onMarkerDragEnd, this)\n\t\t\t.on('touchstart', this._onTouchStart, this)\n\t\t\t.on('touchmove', this._onTouchMove, this)\n\t\t\t.on('MSPointerMove', this._onTouchMove, this)\n\t\t\t.on('touchend', this._onTouchEnd, this)\n\t\t\t.on('MSPointerUp', this._onTouchEnd, this);\n\t},\n\n\t_unbindMarker: function (marker) {\n\t\tmarker\n\t\t\t.off('dragstart', this._onMarkerDragStart, this)\n\t\t\t.off('drag', this._onMarkerDrag, this)\n\t\t\t.off('dragend', this._onMarkerDragEnd, this)\n\t\t\t.off('touchstart', this._onTouchStart, this)\n\t\t\t.off('touchmove', this._onTouchMove, this)\n\t\t\t.off('MSPointerMove', this._onTouchMove, this)\n\t\t\t.off('touchend', this._onTouchEnd, this)\n\t\t\t.off('MSPointerUp', this._onTouchEnd, this);\n\t},\n\n\t_onMarkerDragStart: function (e) {\n\t\tvar marker = e.target;\n\t\tmarker.setOpacity(0);\n\n\t\tthis._shape.fire('editstart');\n\t},\n\n\t_fireEdit: function () {\n\t\tthis._shape.edited = true;\n\t\tthis._shape.fire('edit');\n\t},\n\n\t_onMarkerDrag: function (e) {\n\t\tvar marker = e.target,\n\t\t\tlatlng = marker.getLatLng();\n\n\t\tif (marker === this._moveMarker) {\n\t\t\tthis._move(latlng);\n\t\t} else {\n\t\t\tthis._resize(latlng);\n\t\t}\n\n\t\tthis._shape.redraw();\n\t\tthis._shape.fire('editdrag');\n\t},\n\n\t_onMarkerDragEnd: function (e) {\n\t\tvar marker = e.target;\n\t\tmarker.setOpacity(1);\n\n\t\tthis._fireEdit();\n\t},\n\n\t_onTouchStart: function (e) {\n\t\tL.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);\n\n\t\tif (typeof(this._getCorners) === 'function') {\n\t\t\t// Save a reference to the opposite point\n\t\t\tvar corners = this._getCorners(),\n\t\t\t\tmarker = e.target,\n\t\t\t\tcurrentCornerIndex = marker._cornerIndex;\n\n\t\t\tmarker.setOpacity(0);\n\n\t\t\t// Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart()\n\t\t\t// Latlng is null otherwise.\n\t\t\tthis._oppositeCorner = corners[(currentCornerIndex + 2) % 4];\n\t\t\tthis._toggleCornerMarkers(0, currentCornerIndex);\n\t\t}\n\n\t\tthis._shape.fire('editstart');\n\t},\n\n\t_onTouchMove: function (e) {\n\t\tvar layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint),\n\t\t\tmarker = e.target;\n\n\t\tif (marker === this._moveMarker) {\n\t\t\tthis._move(latlng);\n\t\t} else {\n\t\t\tthis._resize(latlng);\n\t\t}\n\n\t\tthis._shape.redraw();\n\n\t\t// prevent touchcancel in IOS\n\t\t// e.preventDefault();\n\t\treturn false;\n\t},\n\n\t_onTouchEnd: function (e) {\n\t\tvar marker = e.target;\n\t\tmarker.setOpacity(1);\n\t\tthis.updateMarkers();\n\t\tthis._fireEdit();\n\t},\n\n\t_move: function () {\n\t\t// Children override\n\t},\n\n\t_resize: function () {\n\t\t// Children override\n\t}\n});\n","L.Edit = L.Edit || {};\n/**\n * @class L.Edit.Rectangle\n * @aka Edit.Rectangle\n * @inherits L.Edit.SimpleShape\n */\nL.Edit.Rectangle = L.Edit.SimpleShape.extend({\n\t_createMoveMarker: function () {\n\t\tvar bounds = this._shape.getBounds(),\n\t\t\tcenter = bounds.getCenter();\n\n\t\tthis._moveMarker = this._createMarker(center, this.options.moveIcon);\n\t},\n\n\t_createResizeMarker: function () {\n\t\tvar corners = this._getCorners();\n\n\t\tthis._resizeMarkers = [];\n\n\t\tfor (var i = 0, l = corners.length; i < l; i++) {\n\t\t\tthis._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));\n\t\t\t// Monkey in the corner index as we will need to know this for dragging\n\t\t\tthis._resizeMarkers[i]._cornerIndex = i;\n\t\t}\n\t},\n\n\t_onMarkerDragStart: function (e) {\n\t\tL.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);\n\n\t\t// Save a reference to the opposite point\n\t\tvar corners = this._getCorners(),\n\t\t\tmarker = e.target,\n\t\t\tcurrentCornerIndex = marker._cornerIndex;\n\n\t\tthis._oppositeCorner = corners[(currentCornerIndex + 2) % 4];\n\n\t\tthis._toggleCornerMarkers(0, currentCornerIndex);\n\t},\n\n\t_onMarkerDragEnd: function (e) {\n\t\tvar marker = e.target,\n\t\t\tbounds, center;\n\n\t\t// Reset move marker position to the center\n\t\tif (marker === this._moveMarker) {\n\t\t\tbounds = this._shape.getBounds();\n\t\t\tcenter = bounds.getCenter();\n\n\t\t\tmarker.setLatLng(center);\n\t\t}\n\n\t\tthis._toggleCornerMarkers(1);\n\n\t\tthis._repositionCornerMarkers();\n\n\t\tL.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);\n\t},\n\n\t_move: function (newCenter) {\n\t\tvar latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(),\n\t\t\tbounds = this._shape.getBounds(),\n\t\t\tcenter = bounds.getCenter(),\n\t\t\toffset, newLatLngs = [];\n\n\t\t// Offset the latlngs to the new center\n\t\tfor (var i = 0, l = latlngs.length; i < l; i++) {\n\t\t\toffset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];\n\t\t\tnewLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);\n\t\t}\n\n\t\tthis._shape.setLatLngs(newLatLngs);\n\n\t\t// Reposition the resize markers\n\t\tthis._repositionCornerMarkers();\n\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});\n\t},\n\n\t_resize: function (latlng) {\n\t\tvar bounds;\n\n\t\t// Update the shape based on the current position of this corner and the opposite point\n\t\tthis._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));\n\n\t\t// Reposition the move marker\n\t\tbounds = this._shape.getBounds();\n\t\tthis._moveMarker.setLatLng(bounds.getCenter());\n\n\t\tthis._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});\n\t},\n\n\t_getCorners: function () {\n\t\tvar bounds = this._shape.getBounds(),\n\t\t\tnw = bounds.getNorthWest(),\n\t\t\tne = bounds.getNorthEast(),\n\t\t\tse = bounds.getSouthEast(),\n\t\t\tsw = bounds.getSouthWest();\n\n\t\treturn [nw, ne, se, sw];\n\t},\n\n\t_toggleCornerMarkers: function (opacity) {\n\t\tfor (var i = 0, l = this._resizeMarkers.length; i < l; i++) {\n\t\t\tthis._resizeMarkers[i].setOpacity(opacity);\n\t\t}\n\t},\n\n\t_repositionCornerMarkers: function () {\n\t\tvar corners = this._getCorners();\n\n\t\tfor (var i = 0, l = this._resizeMarkers.length; i < l; i++) {\n\t\t\tthis._resizeMarkers[i].setLatLng(corners[i]);\n\t\t}\n\t}\n});\n\nL.Rectangle.addInitHook(function () {\n\tif (L.Edit.Rectangle) {\n\t\tthis.editing = new L.Edit.Rectangle(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n});\n","L.Edit = L.Edit || {};\n/**\n * @class L.Edit.CircleMarker\n * @aka Edit.Circle\n * @inherits L.Edit.SimpleShape\n */\nL.Edit.CircleMarker = L.Edit.SimpleShape.extend({\n\t_createMoveMarker: function () {\n\t\tvar center = this._shape.getLatLng();\n\n\t\tthis._moveMarker = this._createMarker(center, this.options.moveIcon);\n\t},\n\n\t_createResizeMarker: function () {\n\t\t// To avoid an undefined check in L.Edit.SimpleShape.removeHooks\n\t\tthis._resizeMarkers = [];\n\t},\n\n\t_move: function (latlng) {\n\t\tif (this._resizeMarkers.length) {\n\t\t\tvar resizemarkerPoint = this._getResizeMarkerPoint(latlng);\n\t\t\t// Move the resize marker\n\t\t\tthis._resizeMarkers[0].setLatLng(resizemarkerPoint);\n\t\t}\n\n\t\t// Move the circle\n\t\tthis._shape.setLatLng(latlng);\n\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});\n\t},\n});\n\nL.CircleMarker.addInitHook(function () {\n\tif (L.Edit.CircleMarker) {\n\t\tthis.editing = new L.Edit.CircleMarker(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n\n\tthis.on('add', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.addHooks();\n\t\t}\n\t});\n\n\tthis.on('remove', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.removeHooks();\n\t\t}\n\t});\n});\n","L.Edit = L.Edit || {};\n/**\n * @class L.Edit.Circle\n * @aka Edit.Circle\n * @inherits L.Edit.CircleMarker\n */\nL.Edit.Circle = L.Edit.CircleMarker.extend({\n\n\t_createResizeMarker: function () {\n\t\tvar center = this._shape.getLatLng(),\n\t\t\tresizemarkerPoint = this._getResizeMarkerPoint(center);\n\n\t\tthis._resizeMarkers = [];\n\t\tthis._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));\n\t},\n\n\t_getResizeMarkerPoint: function (latlng) {\n\t\t// From L.shape.getBounds()\n\t\tvar delta = this._shape._radius * Math.cos(Math.PI / 4),\n\t\t\tpoint = this._map.project(latlng);\n\t\treturn this._map.unproject([point.x + delta, point.y - delta]);\n\t},\n\n\t_resize: function (latlng) {\n\t\tvar moveLatLng = this._moveMarker.getLatLng();\n\n\t\t// Calculate the radius based on the version\n\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\tradius = moveLatLng.distanceTo(latlng);\n\t\t} else {\n\t\t\tradius = this._map.distance(moveLatLng, latlng);\n\t\t}\n\t\tthis._shape.setRadius(radius);\n\n\t\tthis._map._editTooltip.updateContent({\n\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.subtext + '
' + L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\tsubtext: L.drawLocal.draw.handlers.circle.radius + ': ' +\n\t\t\tL.GeometryUtil.readableDistance(radius, true, this.options.feet, this.options.nautic)\n\t\t});\n\n\t\tthis._shape.setRadius(radius);\n\n\t\tthis._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});\n\t}\n});\n\nL.Circle.addInitHook(function () {\n\tif (L.Edit.Circle) {\n\t\tthis.editing = new L.Edit.Circle(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n\n\tthis.on('add', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.addHooks();\n\t\t}\n\t});\n\n\tthis.on('remove', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.removeHooks();\n\t\t}\n\t});\n});\n","L.Map.mergeOptions({\n\ttouchExtend: true\n});\n\n/**\n * @class L.Map.TouchExtend\n * @aka TouchExtend\n */\nL.Map.TouchExtend = L.Handler.extend({\n\n\t// @method initialize(): void\n\t// Sets TouchExtend private accessor variables\n\tinitialize: function (map) {\n\t\tthis._map = map;\n\t\tthis._container = map._container;\n\t\tthis._pane = map._panes.overlayPane;\n\t},\n\n\t// @method addHooks(): void\n\t// Adds dom listener events to the map container\n\taddHooks: function () {\n\t\tL.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);\n\t\tL.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);\n\t\tL.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);\n\t\tif (this._detectIE()) {\n\t\t\tL.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this);\n\t\t\tL.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this);\n\t\t\tL.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this);\n\t\t\tL.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this);\n\n\t\t} else {\n\t\t\tL.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this);\n\t\t\tL.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Removes dom listener events from the map container\n\tremoveHooks: function () {\n\t\tL.DomEvent.off(this._container, 'touchstart', this._onTouchStart);\n\t\tL.DomEvent.off(this._container, 'touchend', this._onTouchEnd);\n\t\tL.DomEvent.off(this._container, 'touchmove', this._onTouchMove);\n\t\tif (this._detectIE()) {\n\t\t\tL.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart);\n\t\t\tL.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd);\n\t\t\tL.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove);\n\t\t\tL.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel);\n\t\t} else {\n\t\t\tL.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel);\n\t\t\tL.DomEvent.off(this._container, 'touchleave', this._onTouchLeave);\n\t\t}\n\t},\n\n\t_touchEvent: function (e, type) {\n\t\t// #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events\n\t\t// _filterClick is what leaflet uses as a workaround.\n\t\t// This is a problem with more things than just android. Another problem is touchEnd has no touches in\n\t\t// its touch list.\n\t\tvar touchEvent = {};\n\t\tif (typeof e.touches !== 'undefined') {\n\t\t\tif (!e.touches.length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttouchEvent = e.touches[0];\n\t\t} else if (e.pointerType === 'touch') {\n\t\t\ttouchEvent = e;\n\t\t\tif (!this._filterClick(e)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\tvar containerPoint = this._map.mouseEventToContainerPoint(touchEvent),\n\t\t\tlayerPoint = this._map.mouseEventToLayerPoint(touchEvent),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint);\n\n\t\tthis._map.fire(type, {\n\t\t\tlatlng: latlng,\n\t\t\tlayerPoint: layerPoint,\n\t\t\tcontainerPoint: containerPoint,\n\t\t\tpageX: touchEvent.pageX,\n\t\t\tpageY: touchEvent.pageY,\n\t\t\toriginalEvent: e\n\t\t});\n\t},\n\n\t/** Borrowed from Leaflet and modified for bool ops **/\n\t_filterClick: function (e) {\n\t\tvar timeStamp = (e.timeStamp || e.originalEvent.timeStamp),\n\t\t\telapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);\n\n\t\t// are they closer together than 500ms yet more than 100ms?\n\t\t// Android typically triggers them ~300ms apart while multiple listeners\n\t\t// on the same event should be triggered far faster;\n\t\t// or check if click is simulated on the element, and if it is, reject any non-simulated events\n\t\tif ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {\n\t\t\tL.DomEvent.stop(e);\n\t\t\treturn false;\n\t\t}\n\t\tL.DomEvent._lastClick = timeStamp;\n\t\treturn true;\n\t},\n\n\t_onTouchStart: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchstart';\n\t\tthis._touchEvent(e, type);\n\n\t},\n\n\t_onTouchEnd: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchend';\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_onTouchCancel: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchcancel';\n\t\tif (this._detectIE()) {\n\t\t\ttype = 'pointercancel';\n\t\t}\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_onTouchLeave: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchleave';\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_onTouchMove: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchmove';\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_detectIE: function () {\n\t\tvar ua = window.navigator.userAgent;\n\n\t\tvar msie = ua.indexOf('MSIE ');\n\t\tif (msie > 0) {\n\t\t\t// IE 10 or older => return version number\n\t\t\treturn parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);\n\t\t}\n\n\t\tvar trident = ua.indexOf('Trident/');\n\t\tif (trident > 0) {\n\t\t\t// IE 11 => return version number\n\t\t\tvar rv = ua.indexOf('rv:');\n\t\t\treturn parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);\n\t\t}\n\n\t\tvar edge = ua.indexOf('Edge/');\n\t\tif (edge > 0) {\n\t\t\t// IE 12 => return version number\n\t\t\treturn parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);\n\t\t}\n\n\t\t// other browser\n\t\treturn false;\n\t}\n});\n\nL.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);\n\n\n/**\n * @class L.Marker.Touch\n * @aka Marker.Touch\n *\n * This isn't full Touch support. This is just to get markers to also support dom touch events after creation\n * #TODO: find a better way of getting markers to support touch.\n */\nL.Marker.Touch = L.Marker.extend({\n\n\t_initInteraction: function () {\n\t\tif (!this.addInteractiveTarget) {\n\t\t\t// 0.7.x support\n\t\t\treturn this._initInteractionLegacy();\n\t\t}\n\t\t// TODO this may need be updated to re-add touch events for 1.0+\n\t\treturn L.Marker.prototype._initInteraction.apply(this);\n\t},\n\n\t// This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js\n\t// with the addition of the touch events\n\t_initInteractionLegacy: function () {\n\n\t\tif (!this.options.clickable) {\n\t\t\treturn;\n\t\t}\n\n\t\t// TODO refactor into something shared with Map/Path/etc. to DRY it up\n\n\t\tvar icon = this._icon,\n\t\t\tevents = ['dblclick',\n\t\t\t\t'mousedown',\n\t\t\t\t'mouseover',\n\t\t\t\t'mouseout',\n\t\t\t\t'contextmenu',\n\t\t\t\t'touchstart',\n\t\t\t\t'touchend',\n\t\t\t\t'touchmove'];\n\t\tif (this._detectIE) {\n\t\t\tevents.concat(['MSPointerDown',\n\t\t\t\t'MSPointerUp',\n\t\t\t\t'MSPointerMove',\n\t\t\t\t'MSPointerCancel']);\n\t\t} else {\n\t\t\tevents.concat(['touchcancel']);\n\t\t}\n\n\t\tL.DomUtil.addClass(icon, 'leaflet-clickable');\n\t\tL.DomEvent.on(icon, 'click', this._onMouseClick, this);\n\t\tL.DomEvent.on(icon, 'keypress', this._onKeyPress, this);\n\n\t\tfor (var i = 0; i < events.length; i++) {\n\t\t\tL.DomEvent.on(icon, events[i], this._fireMouseEvent, this);\n\t\t}\n\n\t\tif (L.Handler.MarkerDrag) {\n\t\t\tthis.dragging = new L.Handler.MarkerDrag(this);\n\n\t\t\tif (this.options.draggable) {\n\t\t\t\tthis.dragging.enable();\n\t\t\t}\n\t\t}\n\t},\n\n\t_detectIE: function () {\n\t\tvar ua = window.navigator.userAgent;\n\n\t\tvar msie = ua.indexOf('MSIE ');\n\t\tif (msie > 0) {\n\t\t\t// IE 10 or older => return version number\n\t\t\treturn parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);\n\t\t}\n\n\t\tvar trident = ua.indexOf('Trident/');\n\t\tif (trident > 0) {\n\t\t\t// IE 11 => return version number\n\t\t\tvar rv = ua.indexOf('rv:');\n\t\t\treturn parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);\n\t\t}\n\n\t\tvar edge = ua.indexOf('Edge/');\n\t\tif (edge > 0) {\n\t\t\t// IE 12 => return version number\n\t\t\treturn parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);\n\t\t}\n\n\t\t// other browser\n\t\treturn false;\n\t}\n});\n","/**\n * @class L.LatLngUtil\n * @aka LatLngUtil\n */\nL.LatLngUtil = {\n\t// Clones a LatLngs[], returns [][]\n\n\t// @method cloneLatLngs(LatLngs[]): L.LatLngs[]\n\t// Clone the latLng point or points or nested points and return an array with those points\n\tcloneLatLngs: function (latlngs) {\n\t\tvar clone = [];\n\t\tfor (var i = 0, l = latlngs.length; i < l; i++) {\n\t\t\t// Check for nested array (Polyline/Polygon)\n\t\t\tif (Array.isArray(latlngs[i])) {\n\t\t\t\tclone.push(L.LatLngUtil.cloneLatLngs(latlngs[i]));\n\t\t\t} else {\n\t\t\t\tclone.push(this.cloneLatLng(latlngs[i]));\n\t\t\t}\n\t\t}\n\t\treturn clone;\n\t},\n\n\t// @method cloneLatLng(LatLng): L.LatLng\n\t// Clone the latLng and return a new LatLng object.\n\tcloneLatLng: function (latlng) {\n\t\treturn L.latLng(latlng.lat, latlng.lng);\n\t}\n};\n","(function () {\n\n\tvar defaultPrecision = {\n\t\tkm: 2,\n\t\tha: 2,\n\t\tm: 0,\n\t\tmi: 2,\n\t\tac: 2,\n\t\tyd: 0,\n\t\tft: 0,\n\t\tnm: 2\n\t};\n\n\n\t/**\n\t * @class L.GeometryUtil\n\t * @aka GeometryUtil\n\t */\n\tL.GeometryUtil = L.extend(L.GeometryUtil || {}, {\n\t\t// Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270\n\n\t\t// @method geodesicArea(): number\n\t\tgeodesicArea: function (latLngs) {\n\t\t\tvar pointsCount = latLngs.length,\n\t\t\t\tarea = 0.0,\n\t\t\t\td2r = Math.PI / 180,\n\t\t\t\tp1, p2;\n\n\t\t\tif (pointsCount > 2) {\n\t\t\t\tfor (var i = 0; i < pointsCount; i++) {\n\t\t\t\t\tp1 = latLngs[i];\n\t\t\t\t\tp2 = latLngs[(i + 1) % pointsCount];\n\t\t\t\t\tarea += ((p2.lng - p1.lng) * d2r) *\n\t\t\t\t\t\t(2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r));\n\t\t\t\t}\n\t\t\t\tarea = area * 6378137.0 * 6378137.0 / 2.0;\n\t\t\t}\n\n\t\t\treturn Math.abs(area);\n\t\t},\n\n\t\t// @method formattedNumber(n, precision): string\n\t\t// Returns n in specified number format (if defined) and precision\n\t\tformattedNumber: function (n, precision) {\n\t\t\tvar formatted = parseFloat(n).toFixed(precision),\n\t\t\t\tformat = L.drawLocal.format && L.drawLocal.format.numeric,\n\t\t\t\tdelimiters = format && format.delimiters,\n\t\t\t\tthousands = delimiters && delimiters.thousands,\n\t\t\t\tdecimal = delimiters && delimiters.decimal;\n\n\t\t\tif (thousands || decimal) {\n\t\t\t\tvar splitValue = formatted.split('.');\n\t\t\t\tformatted = thousands ? splitValue[0].replace(/(\\d)(?=(\\d{3})+(?!\\d))/g, '$1' + thousands) : splitValue[0];\n\t\t\t\tdecimal = decimal || '.';\n\t\t\t\tif (splitValue.length > 1) {\n\t\t\t\t\tformatted = formatted + decimal + splitValue[1];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn formatted;\n\t\t},\n\n\t\t// @method readableArea(area, isMetric, precision): string\n\t\t// Returns a readable area string in yards or metric.\n\t\t// The value will be rounded as defined by the precision option object.\n\t\treadableArea: function (area, isMetric, precision) {\n\t\t\tvar areaStr,\n\t\t\t\tunits,\n\t\t\t\tprecision = L.Util.extend({}, defaultPrecision, precision);\n\n\t\t\tif (isMetric) {\n\t\t\t\tunits = ['ha', 'm'];\n\t\t\t\ttype = typeof isMetric;\n\t\t\t\tif (type === 'string') {\n\t\t\t\t\tunits = [isMetric];\n\t\t\t\t} else if (type !== 'boolean') {\n\t\t\t\t\tunits = isMetric;\n\t\t\t\t}\n\n\t\t\t\tif (area >= 1000000 && units.indexOf('km') !== -1) {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²';\n\t\t\t\t} else if (area >= 10000 && units.indexOf('ha') !== -1) {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha';\n\t\t\t\t} else {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²';\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tarea /= 0.836127; // Square yards in 1 meter\n\n\t\t\t\tif (area >= 3097600) { //3097600 square yards in 1 square mile\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²';\n\t\t\t\t} else if (area >= 4840) { //4840 square yards in 1 acre\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres';\n\t\t\t\t} else {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn areaStr;\n\t\t},\n\n\t\t// @method readableDistance(distance, units): string\n\t\t// Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string\n\t\t//\n\t\t// @alternative\n\t\t// @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string\n\t\t// Converts metric distance to distance string.\n\t\t// The value will be rounded as defined by the precision option object.\n\t\treadableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) {\n\t\t\tvar distanceStr,\n\t\t\t\tunits,\n\t\t\t\tprecision = L.Util.extend({}, defaultPrecision, precision);\n\n\t\t\tif (isMetric) {\n\t\t\t\tunits = typeof isMetric == 'string' ? isMetric : 'metric';\n\t\t\t} else if (isFeet) {\n\t\t\t\tunits = 'feet';\n\t\t\t} else if (isNauticalMile) {\n\t\t\t\tunits = 'nauticalMile';\n\t\t\t} else {\n\t\t\t\tunits = 'yards';\n\t\t\t}\n\n\t\t\tswitch (units) {\n\t\t\t\tcase 'metric':\n\t\t\t\t\t// show metres when distance is < 1km, then show km\n\t\t\t\t\tif (distance > 1000) {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'feet':\n\t\t\t\t\tdistance *= 1.09361 * 3;\n\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft';\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'nauticalMile':\n\t\t\t\t\tdistance *= 0.53996;\n\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'yards':\n\t\t\t\tdefault:\n\t\t\t\t\tdistance *= 1.09361;\n\n\t\t\t\t\tif (distance > 1760) {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn distanceStr;\n\t\t},\n\n\t\t// @method isVersion07x(): boolean\n\t\t// Returns true if the Leaflet version is 0.7.x, false otherwise.\n\t\tisVersion07x: function () {\n\t\t\tvar version = L.version.split('.');\n\t\t\t//If Version is == 0.7.*\n\t\t\treturn parseInt(version[0], 10) === 0 && parseInt(version[1], 10) === 7;\n\t\t},\n\t});\n\n})();\n","/**\n * @class L.LineUtil\n * @aka Util\n * @aka L.Utils\n */\nL.Util.extend(L.LineUtil, {\n\n\t// @method segmentsIntersect(): boolean\n\t// Checks to see if two line segments intersect. Does not handle degenerate cases.\n\t// http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf\n\tsegmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {\n\t\treturn this._checkCounterclockwise(p, p2, p3) !==\n\t\t\tthis._checkCounterclockwise(p1, p2, p3) &&\n\t\t\tthis._checkCounterclockwise(p, p1, p2) !==\n\t\t\tthis._checkCounterclockwise(p, p1, p3);\n\t},\n\n\t// check to see if points are in counterclockwise order\n\t_checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {\n\t\treturn (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);\n\t}\n});\n","/**\n * @class L.Polyline\n * @aka Polyline\n */\nL.Polyline.include({\n\n\t// @method intersects(): boolean\n\t// Check to see if this polyline has any linesegments that intersect.\n\t// NOTE: does not support detecting intersection for degenerate cases.\n\tintersects: function () {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tlen = points ? points.length : 0,\n\t\t\ti, p, p1;\n\n\t\tif (this._tooFewPointsForIntersection()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (i = len - 1; i >= 3; i--) {\n\t\t\tp = points[i - 1];\n\t\t\tp1 = points[i];\n\n\n\t\t\tif (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\t// @method newLatLngIntersects(): boolean\n\t// Check for intersection if new latlng was added to this polyline.\n\t// NOTE: does not support detecting intersection for degenerate cases.\n\tnewLatLngIntersects: function (latlng, skipFirst) {\n\t\t// Cannot check a polyline for intersecting lats/lngs when not added to the map\n\t\tif (!this._map) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);\n\t},\n\n\t// @method newPointIntersects(): boolean\n\t// Check for intersection if new point was added to this polyline.\n\t// newPoint must be a layer point.\n\t// NOTE: does not support detecting intersection for degenerate cases.\n\tnewPointIntersects: function (newPoint, skipFirst) {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tlen = points ? points.length : 0,\n\t\t\tlastPoint = points ? points[len - 1] : null,\n\t\t\t// The previous previous line segment. Previous line segment doesn't need testing.\n\t\t\tmaxIndex = len - 2;\n\n\t\tif (this._tooFewPointsForIntersection(1)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);\n\t},\n\n\t// Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).\n\t// Cannot have intersection when < 3 line segments (< 4 points)\n\t_tooFewPointsForIntersection: function (extraPoints) {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tlen = points ? points.length : 0;\n\t\t// Increment length by extraPoints if present\n\t\tlen += extraPoints || 0;\n\n\t\treturn !points || len <= 3;\n\t},\n\n\t// Checks a line segment intersections with any line segments before its predecessor.\n\t// Don't need to check the predecessor as will never intersect.\n\t_lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tp2, p3;\n\n\t\tminIndex = minIndex || 0;\n\n\t\t// Check all previous line segments (beside the immediately previous) for intersections\n\t\tfor (var j = maxIndex; j > minIndex; j--) {\n\t\t\tp2 = points[j - 1];\n\t\t\tp3 = points[j];\n\n\t\t\tif (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\t_getProjectedPoints: function () {\n\t\tif (!this._defaultShape) {\n\t\t\treturn this._originalPoints;\n\t\t}\n\t\tvar points = [],\n\t\t\t_shape = this._defaultShape();\n\n\t\tfor (var i = 0; i < _shape.length; i++) {\n\t\t\tpoints.push(this._map.latLngToLayerPoint(_shape[i]));\n\t\t}\n\t\treturn points;\n\t}\n});\n","/**\n * @class L.Polygon\n * @aka Polygon\n */\nL.Polygon.include({\n\n\t// @method intersects(): boolean\n\t// Checks a polygon for any intersecting line segments. Ignores holes.\n\tintersects: function () {\n\t\tvar polylineIntersects,\n\t\t\tpoints = this._getProjectedPoints(),\n\t\t\tlen, firstPoint, lastPoint, maxIndex;\n\n\t\tif (this._tooFewPointsForIntersection()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tpolylineIntersects = L.Polyline.prototype.intersects.call(this);\n\n\t\t// If already found an intersection don't need to check for any more.\n\t\tif (polylineIntersects) {\n\t\t\treturn true;\n\t\t}\n\n\t\tlen = points.length;\n\t\tfirstPoint = points[0];\n\t\tlastPoint = points[len - 1];\n\t\tmaxIndex = len - 2;\n\n\t\t// Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)\n\t\treturn this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);\n\t}\n});\n","/**\n * @class L.Control.Draw\n * @aka L.Draw\n */\nL.Control.Draw = L.Control.extend({\n\n\t// Options\n\toptions: {\n\t\tposition: 'topleft',\n\t\tdraw: {},\n\t\tedit: false\n\t},\n\n\t// @method initialize(): void\n\t// Initializes draw control, toolbars from the options\n\tinitialize: function (options) {\n\t\tif (L.version < '0.7') {\n\t\t\tthrow new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/');\n\t\t}\n\n\t\tL.Control.prototype.initialize.call(this, options);\n\n\t\tvar toolbar;\n\n\t\tthis._toolbars = {};\n\n\t\t// Initialize toolbars\n\t\tif (L.DrawToolbar && this.options.draw) {\n\t\t\ttoolbar = new L.DrawToolbar(this.options.draw);\n\n\t\t\tthis._toolbars[L.DrawToolbar.TYPE] = toolbar;\n\n\t\t\t// Listen for when toolbar is enabled\n\t\t\tthis._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);\n\t\t}\n\n\t\tif (L.EditToolbar && this.options.edit) {\n\t\t\ttoolbar = new L.EditToolbar(this.options.edit);\n\n\t\t\tthis._toolbars[L.EditToolbar.TYPE] = toolbar;\n\n\t\t\t// Listen for when toolbar is enabled\n\t\t\tthis._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);\n\t\t}\n\t\tL.toolbar = this; //set global var for editing the toolbar\n\t},\n\n\t// @method onAdd(): container\n\t// Adds the toolbar container to the map\n\tonAdd: function (map) {\n\t\tvar container = L.DomUtil.create('div', 'leaflet-draw'),\n\t\t\taddedTopClass = false,\n\t\t\ttopClassName = 'leaflet-draw-toolbar-top',\n\t\t\ttoolbarContainer;\n\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars.hasOwnProperty(toolbarId)) {\n\t\t\t\ttoolbarContainer = this._toolbars[toolbarId].addToolbar(map);\n\n\t\t\t\tif (toolbarContainer) {\n\t\t\t\t\t// Add class to the first toolbar to remove the margin\n\t\t\t\t\tif (!addedTopClass) {\n\t\t\t\t\t\tif (!L.DomUtil.hasClass(toolbarContainer, topClassName)) {\n\t\t\t\t\t\t\tL.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName);\n\t\t\t\t\t\t}\n\t\t\t\t\t\taddedTopClass = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tcontainer.appendChild(toolbarContainer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn container;\n\t},\n\n\t// @method onRemove(): void\n\t// Removes the toolbars from the map toolbar container\n\tonRemove: function () {\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars.hasOwnProperty(toolbarId)) {\n\t\t\t\tthis._toolbars[toolbarId].removeToolbar();\n\t\t\t}\n\t\t}\n\t},\n\n\t// @method setDrawingOptions(options): void\n\t// Sets options to all toolbar instances\n\tsetDrawingOptions: function (options) {\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars[toolbarId] instanceof L.DrawToolbar) {\n\t\t\t\tthis._toolbars[toolbarId].setOptions(options);\n\t\t\t}\n\t\t}\n\t},\n\n\t_toolbarEnabled: function (e) {\n\t\tvar enabledToolbar = e.target;\n\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars[toolbarId] !== enabledToolbar) {\n\t\t\t\tthis._toolbars[toolbarId].disable();\n\t\t\t}\n\t\t}\n\t}\n});\n\nL.Map.mergeOptions({\n\tdrawControlTooltips: true,\n\tdrawControl: false\n});\n\nL.Map.addInitHook(function () {\n\tif (this.options.drawControl) {\n\t\tthis.drawControl = new L.Control.Draw();\n\t\tthis.addControl(this.drawControl);\n\t}\n});\n","/**\n * @class L.Draw.Toolbar\n * @aka Toolbar\n *\n * The toolbar class of the API — it is used to create the ui\n * This will be depreciated\n *\n * @example\n *\n * ```js\n * var toolbar = L.Toolbar();\n * toolbar.addToolbar(map);\n * ```\n *\n * ### Disabling a toolbar\n *\n * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false.\n *\n * ```js\n * var drawControl = new L.Control.Draw({\n * draw: false,\n * edit: {\n * featureGroup: editableLayers\n * }\n * });\n * ```\n *\n * ### Disabling a toolbar item\n *\n * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and\n * markers. It also turns off the ability to edit layers.\n *\n * ```js\n * var drawControl = new L.Control.Draw({\n * draw: {\n * polygon: false,\n * marker: false\n * },\n * edit: {\n * featureGroup: editableLayers,\n * edit: false\n * }\n * });\n * ```\n */\nL.Toolbar = L.Class.extend({\n\t// @section Methods for modifying the toolbar\n\n\t// @method initialize(options): void\n\t// Toolbar constructor\n\tinitialize: function (options) {\n\t\tL.setOptions(this, options);\n\n\t\tthis._modes = {};\n\t\tthis._actionButtons = [];\n\t\tthis._activeMode = null;\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.Toolbar.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.Toolbar.include(L.Mixin.Events);\n\t\t}\n\t},\n\n\t// @method enabled(): boolean\n\t// Gets a true/false of whether the toolbar is enabled\n\tenabled: function () {\n\t\treturn this._activeMode !== null;\n\t},\n\n\t// @method disable(): void\n\t// Disables the toolbar\n\tdisable: function () {\n\t\tif (!this.enabled()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._activeMode.handler.disable();\n\t},\n\n\t// @method addToolbar(map): L.DomUtil\n\t// Adds the toolbar to the map and returns the toolbar dom element\n\taddToolbar: function (map) {\n\t\tvar container = L.DomUtil.create('div', 'leaflet-draw-section'),\n\t\t\tbuttonIndex = 0,\n\t\t\tbuttonClassPrefix = this._toolbarClass || '',\n\t\t\tmodeHandlers = this.getModeHandlers(map),\n\t\t\ti;\n\n\t\tthis._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');\n\t\tthis._map = map;\n\n\t\tfor (i = 0; i < modeHandlers.length; i++) {\n\t\t\tif (modeHandlers[i].enabled) {\n\t\t\t\tthis._initModeHandler(\n\t\t\t\t\tmodeHandlers[i].handler,\n\t\t\t\t\tthis._toolbarContainer,\n\t\t\t\t\tbuttonIndex++,\n\t\t\t\t\tbuttonClassPrefix,\n\t\t\t\t\tmodeHandlers[i].title\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// if no buttons were added, do not add the toolbar\n\t\tif (!buttonIndex) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Save button index of the last button, -1 as we would have ++ after the last button\n\t\tthis._lastButtonIndex = --buttonIndex;\n\n\t\t// Create empty actions part of the toolbar\n\t\tthis._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions');\n\n\t\t// Add draw and cancel containers to the control container\n\t\tcontainer.appendChild(this._toolbarContainer);\n\t\tcontainer.appendChild(this._actionsContainer);\n\n\t\treturn container;\n\t},\n\n\t// @method removeToolbar(): void\n\t// Removes the toolbar and drops the handler event listeners\n\tremoveToolbar: function () {\n\t\t// Dispose each handler\n\t\tfor (var handlerId in this._modes) {\n\t\t\tif (this._modes.hasOwnProperty(handlerId)) {\n\t\t\t\t// Unbind handler button\n\t\t\t\tthis._disposeButton(\n\t\t\t\t\tthis._modes[handlerId].button,\n\t\t\t\t\tthis._modes[handlerId].handler.enable,\n\t\t\t\t\tthis._modes[handlerId].handler\n\t\t\t\t);\n\n\t\t\t\t// Make sure is disabled\n\t\t\t\tthis._modes[handlerId].handler.disable();\n\n\t\t\t\t// Unbind handler\n\t\t\t\tthis._modes[handlerId].handler\n\t\t\t\t\t.off('enabled', this._handlerActivated, this)\n\t\t\t\t\t.off('disabled', this._handlerDeactivated, this);\n\t\t\t}\n\t\t}\n\t\tthis._modes = {};\n\n\t\t// Dispose the actions toolbar\n\t\tfor (var i = 0, l = this._actionButtons.length; i < l; i++) {\n\t\t\tthis._disposeButton(\n\t\t\t\tthis._actionButtons[i].button,\n\t\t\t\tthis._actionButtons[i].callback,\n\t\t\t\tthis\n\t\t\t);\n\t\t}\n\t\tthis._actionButtons = [];\n\t\tthis._actionsContainer = null;\n\t},\n\n\t_initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) {\n\t\tvar type = handler.type;\n\n\t\tthis._modes[type] = {};\n\n\t\tthis._modes[type].handler = handler;\n\n\t\tthis._modes[type].button = this._createButton({\n\t\t\ttype: type,\n\t\t\ttitle: buttonTitle,\n\t\t\tclassName: classNamePredix + '-' + type,\n\t\t\tcontainer: container,\n\t\t\tcallback: this._modes[type].handler.enable,\n\t\t\tcontext: this._modes[type].handler\n\t\t});\n\n\t\tthis._modes[type].buttonIndex = buttonIndex;\n\n\t\tthis._modes[type].handler\n\t\t\t.on('enabled', this._handlerActivated, this)\n\t\t\t.on('disabled', this._handlerDeactivated, this);\n\t},\n\n\t/* Detect iOS based on browser User Agent, based on:\n\t * http://stackoverflow.com/a/9039885 */\n\t_detectIOS: function () {\n\t\tvar iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream);\n\t\treturn iOS;\n\t},\n\n\t_createButton: function (options) {\n\n\t\tvar link = L.DomUtil.create('a', options.className || '', options.container);\n\t\t// Screen reader tag\n\t\tvar sr = L.DomUtil.create('span', 'sr-only', options.container);\n\n\t\tlink.href = '#';\n\t\tlink.appendChild(sr);\n\n\t\tif (options.title) {\n\t\t\tlink.title = options.title;\n\t\t\tsr.innerHTML = options.title;\n\t\t}\n\n\t\tif (options.text) {\n\t\t\tlink.innerHTML = options.text;\n\t\t\tsr.innerHTML = options.text;\n\t\t}\n\n\t\t/* iOS does not use click events */\n\t\tvar buttonEvent = this._detectIOS() ? 'touchstart' : 'click';\n\n\t\tL.DomEvent\n\t\t\t.on(link, 'click', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'mousedown', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'dblclick', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'touchstart', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'click', L.DomEvent.preventDefault)\n\t\t\t.on(link, buttonEvent, options.callback, options.context);\n\n\t\treturn link;\n\t},\n\n\t_disposeButton: function (button, callback) {\n\t\t/* iOS does not use click events */\n\t\tvar buttonEvent = this._detectIOS() ? 'touchstart' : 'click';\n\n\t\tL.DomEvent\n\t\t\t.off(button, 'click', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'mousedown', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'dblclick', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'touchstart', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'click', L.DomEvent.preventDefault)\n\t\t\t.off(button, buttonEvent, callback);\n\t},\n\n\t_handlerActivated: function (e) {\n\t\t// Disable active mode (if present)\n\t\tthis.disable();\n\n\t\t// Cache new active feature\n\t\tthis._activeMode = this._modes[e.handler];\n\n\t\tL.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');\n\n\t\tthis._showActionsToolbar();\n\n\t\tthis.fire('enable');\n\t},\n\n\t_handlerDeactivated: function () {\n\t\tthis._hideActionsToolbar();\n\n\t\tL.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');\n\n\t\tthis._activeMode = null;\n\n\t\tthis.fire('disable');\n\t},\n\n\t_createActions: function (handler) {\n\t\tvar container = this._actionsContainer,\n\t\t\tbuttons = this.getActions(handler),\n\t\t\tl = buttons.length,\n\t\t\tli, di, dl, button;\n\n\t\t// Dispose the actions toolbar (todo: dispose only not used buttons)\n\t\tfor (di = 0, dl = this._actionButtons.length; di < dl; di++) {\n\t\t\tthis._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback);\n\t\t}\n\t\tthis._actionButtons = [];\n\n\t\t// Remove all old buttons\n\t\twhile (container.firstChild) {\n\t\t\tcontainer.removeChild(container.firstChild);\n\t\t}\n\n\t\tfor (var i = 0; i < l; i++) {\n\t\t\tif ('enabled' in buttons[i] && !buttons[i].enabled) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tli = L.DomUtil.create('li', '', container);\n\n\t\t\tbutton = this._createButton({\n\t\t\t\ttitle: buttons[i].title,\n\t\t\t\ttext: buttons[i].text,\n\t\t\t\tcontainer: li,\n\t\t\t\tcallback: buttons[i].callback,\n\t\t\t\tcontext: buttons[i].context\n\t\t\t});\n\n\t\t\tthis._actionButtons.push({\n\t\t\t\tbutton: button,\n\t\t\t\tcallback: buttons[i].callback\n\t\t\t});\n\t\t}\n\t},\n\n\t_showActionsToolbar: function () {\n\t\tvar buttonIndex = this._activeMode.buttonIndex,\n\t\t\tlastButtonIndex = this._lastButtonIndex,\n\t\t\ttoolbarPosition = this._activeMode.button.offsetTop - 1;\n\n\t\t// Recreate action buttons on every click\n\t\tthis._createActions(this._activeMode.handler);\n\n\t\t// Correctly position the cancel button\n\t\tthis._actionsContainer.style.top = toolbarPosition + 'px';\n\n\t\tif (buttonIndex === 0) {\n\t\t\tL.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');\n\t\t\tL.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top');\n\t\t}\n\n\t\tif (buttonIndex === lastButtonIndex) {\n\t\t\tL.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');\n\t\t\tL.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom');\n\t\t}\n\n\t\tthis._actionsContainer.style.display = 'block';\n\t\tthis._map.fire(L.Draw.Event.TOOLBAROPENED);\n\t},\n\n\t_hideActionsToolbar: function () {\n\t\tthis._actionsContainer.style.display = 'none';\n\n\t\tL.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');\n\t\tL.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');\n\t\tL.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top');\n\t\tL.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom');\n\t\tthis._map.fire(L.Draw.Event.TOOLBARCLOSED);\n\t}\n});\n","L.Draw = L.Draw || {};\n/**\n * @class L.Draw.Tooltip\n * @aka Tooltip\n *\n * The tooltip class — it is used to display the tooltip while drawing\n * This will be depreciated\n *\n * @example\n *\n * ```js\n * var tooltip = L.Draw.Tooltip();\n * ```\n *\n */\nL.Draw.Tooltip = L.Class.extend({\n\n\t// @section Methods for modifying draw state\n\n\t// @method initialize(map): void\n\t// Tooltip constructor\n\tinitialize: function (map) {\n\t\tthis._map = map;\n\t\tthis._popupPane = map._panes.popupPane;\n\t\tthis._visible = false;\n\n\t\tthis._container = map.options.drawControlTooltips ?\n\t\t\tL.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;\n\t\tthis._singleLineLabel = false;\n\n\t\tthis._map.on('mouseout', this._onMouseOut, this);\n\t},\n\n\t// @method dispose(): void\n\t// Remove Tooltip DOM and unbind events\n\tdispose: function () {\n\t\tthis._map.off('mouseout', this._onMouseOut, this);\n\n\t\tif (this._container) {\n\t\t\tthis._popupPane.removeChild(this._container);\n\t\t\tthis._container = null;\n\t\t}\n\t},\n\n\t// @method updateContent(labelText): this\n\t// Changes the tooltip text to string in function call\n\tupdateContent: function (labelText) {\n\t\tif (!this._container) {\n\t\t\treturn this;\n\t\t}\n\t\tlabelText.subtext = labelText.subtext || '';\n\n\t\t// update the vertical position (only if changed)\n\t\tif (labelText.subtext.length === 0 && !this._singleLineLabel) {\n\t\t\tL.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');\n\t\t\tthis._singleLineLabel = true;\n\t\t}\n\t\telse if (labelText.subtext.length > 0 && this._singleLineLabel) {\n\t\t\tL.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');\n\t\t\tthis._singleLineLabel = false;\n\t\t}\n\n\t\tthis._container.innerHTML =\n\t\t\t(labelText.subtext.length > 0 ?\n\t\t\t\t'' + labelText.subtext + '' + '
' : '') +\n\t\t\t'' + labelText.text + '';\n\n\t\tif (!labelText.text && !labelText.subtext) {\n\t\t\tthis._visible = false;\n\t\t\tthis._container.style.visibility = 'hidden';\n\t\t} else {\n\t\t\tthis._visible = true;\n\t\t\tthis._container.style.visibility = 'inherit';\n\t\t}\n\n\t\treturn this;\n\t},\n\n\t// @method updatePosition(latlng): this\n\t// Changes the location of the tooltip\n\tupdatePosition: function (latlng) {\n\t\tvar pos = this._map.latLngToLayerPoint(latlng),\n\t\t\ttooltipContainer = this._container;\n\n\t\tif (this._container) {\n\t\t\tif (this._visible) {\n\t\t\t\ttooltipContainer.style.visibility = 'inherit';\n\t\t\t}\n\t\t\tL.DomUtil.setPosition(tooltipContainer, pos);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\t// @method showAsError(): this\n\t// Applies error class to tooltip\n\tshowAsError: function () {\n\t\tif (this._container) {\n\t\t\tL.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');\n\t\t}\n\t\treturn this;\n\t},\n\n\t// @method removeError(): this\n\t// Removes the error class from the tooltip\n\tremoveError: function () {\n\t\tif (this._container) {\n\t\t\tL.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');\n\t\t}\n\t\treturn this;\n\t},\n\n\t_onMouseOut: function () {\n\t\tif (this._container) {\n\t\t\tthis._container.style.visibility = 'hidden';\n\t\t}\n\t}\n});\n","/**\n * @class L.DrawToolbar\n * @aka Toolbar\n */\nL.DrawToolbar = L.Toolbar.extend({\n\n\tstatics: {\n\t\tTYPE: 'draw'\n\t},\n\n\toptions: {\n\t\tpolyline: {},\n\t\tpolygon: {},\n\t\trectangle: {},\n\t\tcircle: {},\n\t\tmarker: {},\n\t\tcirclemarker: {}\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (options) {\n\t\t// Ensure that the options are merged correctly since L.extend is only shallow\n\t\tfor (var type in this.options) {\n\t\t\tif (this.options.hasOwnProperty(type)) {\n\t\t\t\tif (options[type]) {\n\t\t\t\t\toptions[type] = L.extend({}, this.options[type], options[type]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._toolbarClass = 'leaflet-draw-draw';\n\t\tL.Toolbar.prototype.initialize.call(this, options);\n\t},\n\n\t// @method getModeHandlers(): object\n\t// Get mode handlers information\n\tgetModeHandlers: function (map) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tenabled: this.options.polyline,\n\t\t\t\thandler: new L.Draw.Polyline(map, this.options.polyline),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.polyline\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.polygon,\n\t\t\t\thandler: new L.Draw.Polygon(map, this.options.polygon),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.polygon\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.rectangle,\n\t\t\t\thandler: new L.Draw.Rectangle(map, this.options.rectangle),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.rectangle\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.circle,\n\t\t\t\thandler: new L.Draw.Circle(map, this.options.circle),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.circle\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.marker,\n\t\t\t\thandler: new L.Draw.Marker(map, this.options.marker),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.marker\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.circlemarker,\n\t\t\t\thandler: new L.Draw.CircleMarker(map, this.options.circlemarker),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.circlemarker\n\t\t\t}\n\t\t];\n\t},\n\n\t// @method getActions(): object\n\t// Get action information\n\tgetActions: function (handler) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tenabled: handler.completeShape,\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.finish.title,\n\t\t\t\ttext: L.drawLocal.draw.toolbar.finish.text,\n\t\t\t\tcallback: handler.completeShape,\n\t\t\t\tcontext: handler\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: handler.deleteLastVertex,\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.undo.title,\n\t\t\t\ttext: L.drawLocal.draw.toolbar.undo.text,\n\t\t\t\tcallback: handler.deleteLastVertex,\n\t\t\t\tcontext: handler\n\t\t\t},\n\t\t\t{\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.actions.title,\n\t\t\t\ttext: L.drawLocal.draw.toolbar.actions.text,\n\t\t\t\tcallback: this.disable,\n\t\t\t\tcontext: this\n\t\t\t}\n\t\t];\n\t},\n\n\t// @method setOptions(): void\n\t// Sets the options to the toolbar\n\tsetOptions: function (options) {\n\t\tL.setOptions(this, options);\n\n\t\tfor (var type in this._modes) {\n\t\t\tif (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {\n\t\t\t\tthis._modes[type].handler.setOptions(options[type]);\n\t\t\t}\n\t\t}\n\t}\n});\n","/*L.Map.mergeOptions({\n editControl: true\n });*/\n/**\n * @class L.EditToolbar\n * @aka EditToolbar\n */\nL.EditToolbar = L.Toolbar.extend({\n\tstatics: {\n\t\tTYPE: 'edit'\n\t},\n\n\toptions: {\n\t\tedit: {\n\t\t\tselectedPathOptions: {\n\t\t\t\tdashArray: '10, 10',\n\n\t\t\t\tfill: true,\n\t\t\t\tfillColor: '#fe57a1',\n\t\t\t\tfillOpacity: 0.1,\n\n\t\t\t\t// Whether to user the existing layers color\n\t\t\t\tmaintainColor: false\n\t\t\t}\n\t\t},\n\t\tremove: {},\n\t\tpoly: null,\n\t\tfeatureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (options) {\n\t\t// Need to set this manually since null is an acceptable value here\n\t\tif (options.edit) {\n\t\t\tif (typeof options.edit.selectedPathOptions === 'undefined') {\n\t\t\t\toptions.edit.selectedPathOptions = this.options.edit.selectedPathOptions;\n\t\t\t}\n\t\t\toptions.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);\n\t\t}\n\n\t\tif (options.remove) {\n\t\t\toptions.remove = L.extend({}, this.options.remove, options.remove);\n\t\t}\n\n\t\tif (options.poly) {\n\t\t\toptions.poly = L.extend({}, this.options.poly, options.poly);\n\t\t}\n\n\t\tthis._toolbarClass = 'leaflet-draw-edit';\n\t\tL.Toolbar.prototype.initialize.call(this, options);\n\n\t\tthis._selectedFeatureCount = 0;\n\t},\n\n\t// @method getModeHandlers(): object\n\t// Get mode handlers information\n\tgetModeHandlers: function (map) {\n\t\tvar featureGroup = this.options.featureGroup;\n\t\treturn [\n\t\t\t{\n\t\t\t\tenabled: this.options.edit,\n\t\t\t\thandler: new L.EditToolbar.Edit(map, {\n\t\t\t\t\tfeatureGroup: featureGroup,\n\t\t\t\t\tselectedPathOptions: this.options.edit.selectedPathOptions,\n\t\t\t\t\tpoly: this.options.poly\n\t\t\t\t}),\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.buttons.edit\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.remove,\n\t\t\t\thandler: new L.EditToolbar.Delete(map, {\n\t\t\t\t\tfeatureGroup: featureGroup\n\t\t\t\t}),\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.buttons.remove\n\t\t\t}\n\t\t];\n\t},\n\n\t// @method getActions(): object\n\t// Get actions information\n\tgetActions: function (handler) {\n\t\tvar actions = [\n\t\t\t{\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.actions.save.title,\n\t\t\t\ttext: L.drawLocal.edit.toolbar.actions.save.text,\n\t\t\t\tcallback: this._save,\n\t\t\t\tcontext: this\n\t\t\t},\n\t\t\t{\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.actions.cancel.title,\n\t\t\t\ttext: L.drawLocal.edit.toolbar.actions.cancel.text,\n\t\t\t\tcallback: this.disable,\n\t\t\t\tcontext: this\n\t\t\t}\n\t\t];\n\n\t\tif (handler.removeAllLayers) {\n\t\t\tactions.push({\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.actions.clearAll.title,\n\t\t\t\ttext: L.drawLocal.edit.toolbar.actions.clearAll.text,\n\t\t\t\tcallback: this._clearAllLayers,\n\t\t\t\tcontext: this\n\t\t\t});\n\t\t}\n\n\t\treturn actions;\n\t},\n\n\t// @method addToolbar(map): L.DomUtil\n\t// Adds the toolbar to the map\n\taddToolbar: function (map) {\n\t\tvar container = L.Toolbar.prototype.addToolbar.call(this, map);\n\n\t\tthis._checkDisabled();\n\n\t\tthis.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);\n\n\t\treturn container;\n\t},\n\n\t// @method removeToolbar(): void\n\t// Removes the toolbar from the map\n\tremoveToolbar: function () {\n\t\tthis.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);\n\n\t\tL.Toolbar.prototype.removeToolbar.call(this);\n\t},\n\n\t// @method disable(): void\n\t// Disables the toolbar\n\tdisable: function () {\n\t\tif (!this.enabled()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._activeMode.handler.revertLayers();\n\n\t\tL.Toolbar.prototype.disable.call(this);\n\t},\n\n\t_save: function () {\n\t\tthis._activeMode.handler.save();\n\t\tif (this._activeMode) {\n\t\t\tthis._activeMode.handler.disable();\n\t\t}\n\t},\n\n\t_clearAllLayers: function () {\n\t\tthis._activeMode.handler.removeAllLayers();\n\t\tif (this._activeMode) {\n\t\t\tthis._activeMode.handler.disable();\n\t\t}\n\t},\n\n\t_checkDisabled: function () {\n\t\tvar featureGroup = this.options.featureGroup,\n\t\t\thasLayers = featureGroup.getLayers().length !== 0,\n\t\t\tbutton;\n\n\t\tif (this.options.edit) {\n\t\t\tbutton = this._modes[L.EditToolbar.Edit.TYPE].button;\n\n\t\t\tif (hasLayers) {\n\t\t\t\tL.DomUtil.removeClass(button, 'leaflet-disabled');\n\t\t\t} else {\n\t\t\t\tL.DomUtil.addClass(button, 'leaflet-disabled');\n\t\t\t}\n\n\t\t\tbutton.setAttribute(\n\t\t\t\t'title',\n\t\t\t\thasLayers ?\n\t\t\t\t\tL.drawLocal.edit.toolbar.buttons.edit\n\t\t\t\t\t: L.drawLocal.edit.toolbar.buttons.editDisabled\n\t\t\t);\n\t\t}\n\n\t\tif (this.options.remove) {\n\t\t\tbutton = this._modes[L.EditToolbar.Delete.TYPE].button;\n\n\t\t\tif (hasLayers) {\n\t\t\t\tL.DomUtil.removeClass(button, 'leaflet-disabled');\n\t\t\t} else {\n\t\t\t\tL.DomUtil.addClass(button, 'leaflet-disabled');\n\t\t\t}\n\n\t\t\tbutton.setAttribute(\n\t\t\t\t'title',\n\t\t\t\thasLayers ?\n\t\t\t\t\tL.drawLocal.edit.toolbar.buttons.remove\n\t\t\t\t\t: L.drawLocal.edit.toolbar.buttons.removeDisabled\n\t\t\t);\n\t\t}\n\t}\n});\n","/**\n * @class L.EditToolbar.Edit\n * @aka EditToolbar.Edit\n */\nL.EditToolbar.Edit = L.Handler.extend({\n\tstatics: {\n\t\tTYPE: 'edit'\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (map, options) {\n\t\tL.Handler.prototype.initialize.call(this, map);\n\n\t\tL.setOptions(this, options);\n\n\t\t// Store the selectable layer group for ease of access\n\t\tthis._featureGroup = options.featureGroup;\n\n\t\tif (!(this._featureGroup instanceof L.FeatureGroup)) {\n\t\t\tthrow new Error('options.featureGroup must be a L.FeatureGroup');\n\t\t}\n\n\t\tthis._uneditedLayerProps = {};\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.EditToolbar.Edit.TYPE;\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.EditToolbar.Edit.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.EditToolbar.Edit.include(L.Mixin.Events);\n\t\t}\n\t},\n\n\t// @method enable(): void\n\t// Enable the edit toolbar\n\tenable: function () {\n\t\tif (this._enabled || !this._hasAvailableLayers()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.fire('enabled', {handler: this.type});\n\t\t//this disable other handlers\n\n\t\tthis._map.fire(L.Draw.Event.EDITSTART, {handler: this.type});\n\t\t//allow drawLayer to be updated before beginning edition.\n\n\t\tL.Handler.prototype.enable.call(this);\n\t\tthis._featureGroup\n\t\t\t.on('layeradd', this._enableLayerEdit, this)\n\t\t\t.on('layerremove', this._disableLayerEdit, this);\n\t},\n\n\t// @method disable(): void\n\t// Disable the edit toolbar\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\t\tthis._featureGroup\n\t\t\t.off('layeradd', this._enableLayerEdit, this)\n\t\t\t.off('layerremove', this._disableLayerEdit, this);\n\t\tL.Handler.prototype.disable.call(this);\n\t\tthis._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type});\n\t\tthis.fire('disabled', {handler: this.type});\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks for this handler\n\taddHooks: function () {\n\t\tvar map = this._map;\n\n\t\tif (map) {\n\t\t\tmap.getContainer().focus();\n\n\t\t\tthis._featureGroup.eachLayer(this._enableLayerEdit, this);\n\n\t\t\tthis._tooltip = new L.Draw.Tooltip(this._map);\n\t\t\tthis._tooltip.updateContent({\n\t\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\t\tsubtext: L.drawLocal.edit.handlers.edit.tooltip.subtext\n\t\t\t});\n\n\t\t\t// Quickly access the tooltip to update for intersection checking\n\t\t\tmap._editTooltip = this._tooltip;\n\n\t\t\tthis._updateTooltip();\n\n\t\t\tthis._map\n\t\t\t\t.on('mousemove', this._onMouseMove, this)\n\t\t\t\t.on('touchmove', this._onMouseMove, this)\n\t\t\t\t.on('MSPointerMove', this._onMouseMove, this)\n\t\t\t\t.on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks for this handler\n\tremoveHooks: function () {\n\t\tif (this._map) {\n\t\t\t// Clean up selected layers.\n\t\t\tthis._featureGroup.eachLayer(this._disableLayerEdit, this);\n\n\t\t\t// Clear the backups of the original layers\n\t\t\tthis._uneditedLayerProps = {};\n\n\t\t\tthis._tooltip.dispose();\n\t\t\tthis._tooltip = null;\n\n\t\t\tthis._map\n\t\t\t\t.off('mousemove', this._onMouseMove, this)\n\t\t\t\t.off('touchmove', this._onMouseMove, this)\n\t\t\t\t.off('MSPointerMove', this._onMouseMove, this)\n\t\t\t\t.off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);\n\t\t}\n\t},\n\n\t// @method revertLayers(): void\n\t// Revert each layer's geometry changes\n\trevertLayers: function () {\n\t\tthis._featureGroup.eachLayer(function (layer) {\n\t\t\tthis._revertLayer(layer);\n\t\t}, this);\n\t},\n\n\t// @method save(): void\n\t// Save the layer geometries\n\tsave: function () {\n\t\tvar editedLayers = new L.LayerGroup();\n\t\tthis._featureGroup.eachLayer(function (layer) {\n\t\t\tif (layer.edited) {\n\t\t\t\teditedLayers.addLayer(layer);\n\t\t\t\tlayer.edited = false;\n\t\t\t}\n\t\t});\n\t\tthis._map.fire(L.Draw.Event.EDITED, {layers: editedLayers});\n\t},\n\n\t_backupLayer: function (layer) {\n\t\tvar id = L.Util.stamp(layer);\n\n\t\tif (!this._uneditedLayerProps[id]) {\n\t\t\t// Polyline, Polygon or Rectangle\n\t\t\tif (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {\n\t\t\t\tthis._uneditedLayerProps[id] = {\n\t\t\t\t\tlatlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())\n\t\t\t\t};\n\t\t\t} else if (layer instanceof L.Circle) {\n\t\t\t\tthis._uneditedLayerProps[id] = {\n\t\t\t\t\tlatlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),\n\t\t\t\t\tradius: layer.getRadius()\n\t\t\t\t};\n\t\t\t} else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker\n\t\t\t\tthis._uneditedLayerProps[id] = {\n\t\t\t\t\tlatlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t},\n\n\t_getTooltipText: function () {\n\t\treturn ({\n\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\tsubtext: L.drawLocal.edit.handlers.edit.tooltip.subtext\n\t\t});\n\t},\n\n\t_updateTooltip: function () {\n\t\tthis._tooltip.updateContent(this._getTooltipText());\n\t},\n\n\t_revertLayer: function (layer) {\n\t\tvar id = L.Util.stamp(layer);\n\t\tlayer.edited = false;\n\t\tif (this._uneditedLayerProps.hasOwnProperty(id)) {\n\t\t\t// Polyline, Polygon or Rectangle\n\t\t\tif (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {\n\t\t\t\tlayer.setLatLngs(this._uneditedLayerProps[id].latlngs);\n\t\t\t} else if (layer instanceof L.Circle) {\n\t\t\t\tlayer.setLatLng(this._uneditedLayerProps[id].latlng);\n\t\t\t\tlayer.setRadius(this._uneditedLayerProps[id].radius);\n\t\t\t} else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker\n\t\t\t\tlayer.setLatLng(this._uneditedLayerProps[id].latlng);\n\t\t\t}\n\n\t\t\tlayer.fire('revert-edited', {layer: layer});\n\t\t}\n\t},\n\n\t_enableLayerEdit: function (e) {\n\t\tvar layer = e.layer || e.target || e,\n\t\t\tpathOptions, poly;\n\n\t\t// Back up this layer (if haven't before)\n\t\tthis._backupLayer(layer);\n\n\t\tif (this.options.poly) {\n\t\t\tpoly = L.Util.extend({}, this.options.poly);\n\t\t\tlayer.options.poly = poly;\n\t\t}\n\n\t\t// Set different style for editing mode\n\t\tif (this.options.selectedPathOptions) {\n\t\t\tpathOptions = L.Util.extend({}, this.options.selectedPathOptions);\n\n\t\t\t// Use the existing color of the layer\n\t\t\tif (pathOptions.maintainColor) {\n\t\t\t\tpathOptions.color = layer.options.color;\n\t\t\t\tpathOptions.fillColor = layer.options.fillColor;\n\t\t\t}\n\n\t\t\tlayer.options.original = L.extend({}, layer.options);\n\t\t\tlayer.options.editing = pathOptions;\n\n\t\t}\n\n\t\tif (layer instanceof L.Marker) {\n\t\t\tif (layer.editing) {\n\t\t\t\tlayer.editing.enable();\n\t\t\t}\n\t\t\tlayer.dragging.enable();\n\t\t\tlayer\n\t\t\t\t.on('dragend', this._onMarkerDragEnd)\n\t\t\t\t// #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.\n\t\t\t\t.on('touchmove', this._onTouchMove, this)\n\t\t\t\t.on('MSPointerMove', this._onTouchMove, this)\n\t\t\t\t.on('touchend', this._onMarkerDragEnd, this)\n\t\t\t\t.on('MSPointerUp', this._onMarkerDragEnd, this);\n\t\t} else {\n\t\t\tlayer.editing.enable();\n\t\t}\n\t},\n\n\t_disableLayerEdit: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tlayer.edited = false;\n\t\tif (layer.editing) {\n\t\t\tlayer.editing.disable();\n\t\t}\n\n\t\tdelete layer.options.editing;\n\t\tdelete layer.options.original;\n\t\t// Reset layer styles to that of before select\n\t\tif (this._selectedPathOptions) {\n\t\t\tif (layer instanceof L.Marker) {\n\t\t\t\tthis._toggleMarkerHighlight(layer);\n\t\t\t} else {\n\t\t\t\t// reset the layer style to what is was before being selected\n\t\t\t\tlayer.setStyle(layer.options.previousOptions);\n\t\t\t\t// remove the cached options for the layer object\n\t\t\t\tdelete layer.options.previousOptions;\n\t\t\t}\n\t\t}\n\n\t\tif (layer instanceof L.Marker) {\n\t\t\tlayer.dragging.disable();\n\t\t\tlayer\n\t\t\t\t.off('dragend', this._onMarkerDragEnd, this)\n\t\t\t\t.off('touchmove', this._onTouchMove, this)\n\t\t\t\t.off('MSPointerMove', this._onTouchMove, this)\n\t\t\t\t.off('touchend', this._onMarkerDragEnd, this)\n\t\t\t\t.off('MSPointerUp', this._onMarkerDragEnd, this);\n\t\t} else {\n\t\t\tlayer.editing.disable();\n\t\t}\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tthis._tooltip.updatePosition(e.latlng);\n\t},\n\n\t_onMarkerDragEnd: function (e) {\n\t\tvar layer = e.target;\n\t\tlayer.edited = true;\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});\n\t},\n\n\t_onTouchMove: function (e) {\n\t\tvar touchEvent = e.originalEvent.changedTouches[0],\n\t\t\tlayerPoint = this._map.mouseEventToLayerPoint(touchEvent),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint);\n\t\te.target.setLatLng(latlng);\n\t},\n\n\t_hasAvailableLayers: function () {\n\t\treturn this._featureGroup.getLayers().length !== 0;\n\t}\n});\n","/**\n * @class L.EditToolbar.Delete\n * @aka EditToolbar.Delete\n */\nL.EditToolbar.Delete = L.Handler.extend({\n\tstatics: {\n\t\tTYPE: 'remove' // not delete as delete is reserved in js\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (map, options) {\n\t\tL.Handler.prototype.initialize.call(this, map);\n\n\t\tL.Util.setOptions(this, options);\n\n\t\t// Store the selectable layer group for ease of access\n\t\tthis._deletableLayers = this.options.featureGroup;\n\n\t\tif (!(this._deletableLayers instanceof L.FeatureGroup)) {\n\t\t\tthrow new Error('options.featureGroup must be a L.FeatureGroup');\n\t\t}\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.EditToolbar.Delete.TYPE;\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.EditToolbar.Delete.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.EditToolbar.Delete.include(L.Mixin.Events);\n\t\t}\n\n\t},\n\n\t// @method enable(): void\n\t// Enable the delete toolbar\n\tenable: function () {\n\t\tif (this._enabled || !this._hasAvailableLayers()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.fire('enabled', {handler: this.type});\n\n\t\tthis._map.fire(L.Draw.Event.DELETESTART, {handler: this.type});\n\n\t\tL.Handler.prototype.enable.call(this);\n\n\t\tthis._deletableLayers\n\t\t\t.on('layeradd', this._enableLayerDelete, this)\n\t\t\t.on('layerremove', this._disableLayerDelete, this);\n\t},\n\n\t// @method disable(): void\n\t// Disable the delete toolbar\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._deletableLayers\n\t\t\t.off('layeradd', this._enableLayerDelete, this)\n\t\t\t.off('layerremove', this._disableLayerDelete, this);\n\n\t\tL.Handler.prototype.disable.call(this);\n\n\t\tthis._map.fire(L.Draw.Event.DELETESTOP, {handler: this.type});\n\n\t\tthis.fire('disabled', {handler: this.type});\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tvar map = this._map;\n\n\t\tif (map) {\n\t\t\tmap.getContainer().focus();\n\n\t\t\tthis._deletableLayers.eachLayer(this._enableLayerDelete, this);\n\t\t\tthis._deletedLayers = new L.LayerGroup();\n\n\t\t\tthis._tooltip = new L.Draw.Tooltip(this._map);\n\t\t\tthis._tooltip.updateContent({text: L.drawLocal.edit.handlers.remove.tooltip.text});\n\n\t\t\tthis._map.on('mousemove', this._onMouseMove, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tif (this._map) {\n\t\t\tthis._deletableLayers.eachLayer(this._disableLayerDelete, this);\n\t\t\tthis._deletedLayers = null;\n\n\t\t\tthis._tooltip.dispose();\n\t\t\tthis._tooltip = null;\n\n\t\t\tthis._map.off('mousemove', this._onMouseMove, this);\n\t\t}\n\t},\n\n\t// @method revertLayers(): void\n\t// Revert the deleted layers back to their prior state.\n\trevertLayers: function () {\n\t\t// Iterate of the deleted layers and add them back into the featureGroup\n\t\tthis._deletedLayers.eachLayer(function (layer) {\n\t\t\tthis._deletableLayers.addLayer(layer);\n\t\t\tlayer.fire('revert-deleted', {layer: layer});\n\t\t}, this);\n\t},\n\n\t// @method save(): void\n\t// Save deleted layers\n\tsave: function () {\n\t\tthis._map.fire(L.Draw.Event.DELETED, {layers: this._deletedLayers});\n\t},\n\n\t// @method removeAllLayers(): void\n\t// Remove all delateable layers\n\tremoveAllLayers: function () {\n\t\t// Iterate of the delateable layers and add remove them\n\t\tthis._deletableLayers.eachLayer(function (layer) {\n\t\t\tthis._removeLayer({layer: layer});\n\t\t}, this);\n\t\tthis.save();\n\t},\n\n\t_enableLayerDelete: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tlayer.on('click', this._removeLayer, this);\n\t},\n\n\t_disableLayerDelete: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tlayer.off('click', this._removeLayer, this);\n\n\t\t// Remove from the deleted layers so we can't accidentally revert if the user presses cancel\n\t\tthis._deletedLayers.removeLayer(layer);\n\t},\n\n\t_removeLayer: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tthis._deletableLayers.removeLayer(layer);\n\n\t\tthis._deletedLayers.addLayer(layer);\n\n\t\tlayer.fire('deleted');\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tthis._tooltip.updatePosition(e.latlng);\n\t},\n\n\t_hasAvailableLayers: function () {\n\t\treturn this._deletableLayers.getLayers().length !== 0;\n\t}\n});\n"]} \ No newline at end of file diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.css b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.css new file mode 100644 index 00000000..a0194106 --- /dev/null +++ b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.css @@ -0,0 +1,10 @@ +.leaflet-draw-section{position:relative}.leaflet-draw-toolbar{margin-top:12px}.leaflet-draw-toolbar-top{margin-top:0}.leaflet-draw-toolbar-notop a:first-child{border-top-right-radius:0}.leaflet-draw-toolbar-nobottom a:last-child{border-bottom-right-radius:0}.leaflet-draw-toolbar a{background-image:url('images/spritesheet.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg');background-repeat:no-repeat;background-size:300px 30px;background-clip:padding-box}.leaflet-retina .leaflet-draw-toolbar a{background-image:url('images/spritesheet-2x.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg')} +.leaflet-draw a{display:block;text-align:center;text-decoration:none}.leaflet-draw a .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.leaflet-draw-actions{display:none;list-style:none;margin:0;padding:0;position:absolute;left:26px;top:0;white-space:nowrap}.leaflet-touch .leaflet-draw-actions{left:32px}.leaflet-right .leaflet-draw-actions{right:26px;left:auto}.leaflet-touch .leaflet-right .leaflet-draw-actions{right:32px;left:auto}.leaflet-draw-actions li{display:inline-block} +.leaflet-draw-actions li:first-child a{border-left:0}.leaflet-draw-actions li:last-child a{-webkit-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.leaflet-right .leaflet-draw-actions li:last-child a{-webkit-border-radius:0;border-radius:0}.leaflet-right .leaflet-draw-actions li:first-child a{-webkit-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.leaflet-draw-actions a{background-color:#919187;border-left:1px solid #AAA;color:#FFF;font:11px/19px "Helvetica Neue",Arial,Helvetica,sans-serif;line-height:28px;text-decoration:none;padding-left:10px;padding-right:10px;height:28px} +.leaflet-touch .leaflet-draw-actions a{font-size:12px;line-height:30px;height:30px}.leaflet-draw-actions-bottom{margin-top:0}.leaflet-draw-actions-top{margin-top:1px}.leaflet-draw-actions-top a,.leaflet-draw-actions-bottom a{height:27px;line-height:27px}.leaflet-draw-actions a:hover{background-color:#a0a098}.leaflet-draw-actions-top.leaflet-draw-actions-bottom a{height:26px;line-height:26px}.leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:-2px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:0 -1px} +.leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-31px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-29px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-62px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-60px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-92px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-90px -1px} +.leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-122px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-120px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-273px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-271px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-152px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-150px -1px} +.leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-182px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-180px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-212px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-210px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-242px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-240px -2px} +.leaflet-mouse-marker{background-color:#fff;cursor:crosshair}.leaflet-draw-tooltip{background:#363636;background:rgba(0,0,0,0.5);border:1px solid transparent;-webkit-border-radius:4px;border-radius:4px;color:#fff;font:12px/18px "Helvetica Neue",Arial,Helvetica,sans-serif;margin-left:20px;margin-top:-21px;padding:4px 8px;position:absolute;visibility:hidden;white-space:nowrap;z-index:6}.leaflet-draw-tooltip:before{border-right:6px solid black;border-right-color:rgba(0,0,0,0.5);border-top:6px solid transparent;border-bottom:6px solid transparent;content:"";position:absolute;top:7px;left:-7px} +.leaflet-error-draw-tooltip{background-color:#f2dede;border:1px solid #e6b6bd;color:#b94a48}.leaflet-error-draw-tooltip:before{border-right-color:#e6b6bd}.leaflet-draw-tooltip-single{margin-top:-12px}.leaflet-draw-tooltip-subtext{color:#f8d5e4}.leaflet-draw-guide-dash{font-size:1%;opacity:.6;position:absolute;width:5px;height:5px}.leaflet-edit-marker-selected{background-color:rgba(254,87,161,0.1);border:4px dashed rgba(254,87,161,0.6);-webkit-border-radius:4px;border-radius:4px;box-sizing:content-box} +.leaflet-edit-move{cursor:move}.leaflet-edit-resize{cursor:pointer}.leaflet-oldie .leaflet-draw-toolbar{border:1px solid #999} \ No newline at end of file diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.js b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.js new file mode 100644 index 00000000..28f9338a --- /dev/null +++ b/ckanext/spatial/public/js/vendor/leaflet.draw/0.4.14/leaflet.draw.js @@ -0,0 +1,10 @@ +/* + Leaflet.draw 0.4.14, a plugin that adds drawing and editing tools to Leaflet powered maps. + (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet + + https://github.com/Leaflet/Leaflet.draw + http://leafletjs.com + */ +!function(t,e,i){function o(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}L.drawVersion="0.4.14",L.Draw={},L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},finish:{title:"Finish drawing",text:"Finish"},undo:{title:"Delete last point drawn",text:"Delete last point"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker",circlemarker:"Draw a circlemarker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."},radius:"Radius"},circlemarker:{tooltip:{start:"Click map to place circle marker."}},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"Error: shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes",text:"Save"},cancel:{title:"Cancel editing, discards all changes",text:"Cancel"},clearAll:{title:"Clear all layers",text:"Clear All"}},buttons:{edit:"Edit layers",editDisabled:"No layers to edit",remove:"Delete layers",removeDisabled:"No layers to delete"}},handlers:{edit:{tooltip:{text:"Drag handles or markers to edit features.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove."}}}}},L.Draw.Event={},L.Draw.Event.CREATED="draw:created",L.Draw.Event.EDITED="draw:edited",L.Draw.Event.DELETED="draw:deleted",L.Draw.Event.DRAWSTART="draw:drawstart",L.Draw.Event.DRAWSTOP="draw:drawstop",L.Draw.Event.DRAWVERTEX="draw:drawvertex",L.Draw.Event.EDITSTART="draw:editstart",L.Draw.Event.EDITMOVE="draw:editmove",L.Draw.Event.EDITRESIZE="draw:editresize",L.Draw.Event.EDITVERTEX="draw:editvertex",L.Draw.Event.EDITSTOP="draw:editstop",L.Draw.Event.DELETESTART="draw:deletestart",L.Draw.Event.DELETESTOP="draw:deletestop",L.Draw.Event.TOOLBAROPENED="draw:toolbaropened",L.Draw.Event.TOOLBARCLOSED="draw:toolbarclosed",L.Draw.Event.MARKERCONTEXT="draw:markercontext",L.Draw=L.Draw||{},L.Draw.Feature=L.Handler.extend({initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.setOptions(this,e);var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.Draw.Feature.include(L.Evented.prototype):L.Draw.Feature.include(L.Mixin.Events)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DRAWSTART,{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DRAWSTOP,{layerType:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Draw.Tooltip(this._map),L.DomEvent.on(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.off(this._container,"keyup",this._cancelDrawing,this))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire(L.Draw.Event.CREATED,{layer:t,layerType:this.type})},_cancelDrawing:function(t){27===t.keyCode&&(this._map.fire("draw:canceled",{layerType:this.type}),this.disable())}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),guidelineDistance:20,maxGuideLineLength:4e3,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,feet:!0,nautic:!1,showLength:!0,zIndexOffset:2e3,factor:1,maxPoints:0},initialize:function(t,e){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("mouseout",this._onMouseOut,this).on("mousemove",this._onMouseMove,this).on("mousedown",this._onMouseDown,this).on("mouseup",this._onMouseUp,this).addTo(this._map),this._map.on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).on("zoomlevelschange",this._onZoomEnd,this).on("touchstart",this._onTouch,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("mousedown",this._onMouseDown,this).off("mouseout",this._onMouseOut,this).off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this).off("zoomlevelschange",this._onZoomEnd,this).off("zoomend",this._onZoomEnd,this).off("touchstart",this._onTouch,this).off("click",this._onTouch,this)},deleteLastVertex:function(){if(!(this._markers.length<=1)){var t=this._markers.pop(),e=this._poly,i=e.getLatLngs(),o=i.splice(-1,1)[0];this._poly.setLatLngs(i),this._markerGroup.removeLayer(t),e.getLatLngs().length<2&&this._map.removeLayer(e),this._vertexChanged(o,!1)}},addVertex:function(t){if(this._markers.length>=2&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t))return void this._showErrorTooltip();this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),this._vertexChanged(t,!0)},completeShape:function(){this._markers.length<=1||(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_finishShape:function(){var t=this._poly._defaultShape?this._poly._defaultShape():this._poly.getLatLngs(),e=this._poly.newLatLngIntersects(t[t.length-1]);if(!this.options.allowIntersection&&e||!this._shapeIsValid())return void this._showErrorTooltip();this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_shapeIsValid:function(){return!0},_onZoomEnd:function(){null!==this._markers&&this._updateGuide()},_onMouseMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent),i=this._map.layerPointToLatLng(e);this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_vertexChanged:function(t,e){this._map.fire(L.Draw.Event.DRAWVERTEX,{layers:this._markerGroup}),this._updateFinishHandler(),this._updateRunningMeasure(t,e),this._clearGuides(),this._updateTooltip()},_onMouseDown:function(t){if(!this._clickHandled&&!this._touchHandled&&!this._disableMarkers){this._onMouseMove(t),this._clickHandled=!0,this._disableNewMarkers();var e=t.originalEvent,i=e.clientX,o=e.clientY;this._startPoint.call(this,i,o)}},_startPoint:function(t,e){this._mouseDownOrigin=L.point(t,e)},_onMouseUp:function(t){var e=t.originalEvent,i=e.clientX,o=e.clientY;this._endPoint.call(this,i,o,t),this._clickHandled=null},_endPoint:function(e,i,o){if(this._mouseDownOrigin){var n=L.point(e,i).distanceTo(this._mouseDownOrigin),a=this._calculateFinishDistance(o.latlng);this.options.maxPoints>1&&this.options.maxPoints==this._markers.length+1?(this.addVertex(o.latlng),this._finishShape()):a<10&&L.Browser.touch?this._finishShape():Math.abs(n)<9*(t.devicePixelRatio||1)&&this.addVertex(o.latlng),this._enableNewMarkers()}this._mouseDownOrigin=null},_onTouch:function(t){var e,i,o=t.originalEvent;!o.touches||!o.touches[0]||this._clickHandled||this._touchHandled||this._disableMarkers||(e=o.touches[0].clientX,i=o.touches[0].clientY,this._disableNewMarkers(),this._touchHandled=!0,this._startPoint.call(this,e,i),this._endPoint.call(this,e,i,t),this._touchHandled=null),this._clickHandled=null},_onMouseOut:function(){this._tooltip&&this._tooltip._onMouseOut.call(this._tooltip)},_calculateFinishDistance:function(t){var e;if(this._markers.length>0){var i;if(this.type===L.Draw.Polyline.TYPE)i=this._markers[this._markers.length-1];else{if(this.type!==L.Draw.Polygon.TYPE)return 1/0;i=this._markers[0]}var o=this._map.latLngToContainerPoint(i.getLatLng()),n=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset}),a=this._map.latLngToContainerPoint(n.getLatLng());e=o.distanceTo(a)}else e=1/0;return e},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers?this._markers.length:0;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,n,a=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))),s=this.options.guidelineDistance,r=this.options.maxGuideLineLength,l=a>r?a-r:s;for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));l1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,showLength:!1,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0,feet:!0,nautic:!1,precision:{}},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:this._markers.length<3?(t=L.drawLocal.draw.handlers.polygon.tooltip.cont,e=this._getMeasurementString()):(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area,e="";return t||this.options.showLength?(this.options.showLength&&(e=L.Draw.Polyline.prototype._getMeasurementString.call(this)),t&&(e+="
"+L.GeometryUtil.readableArea(t,this.options.metric,this.options.precision)),e):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexChanged:function(t,e){var i;!this.options.allowIntersection&&this.options.showArea&&(i=this._poly.getLatLngs(),this._area=L.GeometryUtil.geodesicArea(i)),L.Draw.Polyline.prototype._vertexChanged.call(this,t,e)},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._mapDraggable=this._map.dragging.enabled(),this._mapDraggable&&this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this).on("touchstart",this._onMouseDown,this).on("touchmove",this._onMouseMove,this),e.addEventListener("touchstart",L.DomEvent.preventDefault,{passive:!1}))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._mapDraggable&&this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this).off("touchstart",this._onMouseDown,this).off("touchmove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp,this),L.DomEvent.off(e,"touchend",this._onMouseUp,this),e.removeEventListener("touchstart",L.DomEvent.preventDefault),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_getTooltipText:function(){return{text:this._endLabelText}},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).on(e,"touchend",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent(this._getTooltipText()),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,showArea:!0,clickable:!0},metric:!0},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},disable:function(){this._enabled&&(this._isCurrentlyTwoClickDrawing=!1,L.Draw.SimpleShape.prototype.disable.call(this))},_onMouseUp:function(t){if(!this._shape&&!this._isCurrentlyTwoClickDrawing)return void(this._isCurrentlyTwoClickDrawing=!0);this._isCurrentlyTwoClickDrawing&&!o(t.target,"leaflet-pane")||L.Draw.SimpleShape.prototype._onMouseUp.call(this)},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_getTooltipText:function(){var t,e,i,o=L.Draw.SimpleShape.prototype._getTooltipText.call(this),n=this._shape,a=this.options.showArea;return n&&(t=this._shape._defaultShape?this._shape._defaultShape():this._shape.getLatLngs(),e=L.GeometryUtil.geodesicArea(t),i=a?L.GeometryUtil.readableArea(e,this.options.metric):""),{text:o.text,subtext:i}}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.marker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:this._initialLabelText}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onTouch,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._map.off("click",this._onClick,this).off("click",this._onTouch,this),this._marker&&(this._marker.off("click",this._onClick,this),this._map.removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?(e=this._mouseMarker.getLatLng(),this._marker.setLatLng(e)):(this._marker=this._createMarker(e),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_createMarker:function(t){return new L.Marker(t,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset})},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_onTouch:function(t){this._onMouseMove(t),this._onClick()},_fireCreatedEvent:function(){var t=new L.Marker.Touch(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.CircleMarker=L.Draw.Marker.extend({statics:{TYPE:"circlemarker"},options:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.CircleMarker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circlemarker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},_fireCreatedEvent:function(){var t=new L.CircleMarker(this._marker.getLatLng(),this.options);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)},_createMarker:function(t){return new L.CircleMarker(t,this.options)}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0,feet:!0,nautic:!1},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){if(L.GeometryUtil.isVersion07x())var e=this._startLatLng.distanceTo(t);else var e=this._map.distance(this._startLatLng,t);this._shape?this._shape.setRadius(e):(this._shape=new L.Circle(this._startLatLng,e,this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=this.options.showRadius,n=this.options.metric;if(this._tooltip.updatePosition(i),this._isDrawing){this._drawShape(i),e=this._shape.getRadius().toFixed(1);var a="";o&&(a=L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(e,n,this.options.feet,this.options.nautic)),this._tooltip.updateContent({text:this._endLabelText,subtext:a})}}}),L.Edit=L.Edit||{},L.Edit.Marker=L.Handler.extend({initialize:function(t,e){this._marker=t,L.setOptions(this,e)},addHooks:function(){var t=this._marker;t.dragging.enable(),t.on("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},removeHooks:function(){var t=this._marker;t.dragging.disable(),t.off("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},_onDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_toggleMarkerHighlight:function(){var t=this._marker._icon;t&&(t.style.display="none",L.DomUtil.hasClass(t,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,-4)):(L.DomUtil.addClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,4)),t.style.display="")},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"}}),L.Marker.addInitHook(function(){L.Edit.Marker&&(this.editing=new L.Edit.Marker(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({initialize:function(t){this.latlngs=[t._latlngs],t._holes&&(this.latlngs=this.latlngs.concat(t._holes)),this._poly=t,this._poly.on("revert-edited",this._updateLatLngs,this)},_defaultShape:function(){return L.Polyline._flat?L.Polyline._flat(this._poly._latlngs)?this._poly._latlngs:this._poly._latlngs[0]:this._poly._latlngs},_eachVertexHandler:function(t){for(var e=0;et&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,o,n,a=this._getMiddleLatLng(t,e),s=this._createMarker(a);s.setOpacity(.6),t._middleRight=e._middleLeft=s,o=function(){s.off("touchmove",o,this);var n=e._index;s._index=n,s.off("click",i,this).on("click",this._onMarkerClick,this),a.lat=s.getLatLng().lat,a.lng=s.getLatLng().lng,this._spliceLatLngs(n,0,a),this._markers.splice(n,0,s),s.setOpacity(1),this._updateIndexes(n,1),e._index++,this._updatePrevNext(t,s),this._updatePrevNext(s,e),this._poly.fire("editstart")},n=function(){s.off("dragstart",o,this),s.off("dragend",n,this),s.off("touchmove",o,this),this._createMiddleMarker(t,s),this._createMiddleMarker(s,e)},i=function(){o.call(this),n.call(this),this._fireEdit()},s.on("click",i,this).on("dragstart",o,this).on("dragend",n,this).on("touchmove",o,this),this._markerGroup.addLayer(s)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),n=i.project(e.getLatLng());return i.unproject(o._add(n)._divideBy(2))}}),L.Polyline.addInitHook(function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()}))}),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"}),touchMoveIcon:new L.DivIcon({ +iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon"}),touchResizeIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon"})},initialize:function(t,e){L.Browser.touch&&(this.options.moveIcon=this.options.touchMoveIcon,this.options.resizeIcon=this.options.touchResizeIcon),this._shape=t,L.Util.setOptions(this,e)},addHooks:function(){var t=this._shape;this._shape._map&&(this._map=this._shape._map,t.setStyle(t.options.editing),t._map&&(this._map=t._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup)))},removeHooks:function(){var t=this._shape;if(t.setStyle(t.options.original),t._map){this._unbindMarker(this._moveMarker);for(var e=0,i=this._resizeMarkers.length;e"+L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(radius,!0,this.options.feet,this.options.nautic)}),this._shape.setRadius(radius),this._map.fire(L.Draw.Event.EDITRESIZE,{layer:this._shape})}}),L.Circle.addInitHook(function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),L.Map.mergeOptions({touchExtend:!0}),L.Map.TouchExtend=L.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){L.DomEvent.on(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.on(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.on(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.on(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.on(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.on(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.on(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.on(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.on(this._container,"touchleave",this._onTouchLeave,this))},removeHooks:function(){L.DomEvent.off(this._container,"touchstart",this._onTouchStart),L.DomEvent.off(this._container,"touchend",this._onTouchEnd),L.DomEvent.off(this._container,"touchmove",this._onTouchMove),this._detectIE()?(L.DomEvent.off(this._container,"MSPointerDowm",this._onTouchStart),L.DomEvent.off(this._container,"MSPointerUp",this._onTouchEnd),L.DomEvent.off(this._container,"MSPointerMove",this._onTouchMove),L.DomEvent.off(this._container,"MSPointerCancel",this._onTouchCancel)):(L.DomEvent.off(this._container,"touchcancel",this._onTouchCancel),L.DomEvent.off(this._container,"touchleave",this._onTouchLeave))},_touchEvent:function(t,e){var i={};if(void 0!==t.touches){if(!t.touches.length)return;i=t.touches[0]}else{if("touch"!==t.pointerType)return;if(i=t,!this._filterClick(t))return}var o=this._map.mouseEventToContainerPoint(i),n=this._map.mouseEventToLayerPoint(i),a=this._map.layerPointToLatLng(n);this._map.fire(e,{latlng:a,layerPoint:n,containerPoint:o,pageX:i.pageX,pageY:i.pageY,originalEvent:t})},_filterClick:function(t){var e=t.timeStamp||t.originalEvent.timeStamp,i=L.DomEvent._lastClick&&e-L.DomEvent._lastClick;return i&&i>100&&i<500||t.target._simulatedClick&&!t._simulated?(L.DomEvent.stop(t),!1):(L.DomEvent._lastClick=e,!0)},_onTouchStart:function(t){if(this._map._loaded){this._touchEvent(t,"touchstart")}},_onTouchEnd:function(t){if(this._map._loaded){this._touchEvent(t,"touchend")}},_onTouchCancel:function(t){if(this._map._loaded){var e="touchcancel";this._detectIE()&&(e="pointercancel"),this._touchEvent(t,e)}},_onTouchLeave:function(t){if(this._map._loaded){this._touchEvent(t,"touchleave")}},_onTouchMove:function(t){if(this._map._loaded){this._touchEvent(t,"touchmove")}},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var n=e.indexOf("Edge/");return n>0&&parseInt(e.substring(n+5,e.indexOf(".",n)),10)}}),L.Map.addInitHook("addHandler","touchExtend",L.Map.TouchExtend),L.Marker.Touch=L.Marker.extend({_initInteraction:function(){return this.addInteractiveTarget?L.Marker.prototype._initInteraction.apply(this):this._initInteractionLegacy()},_initInteractionLegacy:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu","touchstart","touchend","touchmove"];this._detectIE?e.concat(["MSPointerDown","MSPointerUp","MSPointerMove","MSPointerCancel"]):e.concat(["touchcancel"]),L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this),L.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var n=e.indexOf("Edge/");return n>0&&parseInt(e.substring(n+5,e.indexOf(".",n)),10)}}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;i2){for(var s=0;s1&&(i=i+s+r[1])}return i},readableArea:function(e,i,o){var n,a,o=L.Util.extend({},t,o);return i?(a=["ha","m"],type=typeof i,"string"===type?a=[i]:"boolean"!==type&&(a=i),n=e>=1e6&&-1!==a.indexOf("km")?L.GeometryUtil.formattedNumber(1e-6*e,o.km)+" km²":e>=1e4&&-1!==a.indexOf("ha")?L.GeometryUtil.formattedNumber(1e-4*e,o.ha)+" ha":L.GeometryUtil.formattedNumber(e,o.m)+" m²"):(e/=.836127,n=e>=3097600?L.GeometryUtil.formattedNumber(e/3097600,o.mi)+" mi²":e>=4840?L.GeometryUtil.formattedNumber(e/4840,o.ac)+" acres":L.GeometryUtil.formattedNumber(e,o.yd)+" yd²"),n},readableDistance:function(e,i,o,n,a){var s,a=L.Util.extend({},t,a);switch(i?"string"==typeof i?i:"metric":o?"feet":n?"nauticalMile":"yards"){case"metric":s=e>1e3?L.GeometryUtil.formattedNumber(e/1e3,a.km)+" km":L.GeometryUtil.formattedNumber(e,a.m)+" m";break;case"feet":e*=3.28083,s=L.GeometryUtil.formattedNumber(e,a.ft)+" ft";break;case"nauticalMile":e*=.53996,s=L.GeometryUtil.formattedNumber(e/1e3,a.nm)+" nm";break;case"yards":default:e*=1.09361,s=e>1760?L.GeometryUtil.formattedNumber(e/1760,a.mi)+" miles":L.GeometryUtil.formattedNumber(e,a.yd)+" yd"}return s},isVersion07x:function(){var t=L.version.split(".");return 0===parseInt(t[0],10)&&7===parseInt(t[1],10)}})}(),L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._getProjectedPoints(),n=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=n-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return!!this._map&&this.newPointIntersects(this._map.latLngToLayerPoint(t),e)},newPointIntersects:function(t,e){var i=this._getProjectedPoints(),o=i?i.length:0,n=i?i[o-1]:null,a=o-2;return!this._tooFewPointsForIntersection(1)&&this._lineSegmentsIntersectsRange(n,t,a,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._getProjectedPoints(),i=e?e.length:0;return i+=t||0,!e||i<=3},_lineSegmentsIntersectsRange:function(t,e,i,o){var n,a,s=this._getProjectedPoints();o=o||0;for(var r=i;r>o;r--)if(n=s[r-1],a=s[r],L.LineUtil.segmentsIntersect(t,e,n,a))return!0;return!1},_getProjectedPoints:function(){if(!this._defaultShape)return this._originalPoints;for(var t=[],e=this._defaultShape(),i=0;i=2?L.Toolbar.include(L.Evented.prototype):L.Toolbar.include(L.Mixin.Events)},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},addToolbar:function(t){var e,i=L.DomUtil.create("div","leaflet-draw-section"),o=0,n=this._toolbarClass||"",a=this.getModeHandlers(t);for(this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,e=0;e0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?''+t.subtext+"
":"")+""+t.text+"",t.text||t.subtext?(this._visible=!0,this._container.style.visibility="inherit"):(this._visible=!1,this._container.style.visibility="hidden"),this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(this._visible&&(i.style.visibility="inherit"),L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this},_onMouseOut:function(){this._container&&(this._container.style.visibility="hidden")}}),L.DrawToolbar=L.Toolbar.extend({statics:{TYPE:"draw"},options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{},circlemarker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e]));this._toolbarClass="leaflet-draw-draw",L.Toolbar.prototype.initialize.call(this,t)},getModeHandlers:function(t){return[{enabled:this.options.polyline,handler:new L.Draw.Polyline(t,this.options.polyline),title:L.drawLocal.draw.toolbar.buttons.polyline},{enabled:this.options.polygon,handler:new L.Draw.Polygon(t,this.options.polygon),title:L.drawLocal.draw.toolbar.buttons.polygon},{enabled:this.options.rectangle,handler:new L.Draw.Rectangle(t,this.options.rectangle),title:L.drawLocal.draw.toolbar.buttons.rectangle},{enabled:this.options.circle,handler:new L.Draw.Circle(t,this.options.circle),title:L.drawLocal.draw.toolbar.buttons.circle},{enabled:this.options.marker,handler:new L.Draw.Marker(t,this.options.marker),title:L.drawLocal.draw.toolbar.buttons.marker},{enabled:this.options.circlemarker,handler:new L.Draw.CircleMarker(t,this.options.circlemarker),title:L.drawLocal.draw.toolbar.buttons.circlemarker}]},getActions:function(t){return[{enabled:t.completeShape,title:L.drawLocal.draw.toolbar.finish.title,text:L.drawLocal.draw.toolbar.finish.text,callback:t.completeShape,context:t},{enabled:t.deleteLastVertex,title:L.drawLocal.draw.toolbar.undo.title,text:L.drawLocal.draw.toolbar.undo.text,callback:t.deleteLastVertex,context:t},{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]},setOptions:function(t){L.setOptions(this,t);for(var e in this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({statics:{TYPE:"edit"},options:{edit:{selectedPathOptions:{dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1,maintainColor:!1}},remove:{},poly:null,featureGroup:null},initialize:function(t){t.edit&&(void 0===t.edit.selectedPathOptions&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit.selectedPathOptions=L.extend({},this.options.edit.selectedPathOptions,t.edit.selectedPathOptions)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),t.poly&&(t.poly=L.extend({},this.options.poly,t.poly)),this._toolbarClass="leaflet-draw-edit",L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},getModeHandlers:function(t){var e=this.options.featureGroup;return[{enabled:this.options.edit,handler:new L.EditToolbar.Edit(t,{featureGroup:e,selectedPathOptions:this.options.edit.selectedPathOptions,poly:this.options.poly}),title:L.drawLocal.edit.toolbar.buttons.edit},{enabled:this.options.remove,handler:new L.EditToolbar.Delete(t,{featureGroup:e}),title:L.drawLocal.edit.toolbar.buttons.remove}]},getActions:function(t){var e=[{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}];return t.removeAllLayers&&e.push({title:L.drawLocal.edit.toolbar.actions.clearAll.title,text:L.drawLocal.edit.toolbar.actions.clearAll.text,callback:this._clearAllLayers,context:this}),e},addToolbar:function(t){var e=L.Toolbar.prototype.addToolbar.call(this,t);return this._checkDisabled(),this.options.featureGroup.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this),L.Toolbar.prototype.removeToolbar.call(this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode&&this._activeMode.handler.disable()},_clearAllLayers:function(){this._activeMode.handler.removeAllLayers(),this._activeMode&&this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=this.options.featureGroup,i=0!==e.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.setOptions(this,e),this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Edit.include(L.Evented.prototype):L.EditToolbar.Edit.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.EDITSTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this))},disable:function(){this._enabled&&(this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.EDITSTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),t._editTooltip=this._tooltip,this._updateTooltip(),this._map.on("mousemove",this._onMouseMove,this).on("touchmove",this._onMouseMove,this).on("MSPointerMove",this._onMouseMove,this).on(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this).off("touchmove",this._onMouseMove,this).off("MSPointerMove",this._onMouseMove,this).off(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},revertLayers:function(){this._featureGroup.eachLayer(function(t){this._revertLayer(t)},this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer(function(e){e.edited&&(t.addLayer(e),e.edited=!1)}),this._map.fire(L.Draw.Event.EDITED,{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?this._uneditedLayerProps[e]={latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:(t instanceof L.Marker||t instanceof L.CircleMarker)&&(this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())}))},_getTooltipText:function(){return{text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}},_updateTooltip:function(){this._tooltip.updateContent(this._getTooltipText())},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):(t instanceof L.Marker||t instanceof L.CircleMarker)&&t.setLatLng(this._uneditedLayerProps[e].latlng),t.fire("revert-edited",{layer:t}))},_enableLayerEdit:function(t){var e,i,o=t.layer||t.target||t;this._backupLayer(o),this.options.poly&&(i=L.Util.extend({},this.options.poly),o.options.poly=i),this.options.selectedPathOptions&&(e=L.Util.extend({},this.options.selectedPathOptions),e.maintainColor&&(e.color=o.options.color,e.fillColor=o.options.fillColor),o.options.original=L.extend({},o.options),o.options.editing=e),o instanceof L.Marker?(o.editing&&o.editing.enable(),o.dragging.enable(),o.on("dragend",this._onMarkerDragEnd).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onMarkerDragEnd,this).on("MSPointerUp",this._onMarkerDragEnd,this)):o.editing.enable()},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,e.editing&&e.editing.disable(),delete e.options.editing,delete e.options.original, +this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onMarkerDragEnd,this).off("MSPointerUp",this._onMarkerDragEnd,this)):e.editing.disable()},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_onMarkerDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_onTouchMove:function(t){var e=t.originalEvent.changedTouches[0],i=this._map.mouseEventToLayerPoint(e),o=this._map.layerPointToLatLng(i);t.target.setLatLng(o)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}),L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Delete.include(L.Evented.prototype):L.EditToolbar.Delete.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DELETESTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this))},disable:function(){this._enabled&&(this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DELETESTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.LayerGroup,this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer(function(t){this._deletableLayers.addLayer(t),t.fire("revert-deleted",{layer:t})},this)},save:function(){this._map.fire(L.Draw.Event.DELETED,{layers:this._deletedLayers})},removeAllLayers:function(){this._deletableLayers.eachLayer(function(t){this._removeLayer({layer:t})},this),this.save()},_enableLayerDelete:function(t){(t.layer||t.target||t).on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e),e.fire("deleted")},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})}(window,document); \ No newline at end of file diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/README.md b/ckanext/spatial/public/js/vendor/leaflet.draw/README.md deleted file mode 100644 index d7c61b31..00000000 --- a/ckanext/spatial/public/js/vendor/leaflet.draw/README.md +++ /dev/null @@ -1,512 +0,0 @@ - -[![Build Status](https://travis-ci.org/Leaflet/Leaflet.draw.svg?branch=master)](https://travis-ci.org/Leaflet/Leaflet.draw) - - Leaflet.draw: [![Join the chat at https://gitter.im/Leaflet/Leaflet.draw](https://badges.gitter.im/Leaflet/Leaflet.draw.svg)](https://gitter.im/Leaflet/Leaflet.draw?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - - Leaflet: [![Gitter](https://badges.gitter.im/Leaflet/Leaflet.svg)](https://gitter.im/Leaflet/Leaflet?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -# Important -Leaflet.draw 0.2.3+ requires [Leaflet 0.7.x](https://github.com/Leaflet/Leaflet/releases). - -Support for Leaflet 1.0 is in development at the [leaflet-master](https://github.com/Leaflet/Leaflet.draw/tree/leaflet-master) branch. - -# Leaflet.draw -Adds support for drawing and editing vectors and markers on [Leaflet maps](https://github.com/Leaflet/Leaflet). Check out the [demo](http://leaflet.github.com/Leaflet.draw/). - -#### Upgrading from Leaflet.draw 0.1 - -Leaflet.draw 0.2.0 changes a LOT of things from 0.1. Please see [BREAKING CHANGES](https://github.com/Leaflet/Leaflet.draw/blob/master/BREAKINGCHANGES.md) for how to upgrade. - -## Table of Contents -[Install](#install) -[Using the plugin](#using) -[Advanced Options](#options) -[Common tasks](#commontasks) -[Thanks](#thanks) - - -## Install - -To install the plugin run `npm install leaflet-draw` via command line in your project. You must also require this in your project like so: `var leaflet-draw = require('leaflet-draw');` - - -## Using the plugin - -The default state for the control is the draw toolbar just below the zoom control. This will allow map users to draw vectors and markers. **Please note the edit toolbar is not enabled by default.** - -To add the draw toolbar set the option `drawControl: true` in the map options. - -````js -// create a map in the "map" div, set the view to a given place and zoom -var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13); - -// add an OpenStreetMap tile layer -L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' -}).addTo(map); -```` - -### Adding the edit toolbar - -To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map. - -````js -// create a map in the "map" div, set the view to a given place and zoom -var map = L.map('map').setView([51.505, -0.09], 13); - -// add an OpenStreetMap tile layer -L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' -}).addTo(map); - -// Initialise the FeatureGroup to store editable layers -var drawnItems = new L.FeatureGroup(); -map.addLayer(drawnItems); - -// Initialise the draw control and pass it the FeatureGroup of editable layers -var drawControl = new L.Control.Draw({ - edit: { - featureGroup: drawnItems - } -}); -map.addControl(drawControl); -```` - -The key here is the `featureGroup` option. This tells the plugin which `FeatureGroup` contains the layers that should be editable. The featureGroup can contain 0 or more features with geometry types `Point`, `LineString`, and `Polygon`. **Leaflet.draw does not work with multigeometry features such as `MultiPoint`, `MultiLineString`, `MultiPolygon`, or `GeometryCollection`.** If you need to add multigeometry features to the draw plugin, convert them to a FeatureCollection of non-multigeometries (`Point`s, `LineString`s, or `Polygon`s). - -### Events - -Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different actions users can initiate. The following events will be triggered on the map: - -#### draw:created - -| Property | Type | Description -| --- | --- | --- -| layer | [Polyline](http://leafletjs.com/reference.html#polyline)/[Polygon](http://leafletjs.com/reference.html#polygon)/[Rectangle](http://leafletjs.com/reference.html#rectangle)/[Circle](http://leafletjs.com/reference.html#circle)/[Marker](http://leafletjs.com/reference.html#marker) | Layer that was just created. -| layerType | String | The type of layer this is. One of: `polyline`, `polygon`, `rectangle`, `circle`, `marker` - - -Triggered when a new vector or marker has been created. - -````js -map.on('draw:created', function (e) { - var type = e.layerType, - layer = e.layer; - - if (type === 'marker') { - // Do marker specific actions - } - - // Do whatever else you need to. (save to db, add to map etc) - map.addLayer(layer); -}); -```` - -#### draw:edited - -| Property | Type | Description -| --- | --- | --- -| layers | [LayerGroup](http://leafletjs.com/reference.html#layergroup) | List of all layers just edited on the map. - -Triggered when layers in the FeatureGroup, initialised with the plugin, have been edited and saved. - -````js -map.on('draw:edited', function (e) { - var layers = e.layers; - layers.eachLayer(function (layer) { - //do whatever you want, most likely save back to db - }); -}); -```` - -#### draw:deleted - -Triggered when layers have been removed (and saved) from the FeatureGroup. - -| Property | Type | Description -| --- | --- | --- -| layers | [LayerGroup](http://leafletjs.com/reference.html#layergroup) | List of all layers just removed from the map. - -#### draw:drawstart - -Triggered when the user has chosen to draw a particular vector or marker. - -| Property | Type | Description -| --- | --- | --- -| layerType | String | The type of layer this is. One of: `polyline`, `polygon`, `rectangle`, `circle`, `marker` - -#### draw:drawstop - -Triggered when the user has finished a particular vector or marker. - -| Property | Type | Description -| --- | --- | --- -| layerType | String | The type of layer this is. One of: `polyline`, `polygon`, `rectangle`, `circle`, `marker` - -#### draw:drawvertex - -Triggered when a vertex is created on a polyline or polygon. - -| Property | Type | Description -| --- | --- | --- -| layers | [LayerGroup](http://leafletjs.com/reference.html#layergroup) | List of all layers just being added from the map. - -#### draw:editstart - -Triggered when the user starts edit mode by clicking the edit tool button. - -| Property | Type | Description -| --- | --- | --- -| handler | String | The type of edit this is. One of: `edit` - -#### draw:editmove - -Triggered as the user moves a rectangle, circle or marker. - -| Property | Type | Description -| --- | --- | --- -| layer | [ILayer](http://leafletjs.com/reference.html#ilayer) | Layer that was just moved. - -#### draw:editresize - -Triggered as the user resizes a rectangle or circle. - -| Property | Type | Description -| --- | --- | --- -| layer | [ILayer](http://leafletjs.com/reference.html#ilayer) | Layer that was just moved. - -#### draw:editvertex - -Triggered when a vertex is edited on a polyline or polygon. - -| Property | Type | Description -| --- | --- | --- -| layers | [LayerGroup](http://leafletjs.com/reference.html#layergroup) | List of all layers just being edited from the map. - -#### draw:editstop - -Triggered when the user has finshed editing (edit mode) and saves edits. - -| Property | Type | Description -| --- | --- | --- -| handler | String | The type of edit this is. One of: `edit` - -#### draw:deletestart - -Triggered when the user starts remove mode by clicking the remove tool button. - -| Property | Type | Description -| --- | --- | --- -| handler | String | The type of edit this is. One of: `remove` - -#### draw:deletestop - -Triggered when the user has finished removing shapes (remove mode) and saves. - -| Property | Type | Description -| --- | --- | --- -| handler | String | The type of edit this is. One of: `remove` - - -## Advanced options - -You can configure the plugin by using the different options listed here. - -### Control.Draw - -These options make up the root object that is used when initialising the Leaflet.draw control. - -| Option | Type | Default | Description -| --- | --- | --- | --- -| position | String | `'topleft'` | The initial position of the control (one of the map corners). See [control positions](http://leafletjs.com/reference.html#control-positions). -| draw | [DrawOptions](#drawoptions) | `{}` | The options used to configure the draw toolbar. -| edit | [EditOptions](#editoptions) | `false` | The options used to configure the edit toolbar. - - -### DrawOptions - -These options will allow you to configure the draw toolbar and its handlers. - -| Option | Type | Default | Description -| --- | --- | --- | --- -| polyline | [PolylineOptions](#polylineoptions) | `{ }` | Polyline draw handler options. Set to `false` to disable handler. -| polygon | [PolygonOptions](#polygonoptions) | `{ }` | Polygon draw handler options. Set to `false` to disable handler. -| rectangle | [RectangleOptions](#rectangleoptions) | `{ }` | Rectangle draw handler options. Set to `false` to disable handler. -| circle | [CircleOptions](#circleoptions) | `{ }` | Circle draw handler options. Set to `false` to disable handler. -| marker | [MarkerOptions](#markeroptions) | `{ }` | Marker draw handler options. Set to `false` to disable handler. - -### Draw handler options - -The following options will allow you to configure the individual draw handlers. - - -#### PolylineOptions - -Polyline and Polygon drawing handlers take the same options. - -| Option | Type | Default | Description -| --- | --- | --- | --- -| allowIntersection | Bool | `true` | Determines if line segments can cross. -| drawError | Object | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/draw/handler/Draw.Polyline.js#L10) | Configuration options for the error that displays if an intersection is detected. -| guidelineDistance | Number | `20` | Distance in pixels between each guide dash. -| shapeOptions | [Leaflet Polyline options](http://leafletjs.com/reference.html#polyline-options) | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/draw/handler/Draw.Polyline.js#L20) | The options used when drawing the polyline/polygon on the map. -| metric | Bool | `true` | Determines which measurement system (metric or imperial) is used. -| zIndexOffset | Number | `2000` | This should be a high number to ensure that you can draw over all other layers on the map. -| repeatMode | Bool | `false` | Determines if the draw tool remains enabled after drawing a shape. - - -#### PolygonOptions - -Polygon options include all of the Polyline options plus the option to show the approximate area. - -| Option | Type | Default | Description -| --- | --- | --- | --- -| showArea | Bool | `false` | Show the area of the drawn polygon in m², ha or km². **The area is only approximate and become less accurate the larger the polygon is.** - - -#### RectangleOptions - -| Option | Type | Default | Description -| --- | --- | --- | --- -| shapeOptions | [Leaflet Path options](http://leafletjs.com/reference.html#path-options) | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/draw/handler/Draw.Rectangle.js#L7) | The options used when drawing the rectangle on the map. -| repeatMode | Bool | `false` | Determines if the draw tool remains enabled after drawing a shape. - - -#### CircleOptions - -| Option | Type | Default | Description -| --- | --- | --- | --- -| shapeOptions | [Leaflet Path options](http://leafletjs.com/reference.html#path-options) | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/draw/handler/Draw.Circle.js#L7) | The options used when drawing the circle on the map. -| repeatMode | Bool | `false` | Determines if the draw tool remains enabled after drawing a shape. - - -#### MarkerOptions - -| Option | Type | Default | Description -| --- | --- | --- | --- -| icon | [Leaflet Icon](http://leafletjs.com/reference.html#icon) | `L.Icon.Default()` | The icon displayed when drawing a marker. -| zIndexOffset | Number | `2000` | This should be a high number to ensure that you can draw over all other layers on the map. -| repeatMode | Bool | `false` | Determines if the draw tool remains enabled after drawing a shape. - - -### EditPolyOptions - -These options will allow you to configure the draw toolbar and its handlers. - -| Option | Type | Default | Description -| --- | --- | --- | --- -| featureGroup | [Leaflet FeatureGroup](http://leafletjs.com/reference.html#featuregroup) | `null` | This is the FeatureGroup that stores all editable shapes. **THIS IS REQUIRED FOR THE EDIT TOOLBAR TO WORK** -| edit | [EditHandlerOptions](#edithandleroptions) | `{ }` | Edit handler options. Set to `false` to disable handler. -| remove | [DeleteHandlerOptions](#deletehandleroptions) | `{ }` | Delete handler options. Set to `false` to disable handler. -| poly | [EditPolyOptions](#editpoly) | `{ }` | Set polygon editing options - - -#### EditHandlerOptions - -| Option | Type | Default | Description -| --- | --- | --- | --- -| selectedPathOptions | [Leaflet Path options](http://leafletjs.com/reference.html#path-options) | [See code](https://github.com/Leaflet/Leaflet.draw/blob/master/src/edit/handler/EditToolbar.Edit.js#L9) | The path options for how the layers will look while in edit mode. If this is set to null the editable path options will not be set. - -**Note:** To maintain the original layer color of the layer use `maintainColor: true` within `selectedPathOptions`. - -E.g. The edit options below will maintain the layer color and set the edit opacity to 0.3. - -````js -{ - selectedPathOptions: { - maintainColor: true, - opacity: 0.3 - } -} -```` - - -#### DeleteHandlerOptions - -| Option | Type | Default | Description -| --- | --- | --- | --- - - - -#### EditPolyOptions - -| Option | Type | Default | Description -| --- | --- | --- | --- -| allowIntersection | Bool | `true` | Determines if line segments can cross. - - - -#### Customizing language and text in Leaflet.draw - -Leaflet.draw uses the `L.drawLocal` configuration object to set any text used in the plugin. Customizing this will allow support for changing the text or supporting another language. - -See [Leaflet.draw.js](https://github.com/Leaflet/Leaflet.draw/blob/master/src/Leaflet.draw.js) for the default strings. - -E.g. - -````js -// Set the button title text for the polygon button -L.drawLocal.draw.toolbar.buttons.polygon = 'Draw a sexy polygon!'; - -// Set the tooltip start text for the rectangle -L.drawLocal.draw.handlers.rectangle.tooltip.start = 'Not telling...'; -```` - - -## Common tasks - -The following examples outline some common tasks. - -### Example Leaflet.draw config - -The following example will show you how to: - -1. Change the position of the control's toolbar. -2. Customize the styles of a vector layer. -3. Use a custom marker. -4. Disable the delete functionality. - -````js -var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', - cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}), - map = new L.Map('map', {layers: [cloudmade], center: new L.LatLng(-37.7772, 175.2756), zoom: 15 }); - -var editableLayers = new L.FeatureGroup(); -map.addLayer(editableLayers); - -var MyCustomMarker = L.Icon.extend({ - options: { - shadowUrl: null, - iconAnchor: new L.Point(12, 12), - iconSize: new L.Point(24, 24), - iconUrl: 'link/to/image.png' - } -}); - -var options = { - position: 'topright', - draw: { - polyline: { - shapeOptions: { - color: '#f357a1', - weight: 10 - } - }, - polygon: { - allowIntersection: false, // Restricts shapes to simple polygons - drawError: { - color: '#e1e100', // Color the shape will turn when intersects - message: 'Oh snap! you can\'t draw that!' // Message that will show when intersect - }, - shapeOptions: { - color: '#bada55' - } - }, - circle: false, // Turns off this drawing tool - rectangle: { - shapeOptions: { - clickable: false - } - }, - marker: { - icon: new MyCustomMarker() - } - }, - edit: { - featureGroup: editableLayers, //REQUIRED!! - remove: false - } -}; - -var drawControl = new L.Control.Draw(options); -map.addControl(drawControl); - -map.on('draw:created', function (e) { - var type = e.layerType, - layer = e.layer; - - if (type === 'marker') { - layer.bindPopup('A popup!'); - } - - editableLayers.addLayer(layer); -}); -```` - -### Disabling a toolbar - -If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false. - -````js -var drawControl = new L.Control.Draw({ - draw: false, - edit: { - featureGroup: editableLayers - } -}); -```` - -### Disabling a toolbar item - -If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and markers. It also turns off the ability to edit layers. - -````js -var drawControl = new L.Control.Draw({ - draw: { - polygon: false, - marker: false - }, - edit: { - featureGroup: editableLayers, - edit: false - } -}); -```` - -### Changing a drawing handlers options - -You can change a draw handlers options after initialisation by using the `setDrawingOptions` method on the Leaflet.draw control. - -E.g. to change the colour of the rectangle: - -````js -drawControl.setDrawingOptions({ - rectangle: { - shapeOptions: { - color: '#0000FF' - } - } -}); -```` - -### Creating a custom build - -If you only require certain handlers (and not the UI), you may wish to create a custom build. You can generate the relevant jake command using the [build html file](https://github.com/Leaflet/Leaflet.draw/blob/master/build/build.html). - -See [edit handlers example](https://github.com/Leaflet/Leaflet.draw/blob/master/examples/edithandlers.html) which uses only the edit handlers. - - - -### Testing - -To test you can install the npm dependencies: - - npm install - -and then use: - - jake test - -## Thanks - -Touch friendly version of Leaflet.draw was created and maintained by Michael Guild (https://github.com/michaelguild13). - -The touch support was initiated due to a demand for it at National Geographic for their Map Maker Projected (http://mapmaker.education.nationalgeographic.com/) that was created by Michael Guild and Daniel Schep (https://github.com/dschep) - -Thanks so much to [@brunob](https://github.com/brunob), [@tnightingale](https://github.com/tnightingale), and [@shramov](https://github.com/shramov). I got a lot of ideas from their Leaflet plugins. - -All the [contributors](https://github.com/Leaflet/Leaflet.draw/graphs/contributors) and issue reporters of this plugin rock. Thanks for tidying up my mess and keeping the plugin on track. - -The icons used for some of the toolbar buttons are either from http://glyphicons.com/ or inspired by them. <3 Glyphicons! - -Finally, [@mourner](https://github.com/mourner) is the man! Thanks for dedicating so much of your time to create the gosh darn best JavaScript mapping library around. diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet-2x.png b/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet-2x.png deleted file mode 100644 index 1525c9f698ed277b39e7bb19a99309cf4f536a46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2078 zcmb7F`#%$k1D$BfH!~^k=PfCugrO!Mkq|<-^O!XY*ZVOu%siS+uT@?v3AxG0V+f0R z>}$2%Z!3?eEhE!fVa+2n&$`{u_b<5ThjY&7e9q@{emvKFyw52sX)6H$0A+VK7e4@C zhwd*A-39!`M_;2*zeFqbEG+e6Vq_{7nGykTx||puVdx%@jEeA!Kwi#B{$B(X08nJQ zyEyr$kAIyU@fCRNV<#sU$jU(<{lR`tujG4xrpKEBp>jTQH9I_auGvFf{K3>KhxOLd zl4N%dxT+T!zc*`(TK)Mt{U*5yX&)@5mYSRF{*@ z9~C=Baz!GPWJ6z1Z#8%d|56U{0k|NK#R`F+yQ&>SfRDHonqHPLNq!AYo)ki{90zlJDo3HY5-+U2Bs@KK%VhB$Uem6^uO?2h2S%GISVu8+_ z;!PaQbUf{>5T>G&K_(M#naJp20qv64a}143oZdIgLTW1%5C0tll-H^JNP3^OIFr;v z$IL+>Xk$!o68##34C%4Ho~iL}ww%;`UOB3{>HMSk5<_F@7HkCD;CaL7Jl5R8^j)4x z^;nMc)N6XH7mkVPw;d^%WR7>S6LJTY7HCbaxJlF8@2i0b#Fw=EED#4h-dWOYn@@JB zDmKK-hec%=YMV%JD|UkPNOPORVC$EwmCs}q+WJ?AE?;OCXD+dNvty!ZeJC$87Afg= zl(y@TlKcqr627N?0V4H`+rYQrVNr)p@}6lz@^Mf!H#0y49w3!&%q#NOhgPRG%8JHT zA{^qLYeIpKS=(b}U&r?L=AIvG_6^(d=Pp?9mRM)&#%XF^4O zd{{&B>GJ7pThpOjrkk)%t7RD_Ja)wFxhP+MfK)HEY-d)FesU~I7;VLz(7Q^g8cC5- zFqxrr>{R$6mkkM$DaF~hGKOW5@i7-izwpkS6L#pCWP>g$;^#?~PnVvs<3V|2uWmLH zZ{-T4O?`bdZmR+FB|-xNQvPbRzDMT_WTJoa=#0aUyspK4kiGE1voou2FFY!-h27|8 z9sJgV8@l_(lVi8&uubp-PZ-C2>cCS4&BJ7edFvWE`f>mnyGw@>qZ^0+X%wfR1_9Ti zhq&dMf1uwQ6>XAa6ZBB;tCUkDa#ne%RtP=fc$dW!MKHRq?j^w9$B39|-9@h_bI1@JEf}d{F1}SSfs;sB8VG z4H~RBO#aVrjE`Nl&Q8UeKUMWuY^?_G6(m7BWVfzrzOv;)vifhRQWwCJcMEFU)*b=< zryJf&ZcHmR#1F7{%KQG)#I^jqdy3Z4Vq^t2>%GK_P`P1}WT+7RdKtSBT>{}v^V@aJ zyzdW?K0aUC6&u=Jr(yG5=oM)fzFTJ}-9g%$Y`4=We@;wVEJp~0j1N2_*?fRSpwNS6 zd_>G565nm*Kq}9{e8Vx&T@ANUZNZ~T`nR$NfV*@QZXpZEXJ(#xZ5ixv4>Be^ zNgj75TOF9X#c=DadJ!_a*;LN$^hm7QrQ;jiV2ON$=&fL>qWOoVFD5fW^$o5m-+n+# zn$5^iAD}#Fuok3jSH4&z=4TqwZd!nk_1@92=~TBfZ63M1%`hDxoN7nMu2hli=DbI*4X3j-oWPj!`o=PN*bMpKv~p( zlK?>bLve1yjn9!PJt^rS6X2Q7N0bo$yHWMC}3ggipD8vsX|xW!=U#I}LWz z^QnfSrM*$QQ{ryc$jHb5k=UWl;GE*YDBK*|2h61)7W<2wvTJ?p)5iRGT5JEC+@%`% zqS6Z%SFIPtt0zaN=eCZlFYjOW`hszTixox$Zr%@j-VXa>c1Sz2W4PM({(;ce?St=s z;7(-eXX4Dn@faRw%x-a`JGUdS|JLnZ!E%QoTvY;b{k^pS#LL#o%6_}2uB3_$$L!~B0eY#os+2dKcS$ZPmw!~epF diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.png b/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.png deleted file mode 100644 index f7035a1cb0294a91eaedd98d2cbc0de6264eb4f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1056 zcmV+*1mF9KP)^cqr000000000000000000000001}0u3|)s5-;puuZqwcDub(csIZEHt~3&VXFw9 z=S}Dq3)Dexi9xvEV5Dhi)kbe9jqQL|8Ub3b#khy(tYgUY?(RiCn z90SpaZPZKS$*92+NJaP-|e z*+P3q)ZU8&?Y+?KZ}|`G;oSX4g2Bs{iwx>FbBYFUO+J?;8V0MJqEL$%hwPHfa}=E` zonPVhPJ3_Y*bT&*e94n9J5II0^Sn`xyiS$t&?+#f8zLZOiAMYt%w+x?$gzi^Bif*&Cl|&@@sNZ*q5>VKmx&adr9P98S#y8by=#s z#)GTmiNry=ug>qSKwm`SNz#GV|Is)RT2MTADN_28bJ>37$o``|vVLOJVC?)oIodlg z*~UutGtV_xUY7VI7_NL2W(Pime9pta^!fq-00000000000000000000000000H`3p a1Q-A)p~XwR_AF)q0000 diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.svg b/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.svg deleted file mode 100644 index 54237932..00000000 --- a/ckanext/spatial/public/js/vendor/leaflet.draw/images/spritesheet.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.js b/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.js deleted file mode 100644 index d5114d0a..00000000 --- a/ckanext/spatial/public/js/vendor/leaflet.draw/leaflet.draw.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - Leaflet.draw, a plugin that adds drawing and editing tools to Leaflet powered maps. - (c) 2012-2016, Jacob Toye, Smartrak, Leaflet - - https://github.com/Leaflet/Leaflet.draw - http://leafletjs.com -*/ -!function(t,e,i){L.drawVersion="0.3.0-dev",L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},finish:{title:"Finish drawing",text:"Finish"},undo:{title:"Delete last point drawn",text:"Delete last point"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."},radius:"Radius"},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"Error: shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes.",text:"Save"},cancel:{title:"Cancel editing, discards all changes.",text:"Cancel"}},buttons:{edit:"Edit layers.",editDisabled:"No layers to edit.",remove:"Delete layers.",removeDisabled:"No layers to delete."}},handlers:{edit:{tooltip:{text:"Drag handles, or marker to edit feature.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove"}}}}},L.Draw={},L.Draw.Feature=L.Handler.extend({includes:L.Mixin.Events,initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.setOptions(this,e)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire("draw:drawstart",{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._map.fire("draw:drawstop",{layerType:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Tooltip(this._map),L.DomEvent.on(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.off(this._container,"keyup",this._cancelDrawing,this))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire("draw:created",{layer:t,layerType:this.type})},_cancelDrawing:function(t){this._map.fire("draw:canceled",{layerType:this.type}),27===t.keyCode&&this.disable()}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),guidelineDistance:20,maxGuideLineLength:4e3,shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,feet:!0,showLength:!0,zIndexOffset:2e3},initialize:function(t,e){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),L.Browser.touch||this._map.on("mouseup",this._onMouseUp,this),this._mouseMarker.on("mousedown",this._onMouseDown,this).on("mouseout",this._onMouseOut,this).on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).addTo(this._map),this._map.on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).on("zoomlevelschange",this._onZoomEnd,this).on("click",this._onTouch,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("mousedown",this._onMouseDown,this).off("mouseout",this._onMouseOut,this).off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this).off("zoomlevelschange",this._onZoomEnd,this).off("zoomend",this._onZoomEnd,this).off("click",this._onTouch,this)},deleteLastVertex:function(){if(!(this._markers.length<=1)){var t=this._markers.pop(),e=this._poly,i=this._poly.spliceLatLngs(e.getLatLngs().length-1,1)[0];this._markerGroup.removeLayer(t),e.getLatLngs().length<2&&this._map.removeLayer(e),this._vertexChanged(i,!1)}},addVertex:function(t){var e=this._markers.length;return e>0&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t)?void this._showErrorTooltip():(this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),void this._vertexChanged(t,!0))},completeShape:function(){this._markers.length<=1||(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_finishShape:function(){var t=this._poly.newLatLngIntersects(this._poly.getLatLngs()[this._poly.getLatLngs().length-1]);return!this.options.allowIntersection&&t||!this._shapeIsValid()?void this._showErrorTooltip():(this._fireCreatedEvent(),this.disable(),void(this.options.repeatMode&&this.enable()))},_shapeIsValid:function(){return!0},_onZoomEnd:function(){null!==this._markers&&this._updateGuide()},_onMouseMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent),i=this._map.layerPointToLatLng(e);this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_vertexChanged:function(t,e){this._map.fire("draw:drawvertex",{layers:this._markerGroup}),this._updateFinishHandler(),this._updateRunningMeasure(t,e),this._clearGuides(),this._updateTooltip()},_onMouseDown:function(t){var e=t.originalEvent;this._mouseDownOrigin=L.point(e.clientX,e.clientY)},_onMouseUp:function(e){if(this._mouseDownOrigin){var i=L.point(e.originalEvent.clientX,e.originalEvent.clientY).distanceTo(this._mouseDownOrigin);Math.abs(i)<9*(t.devicePixelRatio||1)&&this.addVertex(e.latlng)}this._mouseDownOrigin=null},_onTouch:function(t){L.Browser.touch&&(this._onMouseDown(t),this._onMouseUp(t))},_onMouseOut:function(){this._tooltip&&this._tooltip._onMouseOut.call(this._tooltip)},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers?this._markers.length:0;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,n,s=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))),a=this.options.guidelineDistance,r=this.options.maxGuideLineLength,h=s>r?s-r:a;for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));s>h;h+=this.options.guidelineDistance)i=h/s,o={x:Math.floor(t.x*(1-i)+i*e.x),y:Math.floor(t.y*(1-i)+i*e.y)},n=L.DomUtil.create("div","leaflet-draw-guide-dash",this._guidesContainer),n.style.backgroundColor=this._errorShown?this.options.drawError.color:this.options.shapeOptions.color,L.DomUtil.setPosition(n,o)},_updateGuideColor:function(t){if(this._guidesContainer)for(var e=0,i=this._guidesContainer.childNodes.length;i>e;e++)this._guidesContainer.childNodes[e].style.backgroundColor=t},_clearGuides:function(){if(this._guidesContainer)for(;this._guidesContainer.firstChild;)this._guidesContainer.removeChild(this._guidesContainer.firstChild)},_getTooltipText:function(){var t,e,i=this.options.showLength;return 0===this._markers.length?t={text:L.drawLocal.draw.handlers.polyline.tooltip.start}:(e=i?this._getMeasurementString():"",t=1===this._markers.length?{text:L.drawLocal.draw.handlers.polyline.tooltip.cont,subtext:e}:{text:L.drawLocal.draw.handlers.polyline.tooltip.end,subtext:e}),t},_updateRunningMeasure:function(t,e){var i,o,n=this._markers.length;1===this._markers.length?this._measurementRunningTotal=0:(i=n-(e?2:1),o=t.distanceTo(this._markers[i].getLatLng()),this._measurementRunningTotal+=o*(e?1:-1))},_getMeasurementString:function(){var t,e=this._currentLatLng,i=this._markers[this._markers.length-1].getLatLng();return t=this._measurementRunningTotal+e.distanceTo(i),L.GeometryUtil.readableDistance(t,this.options.metric,this.options.feet)},_showErrorTooltip:function(){this._errorShown=!0,this._tooltip.showAsError().updateContent({text:this.options.drawError.message}),this._updateGuideColor(this.options.drawError.color),this._poly.setStyle({color:this.options.drawError.color}),this._clearHideErrorTimeout(),this._hideErrorTimeout=setTimeout(L.Util.bind(this._hideErrorTooltip,this),this.options.drawError.timeout)},_hideErrorTooltip:function(){this._errorShown=!1,this._clearHideErrorTimeout(),this._tooltip.removeError().updateContent(this._getTooltipText()),this._updateGuideColor(this.options.shapeOptions.color),this._poly.setStyle({color:this.options.shapeOptions.color})},_clearHideErrorTimeout:function(){this._hideErrorTimeout&&(clearTimeout(this._hideErrorTimeout),this._hideErrorTimeout=null)},_cleanUpShape:function(){this._markers.length>1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:this._markers.length<3?t=L.drawLocal.draw.handlers.polygon.tooltip.cont:(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area;return t?L.GeometryUtil.readableArea(t,this.options.metric):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexChanged:function(t,e){var i;!this.options.allowIntersection&&this.options.showArea&&(i=this._poly.getLatLngs(),this._area=L.GeometryUtil.geodesicArea(i)),L.Draw.Polyline.prototype._vertexChanged.call(this,t,e)},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._mapDraggable=this._map.dragging.enabled(),this._mapDraggable&&this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this).on("touchstart",this._onMouseDown,this).on("touchmove",this._onMouseMove,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._mapDraggable&&this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this).off("touchstart",this._onMouseDown,this).off("touchmove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp,this),L.DomEvent.off(e,"touchend",this._onMouseUp,this),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_getTooltipText:function(){return{text:this._endLabelText}},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).on(e,"touchend",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent(this._getTooltipText()),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_getTooltipText:function(){var t,e,i,o=L.Draw.SimpleShape.prototype._getTooltipText.call(this),n=this._shape;return n&&(t=this._shape.getLatLngs(),e=L.GeometryUtil.geodesicArea(t),i=L.GeometryUtil.readableArea(e,this.options.metric)),{text:o.text,subtext:i}}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0,feet:!0},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){this._shape?this._shape.setRadius(this._startLatLng.distanceTo(t)):(this._shape=new L.Circle(this._startLatLng,this._startLatLng.distanceTo(t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=this.options.showRadius,n=this.options.metric;this._tooltip.updatePosition(i),this._isDrawing&&(this._drawShape(i),e=this._shape.getRadius().toFixed(1),this._tooltip.updateContent({text:this._endLabelText,subtext:o?L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(e,n,this.options.feet):""}))}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:L.drawLocal.draw.handlers.marker.tooltip.start}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onTouch,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._marker&&(this._marker.off("click",this._onClick,this),this._map.off("click",this._onClick,this).off("click",this._onTouch,this).removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?(e=this._mouseMarker.getLatLng(),this._marker.setLatLng(e)):(this._marker=new L.Marker(e,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset}),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_onTouch:function(t){this._onMouseMove(t),this._onClick()},_fireCreatedEvent:function(){var t=new L.Marker.Touch(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Edit=L.Edit||{},L.Edit.Marker=L.Handler.extend({initialize:function(t,e){this._marker=t,L.setOptions(this,e)},addHooks:function(){var t=this._marker;t.dragging.enable(),t.on("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},removeHooks:function(){var t=this._marker;t.dragging.disable(),t.off("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},_onDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire("draw:editmove",{layer:e})},_toggleMarkerHighlight:function(){var t=this._marker._icon;t&&(t.style.display="none",L.DomUtil.hasClass(t,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,-4)):(L.DomUtil.addClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,4)),t.style.display="")},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"}}),L.Marker.addInitHook(function(){L.Edit.Marker&&(this.editing=new L.Edit.Marker(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({options:{},initialize:function(t,e){this.latlngs=[t._latlngs],t._holes&&(this.latlngs=this.latlngs.concat(t._holes)),this._poly=t,L.setOptions(this,e),this._poly.on("revert-edited",this._updateLatLngs,this)},_eachVertexHandler:function(t){for(var e=0;et;t++)o=this._createMarker(n[t],t),o.on("click",this._onMarkerClick,this),this._markers.push(o);var s,a;for(t=0,e=i-1;i>t;e=t++)(0!==t||L.Polygon&&this._poly instanceof L.Polygon)&&(s=this._markers[e],a=this._markers[t],this._createMiddleMarker(s,a),this._updatePrevNext(s,a))},_createMarker:function(t,e){var i=new L.Marker.Touch(t,{draggable:!0,icon:this.options.icon});return i._origLatLng=t,i._index=e,i.on("dragstart",this._onMarkerDragStart,this).on("drag",this._onMarkerDrag,this).on("dragend",this._fireEdit,this).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._fireEdit,this).on("MSPointerUp",this._fireEdit,this),this._markerGroup.addLayer(i),i},_onMarkerDragStart:function(){this._poly.fire("editstart")},_spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._poly._convertLatLngs(this._latlngs,!0),this._poly.redraw(),t},_removeMarker:function(t){var e=t._index;this._markerGroup.removeLayer(t),this._markers.splice(e,1),this._spliceLatLngs(e,1),this._updateIndexes(e,-1),t.off("dragstart",this._onMarkerDragStart,this).off("drag",this._onMarkerDrag,this).off("dragend",this._fireEdit,this).off("touchmove",this._onMarkerDrag,this).off("touchend",this._fireEdit,this).off("click",this._onMarkerClick,this).off("MSPointerMove",this._onTouchMove,this).off("MSPointerUp",this._fireEdit,this)},_fireEdit:function(){this._poly.edited=!0,this._poly.fire("edit"),this._poly._map.fire("draw:editvertex",{layers:this._markerGroup})},_onMarkerDrag:function(t){var e=t.target,i=this._poly;if(L.extend(e._origLatLng,e._latlng),e._middleLeft&&e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev,e)),e._middleRight&&e._middleRight.setLatLng(this._getMiddleLatLng(e,e._next)),i.options.poly){var o=i._map._editTooltip;if(!i.options.poly.allowIntersection&&i.intersects()){var n=i.options.color;i.setStyle({color:this.options.drawError.color}),o&&o.updateContent({text:L.drawLocal.draw.handlers.polyline.error}),setTimeout(function(){i.setStyle({color:n}),o&&o.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext})},1e3),this._onMarkerClick(t)}}this._poly.redraw(),this._poly.fire("editdrag")},_onMarkerClick:function(t){var e=L.Polygon&&this._poly instanceof L.Polygon?4:3,i=t.target;this._latlngs.lengtht&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,o,n,s=this._getMiddleLatLng(t,e),a=this._createMarker(s);a.setOpacity(.6),t._middleRight=e._middleLeft=a,o=function(){a.off("touchmove",o,this);var n=e._index;a._index=n,a.off("click",i,this).on("click",this._onMarkerClick,this),s.lat=a.getLatLng().lat,s.lng=a.getLatLng().lng,this._spliceLatLngs(n,0,s),this._markers.splice(n,0,a),a.setOpacity(1),this._updateIndexes(n,1),e._index++,this._updatePrevNext(t,a),this._updatePrevNext(a,e),this._poly.fire("editstart")},n=function(){a.off("dragstart",o,this),a.off("dragend",n,this),a.off("touchmove",o,this),this._createMiddleMarker(t,a),this._createMiddleMarker(a,e)},i=function(){o.call(this),n.call(this),this._fireEdit()},a.on("click",i,this).on("dragstart",o,this).on("dragend",n,this).on("touchmove",o,this),this._markerGroup.addLayer(a)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),n=i.project(e.getLatLng());return i.unproject(o._add(n)._divideBy(2))}}),L.Polyline.addInitHook(function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this,this.options.poly),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()}))}),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"}),touchMoveIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon"}),touchResizeIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon"})},initialize:function(t,e){L.Browser.touch&&(this.options.moveIcon=this.options.touchMoveIcon,this.options.resizeIcon=this.options.touchResizeIcon),this._shape=t,L.Util.setOptions(this,e)},addHooks:function(){var t=this._shape;this._shape._map&&(this._map=this._shape._map,t.setStyle(t.options.editing),t._map&&(this._map=t._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup)))},removeHooks:function(){var t=this._shape;if(t.setStyle(t.options.original),t._map){this._unbindMarker(this._moveMarker);for(var e=0,i=this._resizeMarkers.length;i>e;e++)this._unbindMarker(this._resizeMarkers[e]);this._resizeMarkers=null,this._map.removeLayer(this._markerGroup),delete this._markerGroup}this._map=null},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new L.LayerGroup),this._createMoveMarker(),this._createResizeMarker()},_createMoveMarker:function(){},_createResizeMarker:function(){},_createMarker:function(t,e){var i=new L.Marker.Touch(t,{draggable:!0,icon:e,zIndexOffset:10});return this._bindMarker(i),this._markerGroup.addLayer(i),i},_bindMarker:function(t){t.on("dragstart",this._onMarkerDragStart,this).on("drag",this._onMarkerDrag,this).on("dragend",this._onMarkerDragEnd,this).on("touchstart",this._onTouchStart,this).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onTouchEnd,this).on("MSPointerUp",this._onTouchEnd,this)},_unbindMarker:function(t){t.off("dragstart",this._onMarkerDragStart,this).off("drag",this._onMarkerDrag,this).off("dragend",this._onMarkerDragEnd,this).off("touchstart",this._onTouchStart,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onTouchEnd,this).off("MSPointerUp",this._onTouchEnd,this)},_onMarkerDragStart:function(t){var e=t.target;e.setOpacity(0),this._shape.fire("editstart")},_fireEdit:function(){this._shape.edited=!0,this._shape.fire("edit")},_onMarkerDrag:function(t){var e=t.target,i=e.getLatLng();e===this._moveMarker?this._move(i):this._resize(i),this._shape.redraw(),this._shape.fire("editdrag")},_onMarkerDragEnd:function(t){var e=t.target;e.setOpacity(1),this._fireEdit()},_onTouchStart:function(t){if(L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this,t),"function"==typeof this._getCorners){var e=this._getCorners(),i=t.target,o=i._cornerIndex;i.setOpacity(0),this._oppositeCorner=e[(o+2)%4],this._toggleCornerMarkers(0,o)}this._shape.fire("editstart")},_onTouchMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent.touches[0]),i=this._map.layerPointToLatLng(e),o=t.target;return o===this._moveMarker?this._move(i):this._resize(i),this._shape.redraw(),!1},_onTouchEnd:function(t){var e=t.target;e.setOpacity(1),this.updateMarkers(),this._fireEdit()},_move:function(){},_resize:function(){}}),L.Edit=L.Edit||{},L.Edit.Rectangle=L.Edit.SimpleShape.extend({_createMoveMarker:function(){var t=this._shape.getBounds(),e=t.getCenter();this._moveMarker=this._createMarker(e,this.options.moveIcon)},_createResizeMarker:function(){var t=this._getCorners();this._resizeMarkers=[];for(var e=0,i=t.length;i>e;e++)this._resizeMarkers.push(this._createMarker(t[e],this.options.resizeIcon)),this._resizeMarkers[e]._cornerIndex=e},_onMarkerDragStart:function(t){L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this,t);var e=this._getCorners(),i=t.target,o=i._cornerIndex;this._oppositeCorner=e[(o+2)%4],this._toggleCornerMarkers(0,o)},_onMarkerDragEnd:function(t){var e,i,o=t.target;o===this._moveMarker&&(e=this._shape.getBounds(),i=e.getCenter(),o.setLatLng(i)),this._toggleCornerMarkers(1),this._repositionCornerMarkers(),L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this,t)},_move:function(t){for(var e,i=this._shape.getLatLngs(),o=this._shape.getBounds(),n=o.getCenter(),s=[],a=0,r=i.length;r>a;a++)e=[i[a].lat-n.lat,i[a].lng-n.lng],s.push([t.lat+e[0],t.lng+e[1]]);this._shape.setLatLngs(s),this._repositionCornerMarkers(),this._map.fire("draw:editmove",{layer:this._shape})},_resize:function(t){var e;this._shape.setBounds(L.latLngBounds(t,this._oppositeCorner)),e=this._shape.getBounds(),this._moveMarker.setLatLng(e.getCenter()),this._map.fire("draw:editresize",{layer:this._shape})},_getCorners:function(){var t=this._shape.getBounds(),e=t.getNorthWest(),i=t.getNorthEast(),o=t.getSouthEast(),n=t.getSouthWest();return[e,i,o,n]},_toggleCornerMarkers:function(t){for(var e=0,i=this._resizeMarkers.length;i>e;e++)this._resizeMarkers[e].setOpacity(t)},_repositionCornerMarkers:function(){for(var t=this._getCorners(),e=0,i=this._resizeMarkers.length;i>e;e++)this._resizeMarkers[e].setLatLng(t[e])}}),L.Rectangle.addInitHook(function(){L.Edit.Rectangle&&(this.editing=new L.Edit.Rectangle(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.Circle=L.Edit.SimpleShape.extend({_createMoveMarker:function(){var t=this._shape.getLatLng();this._moveMarker=this._createMarker(t,this.options.moveIcon)},_createResizeMarker:function(){var t=this._shape.getLatLng(),e=this._getResizeMarkerPoint(t);this._resizeMarkers=[],this._resizeMarkers.push(this._createMarker(e,this.options.resizeIcon))},_getResizeMarkerPoint:function(t){var e=this._shape._radius*Math.cos(Math.PI/4),i=this._map.project(t);return this._map.unproject([i.x+e,i.y-e])},_move:function(t){var e=this._getResizeMarkerPoint(t);this._resizeMarkers[0].setLatLng(e),this._shape.setLatLng(t),this._map.fire("draw:editmove",{layer:this._shape})},_resize:function(t){ -var e=this._moveMarker.getLatLng(),i=e.distanceTo(t);this._shape.setRadius(i),this._map.fire("draw:editresize",{layer:this._shape})}}),L.Circle.addInitHook(function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),L.Map.mergeOptions({touchExtend:!0}),L.Map.TouchExtend=L.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){L.DomEvent.on(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.on(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.on(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.on(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.on(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.on(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.on(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.on(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.on(this._container,"touchleave",this._onTouchLeave,this))},removeHooks:function(){L.DomEvent.off(this._container,"touchstart",this._onTouchStart),L.DomEvent.off(this._container,"touchend",this._onTouchEnd),L.DomEvent.off(this._container,"touchmove",this._onTouchMove),this._detectIE()?(L.DomEvent.off(this._container,"MSPointerDowm",this._onTouchStart),L.DomEvent.off(this._container,"MSPointerUp",this._onTouchEnd),L.DomEvent.off(this._container,"MSPointerMove",this._onTouchMove),L.DomEvent.off(this._container,"MSPointerCancel",this._onTouchCancel)):(L.DomEvent.off(this._container,"touchcancel",this._onTouchCancel),L.DomEvent.off(this._container,"touchleave",this._onTouchLeave))},_touchEvent:function(t,e){var i={};if("undefined"!=typeof t.touches){if(!t.touches.length)return;i=t.touches[0]}else{if("touch"!==t.pointerType)return;if(i=t,!this._filterClick(t))return}var o=this._map.mouseEventToContainerPoint(i),n=this._map.mouseEventToLayerPoint(i),s=this._map.layerPointToLatLng(n);this._map.fire(e,{latlng:s,layerPoint:n,containerPoint:o,pageX:i.pageX,pageY:i.pageY,originalEvent:t})},_filterClick:function(t){var e=t.timeStamp||t.originalEvent.timeStamp,i=L.DomEvent._lastClick&&e-L.DomEvent._lastClick;return i&&i>100&&500>i||t.target._simulatedClick&&!t._simulated?(L.DomEvent.stop(t),!1):(L.DomEvent._lastClick=e,!0)},_onTouchStart:function(t){if(this._map._loaded){var e="touchstart";this._touchEvent(t,e)}},_onTouchEnd:function(t){if(this._map._loaded){var e="touchend";this._touchEvent(t,e)}},_onTouchCancel:function(t){if(this._map._loaded){var e="touchcancel";this._detectIE()&&(e="pointercancel"),this._touchEvent(t,e)}},_onTouchLeave:function(t){if(this._map._loaded){var e="touchleave";this._touchEvent(t,e)}},_onTouchMove:function(t){if(this._map._loaded){var e="touchmove";this._touchEvent(t,e)}},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);var o=e.indexOf("Trident/");if(o>0){var n=e.indexOf("rv:");return parseInt(e.substring(n+3,e.indexOf(".",n)),10)}var s=e.indexOf("Edge/");return s>0?parseInt(e.substring(s+5,e.indexOf(".",s)),10):!1}}),L.Map.addInitHook("addHandler","touchExtend",L.Map.TouchExtend),L.Marker.Touch=L.Marker.extend({_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu","touchstart","touchend","touchmove"];this._detectIE?e.concat(["MSPointerDown","MSPointerUp","MSPointerMove","MSPointerCancel"]):e.concat(["touchcancel"]),L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this),L.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);var o=e.indexOf("Trident/");if(o>0){var n=e.indexOf("rv:");return parseInt(e.substring(n+3,e.indexOf(".",n)),10)}var s=e.indexOf("Edge/");return s>0?parseInt(e.substring(s+5,e.indexOf(".",s)),10):!1}}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;o>i;i++)e.push(this.cloneLatLng(t[i]));return e},cloneLatLng:function(t){return L.latLng(t.lat,t.lng)}},L.GeometryUtil=L.extend(L.GeometryUtil||{},{geodesicArea:function(t){var e,i,o=t.length,n=0,s=L.LatLng.DEG_TO_RAD;if(o>2){for(var a=0;o>a;a++)e=t[a],i=t[(a+1)%o],n+=(i.lng-e.lng)*s*(2+Math.sin(e.lat*s)+Math.sin(i.lat*s));n=6378137*n*6378137/2}return Math.abs(n)},readableArea:function(t,e){var i;return e?i=t>=1e4?(1e-4*t).toFixed(2)+" ha":t.toFixed(2)+" m²":(t/=.836127,i=t>=3097600?(t/3097600).toFixed(2)+" mi²":t>=4840?(t/4840).toFixed(2)+" acres":Math.ceil(t)+" yd²"),i},readableDistance:function(t,e,i){var o;if(e)o=t>1e3?(t/1e3).toFixed(2)+" km":Math.ceil(t)+" m";else if(t*=1.09361,t>1760)o=(t/1760).toFixed(2)+" miles";else{var n=" yd";i&&(t=3*t,n=" ft"),o=Math.ceil(t)+n}return o}}),L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._originalPoints,n=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=n-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return this._map?this.newPointIntersects(this._map.latLngToLayerPoint(t),e):!1},newPointIntersects:function(t,e){var i=this._originalPoints,o=i?i.length:0,n=i?i[o-1]:null,s=o-2;return this._tooFewPointsForIntersection(1)?!1:this._lineSegmentsIntersectsRange(n,t,s,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._originalPoints,i=e?e.length:0;return i+=t||0,!this._originalPoints||3>=i},_lineSegmentsIntersectsRange:function(t,e,i,o){var n,s,a=this._originalPoints;o=o||0;for(var r=i;r>o;r--)if(n=a[r-1],s=a[r],L.LineUtil.segmentsIntersect(t,e,n,s))return!0;return!1}}),L.Polygon.include({intersects:function(){var t,e,i,o,n,s=this._originalPoints;return this._tooFewPointsForIntersection()?!1:(t=L.Polyline.prototype.intersects.call(this))?!0:(e=s.length,i=s[0],o=s[e-1],n=e-2,this._lineSegmentsIntersectsRange(o,i,n,1))}}),L.Control.Draw=L.Control.extend({options:{position:"topleft",draw:{},edit:!1},initialize:function(t){if(L.version<"0.7")throw new Error("Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/");L.Control.prototype.initialize.call(this,t);var e;this._toolbars={},L.DrawToolbar&&this.options.draw&&(e=new L.DrawToolbar(this.options.draw),this._toolbars[L.DrawToolbar.TYPE]=e,this._toolbars[L.DrawToolbar.TYPE].on("enable",this._toolbarEnabled,this)),L.EditToolbar&&this.options.edit&&(e=new L.EditToolbar(this.options.edit),this._toolbars[L.EditToolbar.TYPE]=e,this._toolbars[L.EditToolbar.TYPE].on("enable",this._toolbarEnabled,this)),L.toolbar=this},onAdd:function(t){var e,i=L.DomUtil.create("div","leaflet-draw"),o=!1,n="leaflet-draw-toolbar-top";for(var s in this._toolbars)this._toolbars.hasOwnProperty(s)&&(e=this._toolbars[s].addToolbar(t),e&&(o||(L.DomUtil.hasClass(e,n)||L.DomUtil.addClass(e.childNodes[0],n),o=!0),i.appendChild(e)));return i},onRemove:function(){for(var t in this._toolbars)this._toolbars.hasOwnProperty(t)&&this._toolbars[t].removeToolbar()},setDrawingOptions:function(t){for(var e in this._toolbars)this._toolbars[e]instanceof L.DrawToolbar&&this._toolbars[e].setOptions(t)},_toolbarEnabled:function(t){var e=t.target;for(var i in this._toolbars)this._toolbars[i]!==e&&this._toolbars[i].disable()}}),L.Map.mergeOptions({drawControlTooltips:!0,drawControl:!1}),L.Map.addInitHook(function(){this.options.drawControl&&(this.drawControl=new L.Control.Draw,this.addControl(this.drawControl))}),L.Toolbar=L.Class.extend({includes:[L.Mixin.Events],initialize:function(t){L.setOptions(this,t),this._modes={},this._actionButtons=[],this._activeMode=null},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},addToolbar:function(t){var e,i=L.DomUtil.create("div","leaflet-draw-section"),o=0,n=this._toolbarClass||"",s=this.getModeHandlers(t);for(this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,e=0;ee;e++)this._disposeButton(this._actionButtons[e].button,this._actionButtons[e].callback,this);this._actionButtons=[],this._actionsContainer=null},_initModeHandler:function(t,e,i,o,n){var s=t.type;this._modes[s]={},this._modes[s].handler=t,this._modes[s].button=this._createButton({type:s,title:n,className:o+"-"+s,container:e,callback:this._modes[s].handler.enable,context:this._modes[s].handler}),this._modes[s].buttonIndex=i,this._modes[s].handler.on("enabled",this._handlerActivated,this).on("disabled",this._handlerDeactivated,this)},_createButton:function(t){var e=L.DomUtil.create("a",t.className||"",t.container);return e.href="#",t.text&&(e.innerHTML=t.text),t.title&&(e.title=t.title),L.DomEvent.on(e,"click",L.DomEvent.stopPropagation).on(e,"mousedown",L.DomEvent.stopPropagation).on(e,"dblclick",L.DomEvent.stopPropagation).on(e,"click",L.DomEvent.preventDefault).on(e,"click",t.callback,t.context),e},_disposeButton:function(t,e){L.DomEvent.off(t,"click",L.DomEvent.stopPropagation).off(t,"mousedown",L.DomEvent.stopPropagation).off(t,"dblclick",L.DomEvent.stopPropagation).off(t,"click",L.DomEvent.preventDefault).off(t,"click",e)},_handlerActivated:function(t){this.disable(),this._activeMode=this._modes[t.handler],L.DomUtil.addClass(this._activeMode.button,"leaflet-draw-toolbar-button-enabled"),this._showActionsToolbar(),this.fire("enable")},_handlerDeactivated:function(){this._hideActionsToolbar(),L.DomUtil.removeClass(this._activeMode.button,"leaflet-draw-toolbar-button-enabled"),this._activeMode=null,this.fire("disable")},_createActions:function(t){var e,i,o,n,s=this._actionsContainer,a=this.getActions(t),r=a.length;for(i=0,o=this._actionButtons.length;o>i;i++)this._disposeButton(this._actionButtons[i].button,this._actionButtons[i].callback);for(this._actionButtons=[];s.firstChild;)s.removeChild(s.firstChild);for(var h=0;r>h;h++)"enabled"in a[h]&&!a[h].enabled||(e=L.DomUtil.create("li","",s),n=this._createButton({title:a[h].title,text:a[h].text,container:e,callback:a[h].callback,context:a[h].context}),this._actionButtons.push({button:n,callback:a[h].callback}))},_showActionsToolbar:function(){var t=this._activeMode.buttonIndex,e=this._lastButtonIndex,i=this._activeMode.button.offsetTop-1;this._createActions(this._activeMode.handler),this._actionsContainer.style.top=i+"px",0===t&&(L.DomUtil.addClass(this._toolbarContainer,"leaflet-draw-toolbar-notop"),L.DomUtil.addClass(this._actionsContainer,"leaflet-draw-actions-top")),t===e&&(L.DomUtil.addClass(this._toolbarContainer,"leaflet-draw-toolbar-nobottom"),L.DomUtil.addClass(this._actionsContainer,"leaflet-draw-actions-bottom")),this._actionsContainer.style.display="block"},_hideActionsToolbar:function(){this._actionsContainer.style.display="none",L.DomUtil.removeClass(this._toolbarContainer,"leaflet-draw-toolbar-notop"),L.DomUtil.removeClass(this._toolbarContainer,"leaflet-draw-toolbar-nobottom"),L.DomUtil.removeClass(this._actionsContainer,"leaflet-draw-actions-top"),L.DomUtil.removeClass(this._actionsContainer,"leaflet-draw-actions-bottom")}}),L.Tooltip=L.Class.extend({initialize:function(t){this._map=t,this._popupPane=t._panes.popupPane,this._container=t.options.drawControlTooltips?L.DomUtil.create("div","leaflet-draw-tooltip",this._popupPane):null,this._singleLineLabel=!1,this._map.on("mouseout",this._onMouseOut,this)},dispose:function(){this._map.off("mouseout",this._onMouseOut,this),this._container&&(this._popupPane.removeChild(this._container),this._container=null)},updateContent:function(t){return this._container?(t.subtext=t.subtext||"",0!==t.subtext.length||this._singleLineLabel?t.subtext.length>0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?''+t.subtext+"
":"")+""+t.text+"",this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(i.style.visibility="inherit",L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this},_onMouseOut:function(){this._container&&(this._container.style.visibility="hidden")}}),L.DrawToolbar=L.Toolbar.extend({statics:{TYPE:"draw"},options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e]));this._toolbarClass="leaflet-draw-draw",L.Toolbar.prototype.initialize.call(this,t)},getModeHandlers:function(t){return[{enabled:this.options.polyline,handler:new L.Draw.Polyline(t,this.options.polyline),title:L.drawLocal.draw.toolbar.buttons.polyline},{enabled:this.options.polygon,handler:new L.Draw.Polygon(t,this.options.polygon),title:L.drawLocal.draw.toolbar.buttons.polygon},{enabled:this.options.rectangle,handler:new L.Draw.Rectangle(t,this.options.rectangle),title:L.drawLocal.draw.toolbar.buttons.rectangle},{enabled:this.options.circle,handler:new L.Draw.Circle(t,this.options.circle),title:L.drawLocal.draw.toolbar.buttons.circle},{enabled:this.options.marker,handler:new L.Draw.Marker(t,this.options.marker),title:L.drawLocal.draw.toolbar.buttons.marker}]},getActions:function(t){return[{enabled:t.completeShape,title:L.drawLocal.draw.toolbar.finish.title,text:L.drawLocal.draw.toolbar.finish.text,callback:t.completeShape,context:t},{enabled:t.deleteLastVertex,title:L.drawLocal.draw.toolbar.undo.title,text:L.drawLocal.draw.toolbar.undo.text,callback:t.deleteLastVertex,context:t},{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]},setOptions:function(t){L.setOptions(this,t);for(var e in this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({statics:{TYPE:"edit"},options:{edit:{selectedPathOptions:{dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1,maintainColor:!1}},remove:{},poly:null,featureGroup:null},initialize:function(t){t.edit&&("undefined"==typeof t.edit.selectedPathOptions&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit.selectedPathOptions=L.extend({},this.options.edit.selectedPathOptions,t.edit.selectedPathOptions)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),t.poly&&(t.poly=L.extend({},this.options.poly,t.poly)),this._toolbarClass="leaflet-draw-edit",L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},getModeHandlers:function(t){var e=this.options.featureGroup;return[{enabled:this.options.edit,handler:new L.EditToolbar.Edit(t,{featureGroup:e,selectedPathOptions:this.options.edit.selectedPathOptions,poly:this.options.poly}),title:L.drawLocal.edit.toolbar.buttons.edit},{enabled:this.options.remove,handler:new L.EditToolbar.Delete(t,{featureGroup:e}),title:L.drawLocal.edit.toolbar.buttons.remove}]},getActions:function(){return[{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}]},addToolbar:function(t){var e=L.Toolbar.prototype.addToolbar.call(this,t);return this._checkDisabled(),this.options.featureGroup.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this),L.Toolbar.prototype.removeToolbar.call(this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode&&this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=this.options.featureGroup,i=0!==e.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},includes:L.Mixin.Events,initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.setOptions(this,e),this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire("draw:editstart",{handler:this.type}),L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this))},disable:function(){this._enabled&&(this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this),this._map.fire("draw:editstop",{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),t._editTooltip=this._tooltip,this._updateTooltip(),this._map.on("mousemove",this._onMouseMove,this).on("touchmove",this._onMouseMove,this).on("MSPointerMove",this._onMouseMove,this).on("click",this._editStyle,this).on("draw:editvertex",this._updateTooltip,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this).off("touchmove",this._onMouseMove,this).off("MSPointerMove",this._onMouseMove,this).off("click",this._editStyle,this).off("draw:editvertex",this._updateTooltip,this))},revertLayers:function(){this._featureGroup.eachLayer(function(t){this._revertLayer(t)},this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer(function(e){e.edited&&(t.addLayer(e),e.edited=!1)}),this._map.fire("draw:edited",{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?this._uneditedLayerProps[e]={latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:t instanceof L.Marker&&(this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())}))},_getTooltipText:function(){return{text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}},_updateTooltip:function(){this._tooltip.updateContent(this._getTooltipText())},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):t instanceof L.Marker&&t.setLatLng(this._uneditedLayerProps[e].latlng),t.fire("revert-edited",{layer:t}))},_enableLayerEdit:function(t){var e,i,o=t.layer||t.target||t;this._backupLayer(o),this.options.poly&&(i=L.Util.extend({},this.options.poly),o.options.poly=i),this.options.selectedPathOptions&&(e=L.Util.extend({},this.options.selectedPathOptions),e.maintainColor&&(e.color=o.options.color,e.fillColor=o.options.fillColor),o.options.original=L.extend({},o.options),o.options.editing=e),this.isMarker?(o.dragging.enable(),o.on("dragend",this._onMarkerDragEnd).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onMarkerDragEnd,this).on("MSPointerUp",this._onMarkerDragEnd,this)):o.editing.enable()},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,e.editing.disable(),delete e.options.editing,delete e.options.original,this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onMarkerDragEnd,this).off("MSPointerUp",this._onMarkerDragEnd,this)):e.editing.disable()},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_onTouchMove:function(t){var e=t.originalEvent.changedTouches[0],i=this._map.mouseEventToLayerPoint(e),o=this._map.layerPointToLatLng(i);t.target.setLatLng(o)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}),L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},includes:L.Mixin.Events,initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire("draw:deletestart",{handler:this.type}),L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this))},disable:function(){this._enabled&&(this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),L.Handler.prototype.disable.call(this),this._map.fire("draw:deletestop",{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.LayerGroup,this._tooltip=new L.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer(function(t){this._deletableLayers.addLayer(t),t.fire("revert-deleted",{layer:t})},this)},save:function(){this._map.fire("draw:deleted",{layers:this._deletedLayers})},_enableLayerDelete:function(t){var e=t.layer||t.target||t;e.on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e),e.fire("deleted")},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})}(window,document); \ No newline at end of file diff --git a/ckanext/spatial/public/js/vendor/leaflet/1.9.3/LICENSE b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/LICENSE new file mode 100644 index 00000000..4cd1f5b5 --- /dev/null +++ b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright (c) 2010-2022, Volodymyr Agafonkin +Copyright (c) 2010-2011, CloudMade +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/layers-2x.png b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/layers-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..200c333dca9652ac4cba004d609e5af4eee168c1 GIT binary patch literal 1259 zcmVFhCYNy;#0irRPomHqW|G1C*;4?@4#E?jH>?v@U%cy?3dQAc-DchXVErpOh~ z-jbon+tNbnl6hoEb;)TVk+%hTDDi_G%i3*RZ&15!$Fjr^f;Ke&A@|?=`2&+{zr+3a z{D*=t(`AXyS%X7N z%a#RZw6vD^t_rnM`L4E>m=U&R!A-&}nZIi$BOPvkhrCuUe@BN~-lRD)f44;J%TwgE zcze8u!PQ_NR7?o(NylLXVTfDO zxs5=@|GsYEsNo4M#nT%N!UE(?dnS)t2+{ELYAFp*3=iF=|EQnTp`#vlSXuGVraYo? z+RCzXo6h3qA8{KG?S4nE(lM+;Eb4nT3XV;7gcAxUi5m)`k5tv}cPy()8ZR3TLW3I- zAS^}cq-IJvL7a4RgR!yk@~RT%$lA7{L5ES*hyx)M4(yxI$Ub(4f)K|^v1>zvwQY!_ zIrWw8q9GS^!Dp~}+?mbnB6jDF8mVlbQ!jFKDY;w=7;XO{9bq7>LXGK24WA`;rL)_Z z)&j}pbV(;6gY;VMhbxgvn`X;6x}VUEE-7 z%)7j-%t8S=ZL3yc)HbXDAqJZvBTPoiW_A-+a8m3_Z?v{DN7Tnr#O_VUMT0UBt$;p` zDh6JbGHN8JJ*JN%y2%msb97@_S>9!%Egwk;?PEkU9ntz&3uR}%Fj5d$JHQbQb3}a{ zSzFT^#n=VInPpcAS}CNxj?_ zVscANk5Cfz(51EI1pz};AWWb|kgbYNb4wCEGUn3+eMUMV?1-{=I4TlmLJMot@rd07 zZuo2hk1ccu{YmGkcYdWAVdk{Z4Nm?^cTD&}jGm+Q1SYIXMwmG*oO*83&#>l%nbR`G zhh=lZ%xIb7kU3#;TBbfECrnC9P=-XpL|TG2BoZdj61*XiFbW8?1Z_wp%#;>${SUIy V$8qr;L*)Pf002ovPDHLkV1hYLS~36t literal 0 HcmV?d00001 diff --git a/ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/layers.png b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..1a72e5784b2b456eac5d7670738db80697af3377 GIT binary patch literal 696 zcmV;p0!RIcP)*@&l2<6p=!C&s@#ZL+%BQvF&b?w6S%wp=I>1QHj7AP5C)IWy#b znXXB;g;j=$a-tW89K%FbDceHVq&unY*Wx3L#=EGWH=rjqnp|4c_Ulec!ql3#G-5ZF zVlbBA@XP=)C8U&+Lrc)S4O5%1$&{(;7R^K(CSnvSr$v;+B$8q&7Bf|h$#PARo1^%M zf1H^nG-EiXVXr07OH(*8R)xa|FD;lXUlg_-%)~ZGsL2cX0NXaAzN2q%jqLRR6ruVk8`Jb7n#{`T;o@`F= z#3YcynIR^s83UNF3D!f5m#Mg)NJ24&Qfrqb&_z=yF;=B)#9Iq7u-@^O!(mW{D;qvr zPc)gVb%aowtS8m@ElL4A9G>w#ffQ~q{i&_i)*6f^)Sz|C?C>zb4Uo?H<-&Hz@a?J; z$ml@zGygWofb9$ZBj6aLjpLhsT2AzjOu=-*u_gSCUYnU^5s62$4H-fe}gSR(=wKRaTHh!@*b)YV6mo|a4Fn6Rgc&Rpk zvn_X|3VY?v=>nJ{slE^V1GaGWk}m@aIWGIpghbfPh8m@aIWEo_%AZI>==moIFVE^L=C zZJ91?mo03UEp3-BY?wBGur6$uD{Yr9Y?m%SHF8Fk1pc(Nva%QJ+{FLkalfypz3&M|||Fn`7|g3c~4(nXHKFmRnwn$J#_$xE8i z|Ns9!kC;(oC1qQk>LMp3_a2(odYyMT@>voX=UI)k>1cJdn;gjmJ-|6v4nb1Oryh)eQMwHP(i@!36%vGJyFK(JTj?Vb{{C=jx&)@1l zlFmnw%0`&bqruifkkHKC=vbiAM3&E`#Mv>2%tw;VK8?_|&E89cs{a1}$J*!f_xd-C z&F%B|oxRgPlh0F!txkxrQjNA`m9~?&&|jw4W0<`_iNHsX$VQXVK!B}Xkh4>av|f_8 zLY2?t?ejE=%(TnfV5iqOjm?d;&qI~ZGl|SzU77a)002XDQchC<95+*MjE@82?VLm= z3xf6%Vd@99z|q|-ua5l3kJxvZwan-8K1cPiwQAtlcNX~ZqLeoMB+a;7)WA|O#HOB% zg6SX;754xD1{Fy}K~#8Ntklac&zTpadXZ& zC*_=T&g7hfbI$R?v%9?sknIb97gJOJ=`-8YyS3ndqN+Jm+x33!p&Hc@@L$w))s2@N ztv~i}Emc?DykgwFWwma($8+~b>l?tqj$dh13R^nMZnva9 zn0Vflzv2Dvp`oVQw{Guby~i`JGbyBGTEC{y>yzCkg>K&CIeQ$u;lyQ+M{O~gEJ^)Z zrF3p)^>|uT;57}WY&IRwyOQ=dq%Az}_t=_hKowP!Z79q0;@Zu(SWEJJcHY+5T6I({ zw)wj*SNi4wrd+POUfZe4gF77vW?j zoFS}|r2n&$U9Y!S4VEOyN}OpZZi|?cr1VcE_tHsDQgp-ga(SwkBrkCm{|*-yb=}ZW zvcYvLvfA90TPn|!-TuYJV<6`}+RJeRgP3EA=qQcF9k0*#*{f&I_pjam%I6Dd#YE|G zqB!R}tW-K!wV1w+4JcFA_s6~=@9F&j8`u$-ifLN3vK;`lvaA-`jRn_}(8|)!3?-}I zvFi{H;@A$gEZYh?%|Qr_y#*UkOPjwiRCsJQ>mb6h5yGIk6C5_XA=8T?IBfm_?+P0; zhhUs)-(0R*H<&Kku(1>#cGtOpk&Z&kQcw&SJv-4VY<+;=8hYnoX zfNJMCa9)^5Z0;2dCUk;x-%#yS!I~Jr3pNuI!g_tHz!$hKwt1GL~sFvx)3u4TA zv>CLGdQtoZ7Du7ctJRfTqY;FPxs1G{ZJ?73D5J@OO{6BHcPbk{_mjg&p2QFeke%QI zlAJ-kvjuwy1<5D-6>su68A+i998aSZNnQX)+Q}6(GK-C%8G-!1bOJBONU{gT%IOOE z;Yk24YC@^lFW77>r6x7eS1Omc;8=GUp#&zLQ&L{ zv8$hGC`wp~$9pR>f%-_Ps3>YhzP(+vC(E*zr1CVO8ChN^MI-VGMX7+|(r!SGZ9gd5 zzO9sQd>sm|f1|X&oh=8lOzd6+ITvo zCXInR?>RZ#>Hb*PO=7dI!dZ(wY4O}ZGv zdfQFio7+0~PN*RFCZGM6@9-o~y*@?;k00NvOsw54t1^tt{*ATMs^2j}4Wp=4t3RH* z_+8b`F-{E=0sOgM<;VHTo!Ij3u zmmI`2?K7g(GOcGA)@h?$SW&pwHdtj1n57PLI8&6RHhx4R%Q7b z^JEqR)@06V!pbS*@D_ZyRMo_LlT}r{#sXOx4kM-V<_V{!5SSuM^SIVCA37|nY7LWQ zZA#B1h4l`6asz=Lvax_#GMRX|NF>=$=p{Qn0i@ExX1jGhy@B8a*_uR+ODEbVi8ObL zezG?azy>E~S~dl43&8<$(2H}P&*tuBdESUP83KQ?8B z?K(!uS>H1wlWQz;qOfB`T#TZ=EoSp~vZ5XtCvwm1h*Ex6mzTsn_y@_=xREIslV-%- zpdWkEzMjeNOGWrSM32gpBt27*O29NdhGzuDgYxcf`Jjjqw@B;Vmdb@fxdhCRi`Kg> zmUTr$=&@#i!%F4Q6mb&4QKfR^95KJ!<6~fqx-f^66AV!|ywG{6D^Vay-3b99>XOe# e-I|>x8~*?ZhF3snGbtJX0000cOl4 literal 0 HcmV?d00001 diff --git a/ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/marker-icon.png b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/images/marker-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..950edf24677ded147df13b26f91baa2b0fa70513 GIT binary patch literal 1466 zcmV;r1x5OaP)P001cn1^@s6z>|W`000GnNklGNuHDcIX17Zdjl&3`L?0sTjIws<{((Dh&g-s0<@jYQyl?D*X^?%13;ml^gy> ziMrY_^1WI=(g@LMizu=zCoA>C`6|QEq1eV92k*7m>G65*&@&6)aC&e}G zI)pf-Za|N`DT&Cn1J|o`19mumxW~hiKiKyc-P`S@q)rdTo84@QI@;0yXrG%9uhI>A zG5QHb6s4=<6xy{1 z@NMxEkryp{LS44%z$3lP^cX!9+2-;CTt3wM4(k*#C{aiIiLuB>jJj;KPhPzIC00bL zU3a#;aJld94lCW=`4&aAy8M7PY=HQ>O%$YEP4c4UY#CRxfgbE~(|uiI=YS8q;O9y6 zmIkXzR`}p7ti|PrM3a}WMnR=3NVnWdAAR>b9X@)DKL6=YsvmH%?I24wdq?Gh54_;# z$?_LvgjEdspdQlft#4CQ z`2Zyvy?*)N1Ftw|{_hakhG9WjS?Az@I@+IZ8JbWewR!XUK4&6346+d#~gsE0SY(LX8&JfY>Aj)RxGy96nwhs2rv zzW6pTnMpFkDSkT*a*6Dx|u@ds6ISVn0@^RmIsKZ5Y;bazbc;tTSq(kg(=481ODrPyNB6n z-$+U}(w$m6U6H$w17Bw+wDaFIe~GvNMYvnw31MpY0eQKT9l>SU``8k7w4)z!GZKMI z#_cEKq7k~i%nlK@6c-K?+R;B#5$?T#YpKD`t_4bAs^#E+@5QW$@OX3*`;(#{U^d-vY)&xEE>n5lYl&T?Amke9$Lam@{1K@O ze*LXqlKQHiv=gx+V^Cbb2?z@ISBQ*3amF;9UJ3SBg(N|710TLamQmYZ&Qjn2LuO<* zCZlB4n%@pc&7NNnY1}x+NWpHlq`OJEo|`aYN9<`RBUB+79g;>dgb6YlfN#kGL?lO_ z!6~M^7sOnbsUkKk<@Ysie&`G>ruxH&Mgy&8;i=A zB9OO!xR{AyODw>DS-q5YM{0ExFEAzt zm>RdS+ssW(-8|?xr0(?$vBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrP zs(rTFX@W#t$hXNvbgX`gK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z!4=W zi$giRj1{ zt;2w`VSCF#WE&*ev7jpsC=6175@(~nTE2;7M-L((0bH@yG}-TB$R~WXd?tA$s3|%y zA`9$sA(>F%J3ioz<-LJl*^o1|w84l>HBR`>3l9c8$5Xr@xCiIQ7{x$fMCzOk_-M=% z+{a_Q#;42`#KfUte@$NT77uaTz?b-fBe)1s5XE$yA79fm?KqM^VgLXD07*qoM6N<$ Ef<_J(9smFU literal 0 HcmV?d00001 diff --git a/ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.esm.js b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.esm.js new file mode 100644 index 00000000..b166b96e --- /dev/null +++ b/ckanext/spatial/public/js/vendor/leaflet/1.9.3/leaflet-src.esm.js @@ -0,0 +1,14356 @@ +/* @preserve + * Leaflet 1.9.3, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2022 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ + +var version = "1.9.3"; + +/* + * @namespace Util + * + * Various utility functions, used by Leaflet internally. + */ + +// @function extend(dest: Object, src?: Object): Object +// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. +function extend(dest) { + var i, j, len, src; + + for (j = 1, len = arguments.length; j < len; j++) { + src = arguments[j]; + for (i in src) { + dest[i] = src[i]; + } + } + return dest; +} + +// @function create(proto: Object, properties?: Object): Object +// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) +var create$2 = Object.create || (function () { + function F() {} + return function (proto) { + F.prototype = proto; + return new F(); + }; +})(); + +// @function bind(fn: Function, …): Function +// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). +// Has a `L.bind()` shortcut. +function bind(fn, obj) { + var slice = Array.prototype.slice; + + if (fn.bind) { + return fn.bind.apply(fn, slice.call(arguments, 1)); + } + + var args = slice.call(arguments, 2); + + return function () { + return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); + }; +} + +// @property lastId: Number +// Last unique ID used by [`stamp()`](#util-stamp) +var lastId = 0; + +// @function stamp(obj: Object): Number +// Returns the unique ID of an object, assigning it one if it doesn't have it. +function stamp(obj) { + if (!('_leaflet_id' in obj)) { + obj['_leaflet_id'] = ++lastId; + } + return obj._leaflet_id; +} + +// @function throttle(fn: Function, time: Number, context: Object): Function +// Returns a function which executes function `fn` with the given scope `context` +// (so that the `this` keyword refers to `context` inside `fn`'s code). The function +// `fn` will be called no more than one time per given amount of `time`. The arguments +// received by the bound function will be any arguments passed when binding the +// function, followed by any arguments passed when invoking the bound function. +// Has an `L.throttle` shortcut. +function throttle(fn, time, context) { + var lock, args, wrapperFn, later; + + later = function () { + // reset lock and call if queued + lock = false; + if (args) { + wrapperFn.apply(context, args); + args = false; + } + }; + + wrapperFn = function () { + if (lock) { + // called too soon, queue to call later + args = arguments; + + } else { + // call and lock until later + fn.apply(context, arguments); + setTimeout(later, time); + lock = true; + } + }; + + return wrapperFn; +} + +// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number +// Returns the number `num` modulo `range` in such a way so it lies within +// `range[0]` and `range[1]`. The returned value will be always smaller than +// `range[1]` unless `includeMax` is set to `true`. +function wrapNum(x, range, includeMax) { + var max = range[1], + min = range[0], + d = max - min; + return x === max && includeMax ? x : ((x - min) % d + d) % d + min; +} + +// @function falseFn(): Function +// Returns a function which always returns `false`. +function falseFn() { return false; } + +// @function formatNum(num: Number, precision?: Number|false): Number +// Returns the number `num` rounded with specified `precision`. +// The default `precision` value is 6 decimal places. +// `false` can be passed to skip any processing (can be useful to avoid round-off errors). +function formatNum(num, precision) { + if (precision === false) { return num; } + var pow = Math.pow(10, precision === undefined ? 6 : precision); + return Math.round(num * pow) / pow; +} + +// @function trim(str: String): String +// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) +function trim(str) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); +} + +// @function splitWords(str: String): String[] +// Trims and splits the string on whitespace and returns the array of parts. +function splitWords(str) { + return trim(str).split(/\s+/); +} + +// @function setOptions(obj: Object, options: Object): Object +// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. +function setOptions(obj, options) { + if (!Object.prototype.hasOwnProperty.call(obj, 'options')) { + obj.options = obj.options ? create$2(obj.options) : {}; + } + for (var i in options) { + obj.options[i] = options[i]; + } + return obj.options; +} + +// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String +// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` +// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will +// be appended at the end. If `uppercase` is `true`, the parameter names will +// be uppercased (e.g. `'?A=foo&B=bar'`) +function getParamString(obj, existingUrl, uppercase) { + var params = []; + for (var i in obj) { + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); + } + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); +} + +var templateRe = /\{ *([\w_ -]+) *\}/g; + +// @function template(str: String, data: Object): String +// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` +// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string +// `('Hello foo, bar')`. You can also specify functions instead of strings for +// data values — they will be evaluated passing `data` as an argument. +function template(str, data) { + return str.replace(templateRe, function (str, key) { + var value = data[key]; + + if (value === undefined) { + throw new Error('No value provided for variable ' + str); + + } else if (typeof value === 'function') { + value = value(data); + } + return value; + }); +} + +// @function isArray(obj): Boolean +// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) +var isArray = Array.isArray || function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); +}; + +// @function indexOf(array: Array, el: Object): Number +// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) +function indexOf(array, el) { + for (var i = 0; i < array.length; i++) { + if (array[i] === el) { return i; } + } + return -1; +} + +// @property emptyImageUrl: String +// Data URI string containing a base64-encoded empty GIF image. +// Used as a hack to free memory from unused images on WebKit-powered +// mobile devices (by setting image `src` to this string). +var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; + +// inspired by https://paulirish.com/2011/requestanimationframe-for-smart-animating/ + +function getPrefixed(name) { + return window['webkit' + name] || window['moz' + name] || window['ms' + name]; +} + +var lastTime = 0; + +// fallback for IE 7-8 +function timeoutDefer(fn) { + var time = +new Date(), + timeToCall = Math.max(0, 16 - (time - lastTime)); + + lastTime = time + timeToCall; + return window.setTimeout(fn, timeToCall); +} + +var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; +var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; + +// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number +// Schedules `fn` to be executed when the browser repaints. `fn` is bound to +// `context` if given. When `immediate` is set, `fn` is called immediately if +// the browser doesn't have native support for +// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), +// otherwise it's delayed. Returns a request ID that can be used to cancel the request. +function requestAnimFrame(fn, context, immediate) { + if (immediate && requestFn === timeoutDefer) { + fn.call(context); + } else { + return requestFn.call(window, bind(fn, context)); + } +} + +// @function cancelAnimFrame(id: Number): undefined +// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). +function cancelAnimFrame(id) { + if (id) { + cancelFn.call(window, id); + } +} + +var Util = { + __proto__: null, + extend: extend, + create: create$2, + bind: bind, + get lastId () { return lastId; }, + stamp: stamp, + throttle: throttle, + wrapNum: wrapNum, + falseFn: falseFn, + formatNum: formatNum, + trim: trim, + splitWords: splitWords, + setOptions: setOptions, + getParamString: getParamString, + template: template, + isArray: isArray, + indexOf: indexOf, + emptyImageUrl: emptyImageUrl, + requestFn: requestFn, + cancelFn: cancelFn, + requestAnimFrame: requestAnimFrame, + cancelAnimFrame: cancelAnimFrame +}; + +// @class Class +// @aka L.Class + +// @section +// @uninheritable + +// Thanks to John Resig and Dean Edwards for inspiration! + +function Class() {} + +Class.extend = function (props) { + + // @function extend(props: Object): Function + // [Extends the current class](#class-inheritance) given the properties to be included. + // Returns a Javascript function that is a class constructor (to be called with `new`). + var NewClass = function () { + + setOptions(this); + + // call the constructor + if (this.initialize) { + this.initialize.apply(this, arguments); + } + + // call all constructor hooks + this.callInitHooks(); + }; + + var parentProto = NewClass.__super__ = this.prototype; + + var proto = create$2(parentProto); + proto.constructor = NewClass; + + NewClass.prototype = proto; + + // inherit parent's statics + for (var i in this) { + if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') { + NewClass[i] = this[i]; + } + } + + // mix static properties into the class + if (props.statics) { + extend(NewClass, props.statics); + } + + // mix includes into the prototype + if (props.includes) { + checkDeprecatedMixinEvents(props.includes); + extend.apply(null, [proto].concat(props.includes)); + } + + // mix given properties into the prototype + extend(proto, props); + delete proto.statics; + delete proto.includes; + + // merge options + if (proto.options) { + proto.options = parentProto.options ? create$2(parentProto.options) : {}; + extend(proto.options, props.options); + } + + proto._initHooks = []; + + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parentProto.callInitHooks) { + parentProto.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + + return NewClass; +}; + + +// @function include(properties: Object): this +// [Includes a mixin](#class-includes) into the current class. +Class.include = function (props) { + var parentOptions = this.prototype.options; + extend(this.prototype, props); + if (props.options) { + this.prototype.options = parentOptions; + this.mergeOptions(props.options); + } + return this; +}; + +// @function mergeOptions(options: Object): this +// [Merges `options`](#class-options) into the defaults of the class. +Class.mergeOptions = function (options) { + extend(this.prototype.options, options); + return this; +}; + +// @function addInitHook(fn: Function): this +// Adds a [constructor hook](#class-constructor-hooks) to the class. +Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); + return this; +}; + +function checkDeprecatedMixinEvents(includes) { + /* global L: true */ + if (typeof L === 'undefined' || !L || !L.Mixin) { return; } + + includes = isArray(includes) ? includes : [includes]; + + for (var i = 0; i < includes.length; i++) { + if (includes[i] === L.Mixin.Events) { + console.warn('Deprecated include of L.Mixin.Events: ' + + 'this property will be removed in future releases, ' + + 'please inherit from L.Evented instead.', new Error().stack); + } + } +} + +/* + * @class Evented + * @aka L.Evented + * @inherits Class + * + * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event). + * + * @example + * + * ```js + * map.on('click', function(e) { + * alert(e.latlng); + * } ); + * ``` + * + * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: + * + * ```js + * function onClick(e) { ... } + * + * map.on('click', onClick); + * map.off('click', onClick); + * ``` + */ + +var Events = { + /* @method on(type: String, fn: Function, context?: Object): this + * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). + * + * @alternative + * @method on(eventMap: Object): this + * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + */ + on: function (types, fn, context) { + + // types can be a map of types/handlers + if (typeof types === 'object') { + for (var type in types) { + // we don't process space-separated events here for performance; + // it's a hot path since Layer uses the on(obj) syntax + this._on(type, types[type], fn); + } + + } else { + // types can be a string of space-separated words + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._on(types[i], fn, context); + } + } + + return this; + }, + + /* @method off(type: String, fn?: Function, context?: Object): this + * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener. + * + * @alternative + * @method off(eventMap: Object): this + * Removes a set of type/listener pairs. + * + * @alternative + * @method off: this + * Removes all listeners to all events on the object. This includes implicitly attached events. + */ + off: function (types, fn, context) { + + if (!arguments.length) { + // clear all listeners if called without arguments + delete this._events; + + } else if (typeof types === 'object') { + for (var type in types) { + this._off(type, types[type], fn); + } + + } else { + types = splitWords(types); + + var removeAll = arguments.length === 1; + for (var i = 0, len = types.length; i < len; i++) { + if (removeAll) { + this._off(types[i]); + } else { + this._off(types[i], fn, context); + } + } + } + + return this; + }, + + // attach listener (without syntactic sugar now) + _on: function (type, fn, context, _once) { + if (typeof fn !== 'function') { + console.warn('wrong listener type: ' + typeof fn); + return; + } + + // check if fn already there + if (this._listens(type, fn, context) !== false) { + return; + } + + if (context === this) { + // Less memory footprint. + context = undefined; + } + + var newListener = {fn: fn, ctx: context}; + if (_once) { + newListener.once = true; + } + + this._events = this._events || {}; + this._events[type] = this._events[type] || []; + this._events[type].push(newListener); + }, + + _off: function (type, fn, context) { + var listeners, + i, + len; + + if (!this._events) { + return; + } + + listeners = this._events[type]; + if (!listeners) { + return; + } + + if (arguments.length === 1) { // remove all + if (this._firingCount) { + // Set all removed listeners to noop + // so they are not called if remove happens in fire + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].fn = falseFn; + } + } + // clear all listeners for a type if function isn't specified + delete this._events[type]; + return; + } + + if (typeof fn !== 'function') { + console.warn('wrong listener type: ' + typeof fn); + return; + } + + // find fn and remove it + var index = this._listens(type, fn, context); + if (index !== false) { + var listener = listeners[index]; + if (this._firingCount) { + // set the removed listener to noop so that's not called if remove happens in fire + listener.fn = falseFn; + + /* copy array in case events are being fired */ + this._events[type] = listeners = listeners.slice(); + } + listeners.splice(index, 1); + } + }, + + // @method fire(type: String, data?: Object, propagate?: Boolean): this + // Fires an event of the specified type. You can optionally provide a data + // object — the first argument of the listener function will contain its + // properties. The event can optionally be propagated to event parents. + fire: function (type, data, propagate) { + if (!this.listens(type, propagate)) { return this; } + + var event = extend({}, data, { + type: type, + target: this, + sourceTarget: data && data.sourceTarget || this + }); + + if (this._events) { + var listeners = this._events[type]; + if (listeners) { + this._firingCount = (this._firingCount + 1) || 1; + for (var i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + // off overwrites l.fn, so we need to copy fn to a var + var fn = l.fn; + if (l.once) { + this.off(type, fn, l.ctx); + } + fn.call(l.ctx || this, event); + } + + this._firingCount--; + } + } + + if (propagate) { + // propagate the event to parents (set with addEventParent) + this._propagateEvent(event); + } + + return this; + }, + + // @method listens(type: String, propagate?: Boolean): Boolean + // @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean + // Returns `true` if a particular event type has any listeners attached to it. + // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it. + listens: function (type, fn, context, propagate) { + if (typeof type !== 'string') { + console.warn('"string" type argument expected'); + } + + // we don't overwrite the input `fn` value, because we need to use it for propagation + var _fn = fn; + if (typeof fn !== 'function') { + propagate = !!fn; + _fn = undefined; + context = undefined; + } + + var listeners = this._events && this._events[type]; + if (listeners && listeners.length) { + if (this._listens(type, _fn, context) !== false) { + return true; + } + } + + if (propagate) { + // also check parents for listeners if event propagates + for (var id in this._eventParents) { + if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; } + } + } + return false; + }, + + // returns the index (number) or false + _listens: function (type, fn, context) { + if (!this._events) { + return false; + } + + var listeners = this._events[type] || []; + if (!fn) { + return !!listeners.length; + } + + if (context === this) { + // Less memory footprint. + context = undefined; + } + + for (var i = 0, len = listeners.length; i < len; i++) { + if (listeners[i].fn === fn && listeners[i].ctx === context) { + return i; + } + } + return false; + + }, + + // @method once(…): this + // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. + once: function (types, fn, context) { + + // types can be a map of types/handlers + if (typeof types === 'object') { + for (var type in types) { + // we don't process space-separated events here for performance; + // it's a hot path since Layer uses the on(obj) syntax + this._on(type, types[type], fn, true); + } + + } else { + // types can be a string of space-separated words + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._on(types[i], fn, context, true); + } + } + + return this; + }, + + // @method addEventParent(obj: Evented): this + // Adds an event parent - an `Evented` that will receive propagated events + addEventParent: function (obj) { + this._eventParents = this._eventParents || {}; + this._eventParents[stamp(obj)] = obj; + return this; + }, + + // @method removeEventParent(obj: Evented): this + // Removes an event parent, so it will stop receiving propagated events + removeEventParent: function (obj) { + if (this._eventParents) { + delete this._eventParents[stamp(obj)]; + } + return this; + }, + + _propagateEvent: function (e) { + for (var id in this._eventParents) { + this._eventParents[id].fire(e.type, extend({ + layer: e.target, + propagatedFrom: e.target + }, e), true); + } + } +}; + +// aliases; we should ditch those eventually + +// @method addEventListener(…): this +// Alias to [`on(…)`](#evented-on) +Events.addEventListener = Events.on; + +// @method removeEventListener(…): this +// Alias to [`off(…)`](#evented-off) + +// @method clearAllEventListeners(…): this +// Alias to [`off()`](#evented-off) +Events.removeEventListener = Events.clearAllEventListeners = Events.off; + +// @method addOneTimeEventListener(…): this +// Alias to [`once(…)`](#evented-once) +Events.addOneTimeEventListener = Events.once; + +// @method fireEvent(…): this +// Alias to [`fire(…)`](#evented-fire) +Events.fireEvent = Events.fire; + +// @method hasEventListeners(…): Boolean +// Alias to [`listens(…)`](#evented-listens) +Events.hasEventListeners = Events.listens; + +var Evented = Class.extend(Events); + +/* + * @class Point + * @aka L.Point + * + * Represents a point with `x` and `y` coordinates in pixels. + * + * @example + * + * ```js + * var point = L.point(200, 300); + * ``` + * + * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent: + * + * ```js + * map.panBy([200, 300]); + * map.panBy(L.point(200, 300)); + * ``` + * + * Note that `Point` does not inherit from Leaflet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function Point(x, y, round) { + // @property x: Number; The `x` coordinate of the point + this.x = (round ? Math.round(x) : x); + // @property y: Number; The `y` coordinate of the point + this.y = (round ? Math.round(y) : y); +} + +var trunc = Math.trunc || function (v) { + return v > 0 ? Math.floor(v) : Math.ceil(v); +}; + +Point.prototype = { + + // @method clone(): Point + // Returns a copy of the current point. + clone: function () { + return new Point(this.x, this.y); + }, + + // @method add(otherPoint: Point): Point + // Returns the result of addition of the current and the given points. + add: function (point) { + // non-destructive, returns a new point + return this.clone()._add(toPoint(point)); + }, + + _add: function (point) { + // destructive, used directly for performance in situations where it's safe to modify existing point + this.x += point.x; + this.y += point.y; + return this; + }, + + // @method subtract(otherPoint: Point): Point + // Returns the result of subtraction of the given point from the current. + subtract: function (point) { + return this.clone()._subtract(toPoint(point)); + }, + + _subtract: function (point) { + this.x -= point.x; + this.y -= point.y; + return this; + }, + + // @method divideBy(num: Number): Point + // Returns the result of division of the current point by the given number. + divideBy: function (num) { + return this.clone()._divideBy(num); + }, + + _divideBy: function (num) { + this.x /= num; + this.y /= num; + return this; + }, + + // @method multiplyBy(num: Number): Point + // Returns the result of multiplication of the current point by the given number. + multiplyBy: function (num) { + return this.clone()._multiplyBy(num); + }, + + _multiplyBy: function (num) { + this.x *= num; + this.y *= num; + return this; + }, + + // @method scaleBy(scale: Point): Point + // Multiply each coordinate of the current point by each coordinate of + // `scale`. In linear algebra terms, multiply the point by the + // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) + // defined by `scale`. + scaleBy: function (point) { + return new Point(this.x * point.x, this.y * point.y); + }, + + // @method unscaleBy(scale: Point): Point + // Inverse of `scaleBy`. Divide each coordinate of the current point by + // each coordinate of `scale`. + unscaleBy: function (point) { + return new Point(this.x / point.x, this.y / point.y); + }, + + // @method round(): Point + // Returns a copy of the current point with rounded coordinates. + round: function () { + return this.clone()._round(); + }, + + _round: function () { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }, + + // @method floor(): Point + // Returns a copy of the current point with floored coordinates (rounded down). + floor: function () { + return this.clone()._floor(); + }, + + _floor: function () { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }, + + // @method ceil(): Point + // Returns a copy of the current point with ceiled coordinates (rounded up). + ceil: function () { + return this.clone()._ceil(); + }, + + _ceil: function () { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + return this; + }, + + // @method trunc(): Point + // Returns a copy of the current point with truncated coordinates (rounded towards zero). + trunc: function () { + return this.clone()._trunc(); + }, + + _trunc: function () { + this.x = trunc(this.x); + this.y = trunc(this.y); + return this; + }, + + // @method distanceTo(otherPoint: Point): Number + // Returns the cartesian distance between the current and the given points. + distanceTo: function (point) { + point = toPoint(point); + + var x = point.x - this.x, + y = point.y - this.y; + + return Math.sqrt(x * x + y * y); + }, + + // @method equals(otherPoint: Point): Boolean + // Returns `true` if the given point has the same coordinates. + equals: function (point) { + point = toPoint(point); + + return point.x === this.x && + point.y === this.y; + }, + + // @method contains(otherPoint: Point): Boolean + // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). + contains: function (point) { + point = toPoint(point); + + return Math.abs(point.x) <= Math.abs(this.x) && + Math.abs(point.y) <= Math.abs(this.y); + }, + + // @method toString(): String + // Returns a string representation of the point for debugging purposes. + toString: function () { + return 'Point(' + + formatNum(this.x) + ', ' + + formatNum(this.y) + ')'; + } +}; + +// @factory L.point(x: Number, y: Number, round?: Boolean) +// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. + +// @alternative +// @factory L.point(coords: Number[]) +// Expects an array of the form `[x, y]` instead. + +// @alternative +// @factory L.point(coords: Object) +// Expects a plain object of the form `{x: Number, y: Number}` instead. +function toPoint(x, y, round) { + if (x instanceof Point) { + return x; + } + if (isArray(x)) { + return new Point(x[0], x[1]); + } + if (x === undefined || x === null) { + return x; + } + if (typeof x === 'object' && 'x' in x && 'y' in x) { + return new Point(x.x, x.y); + } + return new Point(x, y, round); +} + +/* + * @class Bounds + * @aka L.Bounds + * + * Represents a rectangular area in pixel coordinates. + * + * @example + * + * ```js + * var p1 = L.point(10, 10), + * p2 = L.point(40, 60), + * bounds = L.bounds(p1, p2); + * ``` + * + * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * otherBounds.intersects([[10, 10], [40, 60]]); + * ``` + * + * Note that `Bounds` does not inherit from Leaflet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function Bounds(a, b) { + if (!a) { return; } + + var points = b ? [a, b] : a; + + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } +} + +Bounds.prototype = { + // @method extend(point: Point): this + // Extends the bounds to contain the given point. + + // @alternative + // @method extend(otherBounds: Bounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var min2, max2; + if (!obj) { return this; } + + if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) { + min2 = max2 = toPoint(obj); + } else { + obj = toBounds(obj); + min2 = obj.min; + max2 = obj.max; + + if (!min2 || !max2) { return this; } + } + + // @property min: Point + // The top left corner of the rectangle. + // @property max: Point + // The bottom right corner of the rectangle. + if (!this.min && !this.max) { + this.min = min2.clone(); + this.max = max2.clone(); + } else { + this.min.x = Math.min(min2.x, this.min.x); + this.max.x = Math.max(max2.x, this.max.x); + this.min.y = Math.min(min2.y, this.min.y); + this.max.y = Math.max(max2.y, this.max.y); + } + return this; + }, + + // @method getCenter(round?: Boolean): Point + // Returns the center point of the bounds. + getCenter: function (round) { + return toPoint( + (this.min.x + this.max.x) / 2, + (this.min.y + this.max.y) / 2, round); + }, + + // @method getBottomLeft(): Point + // Returns the bottom-left point of the bounds. + getBottomLeft: function () { + return toPoint(this.min.x, this.max.y); + }, + + // @method getTopRight(): Point + // Returns the top-right point of the bounds. + getTopRight: function () { // -> Point + return toPoint(this.max.x, this.min.y); + }, + + // @method getTopLeft(): Point + // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). + getTopLeft: function () { + return this.min; // left, top + }, + + // @method getBottomRight(): Point + // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). + getBottomRight: function () { + return this.max; // right, bottom + }, + + // @method getSize(): Point + // Returns the size of the given bounds + getSize: function () { + return this.max.subtract(this.min); + }, + + // @method contains(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle contains the given one. + // @alternative + // @method contains(point: Point): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { + var min, max; + + if (typeof obj[0] === 'number' || obj instanceof Point) { + obj = toPoint(obj); + } else { + obj = toBounds(obj); + } + + if (obj instanceof Bounds) { + min = obj.min; + max = obj.max; + } else { + min = max = obj; + } + + return (min.x >= this.min.x) && + (max.x <= this.max.x) && + (min.y >= this.min.y) && + (max.y <= this.max.y); + }, + + // @method intersects(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds + // intersect if they have at least one point in common. + intersects: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xIntersects = (max2.x >= min.x) && (min2.x <= max.x), + yIntersects = (max2.y >= min.y) && (min2.y <= max.y); + + return xIntersects && yIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds + // overlap if their intersection is an area. + overlaps: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xOverlaps = (max2.x > min.x) && (min2.x < max.x), + yOverlaps = (max2.y > min.y) && (min2.y < max.y); + + return xOverlaps && yOverlaps; + }, + + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. + isValid: function () { + return !!(this.min && this.max); + }, + + + // @method pad(bufferRatio: Number): Bounds + // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. + // For example, a ratio of 0.5 extends the bounds by 50% in each direction. + // Negative values will retract the bounds. + pad: function (bufferRatio) { + var min = this.min, + max = this.max, + heightBuffer = Math.abs(min.x - max.x) * bufferRatio, + widthBuffer = Math.abs(min.y - max.y) * bufferRatio; + + + return toBounds( + toPoint(min.x - heightBuffer, min.y - widthBuffer), + toPoint(max.x + heightBuffer, max.y + widthBuffer)); + }, + + + // @method equals(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle is equivalent to the given bounds. + equals: function (bounds) { + if (!bounds) { return false; } + + bounds = toBounds(bounds); + + return this.min.equals(bounds.getTopLeft()) && + this.max.equals(bounds.getBottomRight()); + }, +}; + + +// @factory L.bounds(corner1: Point, corner2: Point) +// Creates a Bounds object from two corners coordinate pairs. +// @alternative +// @factory L.bounds(points: Point[]) +// Creates a Bounds object from the given array of points. +function toBounds(a, b) { + if (!a || a instanceof Bounds) { + return a; + } + return new Bounds(a, b); +} + +/* + * @class LatLngBounds + * @aka L.LatLngBounds + * + * Represents a rectangular geographical area on a map. + * + * @example + * + * ```js + * var corner1 = L.latLng(40.712, -74.227), + * corner2 = L.latLng(40.774, -74.125), + * bounds = L.latLngBounds(corner1, corner2); + * ``` + * + * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * map.fitBounds([ + * [40.712, -74.227], + * [40.774, -74.125] + * ]); + * ``` + * + * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. + * + * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) + if (!corner1) { return; } + + var latlngs = corner2 ? [corner1, corner2] : corner1; + + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +} + +LatLngBounds.prototype = { + + // @method extend(latlng: LatLng): this + // Extend the bounds to contain the given point + + // @alternative + // @method extend(otherBounds: LatLngBounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLng) { + sw2 = obj; + ne2 = obj; + + } else if (obj instanceof LatLngBounds) { + sw2 = obj._southWest; + ne2 = obj._northEast; + + if (!sw2 || !ne2) { return this; } + + } else { + return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; + } + + if (!sw && !ne) { + this._southWest = new LatLng(sw2.lat, sw2.lng); + this._northEast = new LatLng(ne2.lat, ne2.lng); + } else { + sw.lat = Math.min(sw2.lat, sw.lat); + sw.lng = Math.min(sw2.lng, sw.lng); + ne.lat = Math.max(ne2.lat, ne.lat); + ne.lng = Math.max(ne2.lng, ne.lng); + } + + return this; + }, + + // @method pad(bufferRatio: Number): LatLngBounds + // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. + // For example, a ratio of 0.5 extends the bounds by 50% in each direction. + // Negative values will retract the bounds. + pad: function (bufferRatio) { + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; + + return new LatLngBounds( + new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); + }, + + // @method getCenter(): LatLng + // Returns the center point of the bounds. + getCenter: function () { + return new LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); + }, + + // @method getSouthWest(): LatLng + // Returns the south-west point of the bounds. + getSouthWest: function () { + return this._southWest; + }, + + // @method getNorthEast(): LatLng + // Returns the north-east point of the bounds. + getNorthEast: function () { + return this._northEast; + }, + + // @method getNorthWest(): LatLng + // Returns the north-west point of the bounds. + getNorthWest: function () { + return new LatLng(this.getNorth(), this.getWest()); + }, + + // @method getSouthEast(): LatLng + // Returns the south-east point of the bounds. + getSouthEast: function () { + return new LatLng(this.getSouth(), this.getEast()); + }, + + // @method getWest(): Number + // Returns the west longitude of the bounds + getWest: function () { + return this._southWest.lng; + }, + + // @method getSouth(): Number + // Returns the south latitude of the bounds + getSouth: function () { + return this._southWest.lat; + }, + + // @method getEast(): Number + // Returns the east longitude of the bounds + getEast: function () { + return this._northEast.lng; + }, + + // @method getNorth(): Number + // Returns the north latitude of the bounds + getNorth: function () { + return this._northEast.lat; + }, + + // @method contains(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle contains the given one. + + // @alternative + // @method contains (latlng: LatLng): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { + obj = toLatLng(obj); + } else { + obj = toLatLngBounds(obj); + } + + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); + } else { + sw2 = ne2 = obj; + } + + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + }, + + // @method intersects(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. + intersects: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + + return latIntersects && lngIntersects; + }, + + // @method overlaps(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. + overlaps: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), + lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); + + return latOverlaps && lngOverlaps; + }, + + // @method toBBoxString(): String + // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); + }, + + // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean + // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (bounds, maxMargin) { + if (!bounds) { return false; } + + bounds = toLatLngBounds(bounds); + + return this._southWest.equals(bounds.getSouthWest(), maxMargin) && + this._northEast.equals(bounds.getNorthEast(), maxMargin); + }, + + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. + isValid: function () { + return !!(this._southWest && this._northEast); + } +}; + +// TODO International date line? + +// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) +// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. + +// @alternative +// @factory L.latLngBounds(latlngs: LatLng[]) +// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). +function toLatLngBounds(a, b) { + if (a instanceof LatLngBounds) { + return a; + } + return new LatLngBounds(a, b); +} + +/* @class LatLng + * @aka L.LatLng + * + * Represents a geographical point with a certain latitude and longitude. + * + * @example + * + * ``` + * var latlng = L.latLng(50.5, 30.5); + * ``` + * + * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent: + * + * ``` + * map.panTo([50, 30]); + * map.panTo({lon: 30, lat: 50}); + * map.panTo({lat: 50, lng: 30}); + * map.panTo(L.latLng(50, 30)); + * ``` + * + * Note that `LatLng` does not inherit from Leaflet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function LatLng(lat, lng, alt) { + if (isNaN(lat) || isNaN(lng)) { + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); + } + + // @property lat: Number + // Latitude in degrees + this.lat = +lat; + + // @property lng: Number + // Longitude in degrees + this.lng = +lng; + + // @property alt: Number + // Altitude in meters (optional) + if (alt !== undefined) { + this.alt = +alt; + } +} + +LatLng.prototype = { + // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean + // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (obj, maxMargin) { + if (!obj) { return false; } + + obj = toLatLng(obj); + + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + + return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); + }, + + // @method toString(): String + // Returns a string representation of the point (for debugging purposes). + toString: function (precision) { + return 'LatLng(' + + formatNum(this.lat, precision) + ', ' + + formatNum(this.lng, precision) + ')'; + }, + + // @method distanceTo(otherLatLng: LatLng): Number + // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines). + distanceTo: function (other) { + return Earth.distance(this, toLatLng(other)); + }, + + // @method wrap(): LatLng + // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. + wrap: function () { + return Earth.wrapLatLng(this); + }, + + // @method toBounds(sizeInMeters: Number): LatLngBounds + // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. + toBounds: function (sizeInMeters) { + var latAccuracy = 180 * sizeInMeters / 40075017, + lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); + + return toLatLngBounds( + [this.lat - latAccuracy, this.lng - lngAccuracy], + [this.lat + latAccuracy, this.lng + lngAccuracy]); + }, + + clone: function () { + return new LatLng(this.lat, this.lng, this.alt); + } +}; + + + +// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng +// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). + +// @alternative +// @factory L.latLng(coords: Array): LatLng +// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. + +// @alternative +// @factory L.latLng(coords: Object): LatLng +// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. + +function toLatLng(a, b, c) { + if (a instanceof LatLng) { + return a; + } + if (isArray(a) && typeof a[0] !== 'object') { + if (a.length === 3) { + return new LatLng(a[0], a[1], a[2]); + } + if (a.length === 2) { + return new LatLng(a[0], a[1]); + } + return null; + } + if (a === undefined || a === null) { + return a; + } + if (typeof a === 'object' && 'lat' in a) { + return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); + } + if (b === undefined) { + return null; + } + return new LatLng(a, b, c); +} + +/* + * @namespace CRS + * @crs L.CRS.Base + * Object that defines coordinate reference systems for projecting + * geographical points into pixel (screen) coordinates and back (and to + * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See + * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system). + * + * Leaflet defines the most usual CRSs by default. If you want to use a + * CRS not defined by default, take a look at the + * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. + * + * Note that the CRS instances do not inherit from Leaflet's `Class` object, + * and can't be instantiated. Also, new classes can't inherit from them, + * and methods can't be added to them with the `include` function. + */ + +var CRS = { + // @method latLngToPoint(latlng: LatLng, zoom: Number): Point + // Projects geographical coordinates into pixel coordinates for a given zoom. + latLngToPoint: function (latlng, zoom) { + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + + return this.transformation._transform(projectedPoint, scale); + }, + + // @method pointToLatLng(point: Point, zoom: Number): LatLng + // The inverse of `latLngToPoint`. Projects pixel coordinates on a given + // zoom into geographical coordinates. + pointToLatLng: function (point, zoom) { + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + + return this.projection.unproject(untransformedPoint); + }, + + // @method project(latlng: LatLng): Point + // Projects geographical coordinates into coordinates in units accepted for + // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). + project: function (latlng) { + return this.projection.project(latlng); + }, + + // @method unproject(point: Point): LatLng + // Given a projected coordinate returns the corresponding LatLng. + // The inverse of `project`. + unproject: function (point) { + return this.projection.unproject(point); + }, + + // @method scale(zoom: Number): Number + // Returns the scale used when transforming projected coordinates into + // pixel coordinates for a particular zoom. For example, it returns + // `256 * 2^zoom` for Mercator-based CRS. + scale: function (zoom) { + return 256 * Math.pow(2, zoom); + }, + + // @method zoom(scale: Number): Number + // Inverse of `scale()`, returns the zoom level corresponding to a scale + // factor of `scale`. + zoom: function (scale) { + return Math.log(scale / 256) / Math.LN2; + }, + + // @method getProjectedBounds(zoom: Number): Bounds + // Returns the projection's bounds scaled and transformed for the provided `zoom`. + getProjectedBounds: function (zoom) { + if (this.infinite) { return null; } + + var b = this.projection.bounds, + s = this.scale(zoom), + min = this.transformation.transform(b.min, s), + max = this.transformation.transform(b.max, s); + + return new Bounds(min, max); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates. + + // @property code: String + // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) + // + // @property wrapLng: Number[] + // An array of two numbers defining whether the longitude (horizontal) coordinate + // axis wraps around a given range and how. Defaults to `[-180, 180]` in most + // geographical CRSs. If `undefined`, the longitude axis does not wrap around. + // + // @property wrapLat: Number[] + // Like `wrapLng`, but for the latitude (vertical) axis. + + // wrapLng: [min, max], + // wrapLat: [min, max], + + // @property infinite: Boolean + // If true, the coordinate space will be unbounded (infinite in both axes) + infinite: false, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where lat and lng has been wrapped according to the + // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. + wrapLatLng: function (latlng) { + var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, + lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, + alt = latlng.alt; + + return new LatLng(lat, lng, alt); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring + // that its center is within the CRS's bounds. + // Only accepts actual `L.LatLngBounds` instances, not arrays. + wrapLatLngBounds: function (bounds) { + var center = bounds.getCenter(), + newCenter = this.wrapLatLng(center), + latShift = center.lat - newCenter.lat, + lngShift = center.lng - newCenter.lng; + + if (latShift === 0 && lngShift === 0) { + return bounds; + } + + var sw = bounds.getSouthWest(), + ne = bounds.getNorthEast(), + newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), + newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); + + return new LatLngBounds(newSw, newNe); + } +}; + +/* + * @namespace CRS + * @crs L.CRS.Earth + * + * Serves as the base for CRS that are global such that they cover the earth. + * Can only be used as the base for other CRS and cannot be used directly, + * since it does not have a `code`, `projection` or `transformation`. `distance()` returns + * meters. + */ + +var Earth = extend({}, CRS, { + wrapLng: [-180, 180], + + // Mean Earth Radius, as recommended for use by + // the International Union of Geodesy and Geophysics, + // see https://rosettacode.org/wiki/Haversine_formula + R: 6371000, + + // distance between two geographical points using spherical law of cosines approximation + distance: function (latlng1, latlng2) { + var rad = Math.PI / 180, + lat1 = latlng1.lat * rad, + lat2 = latlng2.lat * rad, + sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), + sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), + a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, + c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return this.R * c; + } +}); + +/* + * @namespace Projection + * @projection L.Projection.SphericalMercator + * + * Spherical Mercator projection — the most common projection for online maps, + * used by almost all free and commercial tile providers. Assumes that Earth is + * a sphere. Used by the `EPSG:3857` CRS. + */ + +var earthRadius = 6378137; + +var SphericalMercator = { + + R: earthRadius, + MAX_LATITUDE: 85.0511287798, + + project: function (latlng) { + var d = Math.PI / 180, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + sin = Math.sin(lat * d); + + return new Point( + this.R * latlng.lng * d, + this.R * Math.log((1 + sin) / (1 - sin)) / 2); + }, + + unproject: function (point) { + var d = 180 / Math.PI; + + return new LatLng( + (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, + point.x * d / this.R); + }, + + bounds: (function () { + var d = earthRadius * Math.PI; + return new Bounds([-d, -d], [d, d]); + })() +}; + +/* + * @class Transformation + * @aka L.Transformation + * + * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` + * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing + * the reverse. Used by Leaflet in its projections code. + * + * @example + * + * ```js + * var transformation = L.transformation(2, 5, -1, 10), + * p = L.point(1, 2), + * p2 = transformation.transform(p), // L.point(7, 8) + * p3 = transformation.untransform(p2); // L.point(1, 2) + * ``` + */ + + +// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) +// Creates a `Transformation` object with the given coefficients. +function Transformation(a, b, c, d) { + if (isArray(a)) { + // use array properties + this._a = a[0]; + this._b = a[1]; + this._c = a[2]; + this._d = a[3]; + return; + } + this._a = a; + this._b = b; + this._c = c; + this._d = d; +} + +Transformation.prototype = { + // @method transform(point: Point, scale?: Number): Point + // Returns a transformed point, optionally multiplied by the given scale. + // Only accepts actual `L.Point` instances, not arrays. + transform: function (point, scale) { // (Point, Number) -> Point + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (point, scale) { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + // @method untransform(point: Point, scale?: Number): Point + // Returns the reverse transformation of the given point, optionally divided + // by the given scale. Only accepts actual `L.Point` instances, not arrays. + untransform: function (point, scale) { + scale = scale || 1; + return new Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } +}; + +// factory L.transformation(a: Number, b: Number, c: Number, d: Number) + +// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) +// Instantiates a Transformation object with the given coefficients. + +// @alternative +// @factory L.transformation(coefficients: Array): Transformation +// Expects an coefficients array of the form +// `[a: Number, b: Number, c: Number, d: Number]`. + +function toTransformation(a, b, c, d) { + return new Transformation(a, b, c, d); +} + +/* + * @namespace CRS + * @crs L.CRS.EPSG3857 + * + * The most common CRS for online maps, used by almost all free and commercial + * tile providers. Uses Spherical Mercator projection. Set in by default in + * Map's `crs` option. + */ + +var EPSG3857 = extend({}, Earth, { + code: 'EPSG:3857', + projection: SphericalMercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * SphericalMercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +var EPSG900913 = extend({}, EPSG3857, { + code: 'EPSG:900913' +}); + +// @namespace SVG; @section +// There are several static functions which can be called without instantiating L.SVG: + +// @function create(name: String): SVGElement +// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), +// corresponding to the class name passed. For example, using 'line' will return +// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). +function svgCreate(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); +} + +// @function pointsToPath(rings: Point[], closed: Boolean): String +// Generates a SVG path string for multiple rings, with each ring turning +// into "M..L..L.." instructions +function pointsToPath(rings, closed) { + var str = '', + i, j, len, len2, points, p; + + for (i = 0, len = rings.length; i < len; i++) { + points = rings[i]; + + for (j = 0, len2 = points.length; j < len2; j++) { + p = points[j]; + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + + // closes the ring for polygons; "x" is VML syntax + str += closed ? (Browser.svg ? 'z' : 'x') : ''; + } + + // SVG complains about empty path strings + return str || 'M0 0'; +} + +/* + * @namespace Browser + * @aka L.Browser + * + * A namespace with static properties for browser/feature detection used by Leaflet internally. + * + * @example + * + * ```js + * if (L.Browser.ielt9) { + * alert('Upgrade your browser, dude!'); + * } + * ``` + */ + +var style = document.documentElement.style; + +// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). +var ie = 'ActiveXObject' in window; + +// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. +var ielt9 = ie && !document.addEventListener; + +// @property edge: Boolean; `true` for the Edge web browser. +var edge = 'msLaunchUri' in navigator && !('documentMode' in document); + +// @property webkit: Boolean; +// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). +var webkit = userAgentContains('webkit'); + +// @property android: Boolean +// **Deprecated.** `true` for any browser running on an Android platform. +var android = userAgentContains('android'); + +// @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3. +var android23 = userAgentContains('android 2') || userAgentContains('android 3'); + +/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ +var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit +// @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome) +var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); + +// @property opera: Boolean; `true` for the Opera browser +var opera = !!window.opera; + +// @property chrome: Boolean; `true` for the Chrome browser. +var chrome = !edge && userAgentContains('chrome'); + +// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. +var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; + +// @property safari: Boolean; `true` for the Safari browser. +var safari = !chrome && userAgentContains('safari'); + +var phantom = userAgentContains('phantom'); + +// @property opera12: Boolean +// `true` for the Opera browser supporting CSS transforms (version 12 or later). +var opera12 = 'OTransition' in style; + +// @property win: Boolean; `true` when the browser is running in a Windows platform +var win = navigator.platform.indexOf('Win') === 0; + +// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. +var ie3d = ie && ('transition' in style); + +// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. +var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; + +// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. +var gecko3d = 'MozPerspective' in style; + +// @property any3d: Boolean +// `true` for all browsers supporting CSS transforms. +var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; + +// @property mobile: Boolean; `true` for all browsers running in a mobile device. +var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); + +// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. +var mobileWebkit = mobile && webkit; + +// @property mobileWebkit3d: Boolean +// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. +var mobileWebkit3d = mobile && webkit3d; + +// @property msPointer: Boolean +// `true` for browsers implementing the Microsoft touch events model (notably IE10). +var msPointer = !window.PointerEvent && window.MSPointerEvent; + +// @property pointer: Boolean +// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). +var pointer = !!(window.PointerEvent || msPointer); + +// @property touchNative: Boolean +// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). +// **This does not necessarily mean** that the browser is running in a computer with +// a touchscreen, it only means that the browser is capable of understanding +// touch events. +var touchNative = 'ontouchstart' in window || !!window.TouchEvent; + +// @property touch: Boolean +// `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events. +// Note: pointer events will be preferred (if available), and processed for all `touch*` listeners. +var touch = !window.L_NO_TOUCH && (touchNative || pointer); + +// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. +var mobileOpera = mobile && opera; + +// @property mobileGecko: Boolean +// `true` for gecko-based browsers running in a mobile device. +var mobileGecko = mobile && gecko; + +// @property retina: Boolean +// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. +var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; + +// @property passiveEvents: Boolean +// `true` for browsers that support passive events. +var passiveEvents = (function () { + var supportsPassiveOption = false; + try { + var opts = Object.defineProperty({}, 'passive', { + get: function () { // eslint-disable-line getter-return + supportsPassiveOption = true; + } + }); + window.addEventListener('testPassiveEventSupport', falseFn, opts); + window.removeEventListener('testPassiveEventSupport', falseFn, opts); + } catch (e) { + // Errors can safely be ignored since this is only a browser support test. + } + return supportsPassiveOption; +}()); + +// @property canvas: Boolean +// `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). +var canvas$1 = (function () { + return !!document.createElement('canvas').getContext; +}()); + +// @property svg: Boolean +// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). +var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect); + +var inlineSvg = !!svg$1 && (function () { + var div = document.createElement('div'); + div.innerHTML = ''; + return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg'; +})(); + +// @property vml: Boolean +// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). +var vml = !svg$1 && (function () { + try { + var div = document.createElement('div'); + div.innerHTML = ''; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + + } catch (e) { + return false; + } +}()); + + +// @property mac: Boolean; `true` when the browser is running in a Mac platform +var mac = navigator.platform.indexOf('Mac') === 0; + +// @property mac: Boolean; `true` when the browser is running in a Linux platform +var linux = navigator.platform.indexOf('Linux') === 0; + +function userAgentContains(str) { + return navigator.userAgent.toLowerCase().indexOf(str) >= 0; +} + + +var Browser = { + ie: ie, + ielt9: ielt9, + edge: edge, + webkit: webkit, + android: android, + android23: android23, + androidStock: androidStock, + opera: opera, + chrome: chrome, + gecko: gecko, + safari: safari, + phantom: phantom, + opera12: opera12, + win: win, + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + any3d: any3d, + mobile: mobile, + mobileWebkit: mobileWebkit, + mobileWebkit3d: mobileWebkit3d, + msPointer: msPointer, + pointer: pointer, + touch: touch, + touchNative: touchNative, + mobileOpera: mobileOpera, + mobileGecko: mobileGecko, + retina: retina, + passiveEvents: passiveEvents, + canvas: canvas$1, + svg: svg$1, + vml: vml, + inlineSvg: inlineSvg, + mac: mac, + linux: linux +}; + +/* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + +var POINTER_DOWN = Browser.msPointer ? 'MSPointerDown' : 'pointerdown'; +var POINTER_MOVE = Browser.msPointer ? 'MSPointerMove' : 'pointermove'; +var POINTER_UP = Browser.msPointer ? 'MSPointerUp' : 'pointerup'; +var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel'; +var pEvent = { + touchstart : POINTER_DOWN, + touchmove : POINTER_MOVE, + touchend : POINTER_UP, + touchcancel : POINTER_CANCEL +}; +var handle = { + touchstart : _onPointerStart, + touchmove : _handlePointer, + touchend : _handlePointer, + touchcancel : _handlePointer +}; +var _pointers = {}; +var _pointerDocListener = false; + +// Provides a touch events wrapper for (ms)pointer events. +// ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 + +function addPointerListener(obj, type, handler) { + if (type === 'touchstart') { + _addPointerDocListener(); + } + if (!handle[type]) { + console.warn('wrong event specified:', type); + return falseFn; + } + handler = handle[type].bind(this, handler); + obj.addEventListener(pEvent[type], handler, false); + return handler; +} + +function removePointerListener(obj, type, handler) { + if (!pEvent[type]) { + console.warn('wrong event specified:', type); + return; + } + obj.removeEventListener(pEvent[type], handler, false); +} + +function _globalPointerDown(e) { + _pointers[e.pointerId] = e; +} + +function _globalPointerMove(e) { + if (_pointers[e.pointerId]) { + _pointers[e.pointerId] = e; + } +} + +function _globalPointerUp(e) { + delete _pointers[e.pointerId]; +} + +function _addPointerDocListener() { + // need to keep track of what pointers and how many are active to provide e.touches emulation + if (!_pointerDocListener) { + // we listen document as any drags that end by moving the touch off the screen get fired there + document.addEventListener(POINTER_DOWN, _globalPointerDown, true); + document.addEventListener(POINTER_MOVE, _globalPointerMove, true); + document.addEventListener(POINTER_UP, _globalPointerUp, true); + document.addEventListener(POINTER_CANCEL, _globalPointerUp, true); + + _pointerDocListener = true; + } +} + +function _handlePointer(handler, e) { + if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; } + + e.touches = []; + for (var i in _pointers) { + e.touches.push(_pointers[i]); + } + e.changedTouches = [e]; + + handler(e); +} + +function _onPointerStart(handler, e) { + // IE10 specific: MsTouch needs preventDefault. See #2000 + if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) { + preventDefault(e); + } + _handlePointer(handler, e); +} + +/* + * Extends the event handling code with double tap support for mobile browsers. + * + * Note: currently most browsers fire native dblclick, with only a few exceptions + * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386) + */ + +function makeDblclick(event) { + // in modern browsers `type` cannot be just overridden: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only + var newEvent = {}, + prop, i; + for (i in event) { + prop = event[i]; + newEvent[i] = prop && prop.bind ? prop.bind(event) : prop; + } + event = newEvent; + newEvent.type = 'dblclick'; + newEvent.detail = 2; + newEvent.isTrusted = false; + newEvent._simulated = true; // for debug purposes + return newEvent; +} + +var delay = 200; +function addDoubleTapListener(obj, handler) { + // Most browsers handle double tap natively + obj.addEventListener('dblclick', handler); + + // On some platforms the browser doesn't fire native dblclicks for touch events. + // It seems that in all such cases `detail` property of `click` event is always `1`. + // So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed. + var last = 0, + detail; + function simDblclick(e) { + if (e.detail !== 1) { + detail = e.detail; // keep in sync to avoid false dblclick in some cases + return; + } + + if (e.pointerType === 'mouse' || + (e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) { + + return; + } + + // When clicking on an , the browser generates a click on its + //
` is submitted). +// Use it inside listener functions. +function preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; +} + +// @function stop(ev: DOMEvent): this +// Does `stopPropagation` and `preventDefault` at the same time. +function stop(e) { + preventDefault(e); + stopPropagation(e); + return this; +} + +// @function getPropagationPath(ev: DOMEvent): Array +// Compatibility polyfill for [`Event.composedPath()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath). +// Returns an array containing the `HTMLElement`s that the given DOM event +// should propagate to (if not stopped). +function getPropagationPath(ev) { + if (ev.composedPath) { + return ev.composedPath(); + } + + var path = []; + var el = ev.target; + + while (el) { + path.push(el); + el = el.parentNode; + } + return path; +} + + +// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point +// Gets normalized mouse position from a DOM event relative to the +// `container` (border excluded) or to the whole page if not specified. +function getMousePosition(e, container) { + if (!container) { + return new Point(e.clientX, e.clientY); + } + + var scale = getScale(container), + offset = scale.boundingClientRect; // left and top values are in page scale (like the event clientX/Y) + + return new Point( + // offset.left/top values are in page scale (like clientX/Y), + // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies). + (e.clientX - offset.left) / scale.x - container.clientLeft, + (e.clientY - offset.top) / scale.y - container.clientTop + ); +} + + +// except , Safari and +// We need double the scroll pixels (see #7403 and #4538) for all Browsers +// except OSX (Mac) -> 3x, Chrome running on Linux 1x + +var wheelPxFactor = + (Browser.linux && Browser.chrome) ? window.devicePixelRatio : + Browser.mac ? window.devicePixelRatio * 3 : + window.devicePixelRatio > 0 ? 2 * window.devicePixelRatio : 1; +// @function getWheelDelta(ev: DOMEvent): Number +// Gets normalized wheel delta from a wheel DOM event, in vertical +// pixels scrolled (negative if scrolling down). +// Events from pointing devices without precise scrolling are mapped to +// a best guess of 60 pixels. +function getWheelDelta(e) { + return (Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta + (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels + (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines + (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages + (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events + e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels + (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines + e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages + 0; +} + +// check if element really left/entered the event target (for mouseenter/mouseleave) +function isExternalTarget(el, e) { + + var related = e.relatedTarget; + + if (!related) { return true; } + + try { + while (related && (related !== el)) { + related = related.parentNode; + } + } catch (err) { + return false; + } + return (related !== el); +} + +var DomEvent = { + __proto__: null, + on: on, + off: off, + stopPropagation: stopPropagation, + disableScrollPropagation: disableScrollPropagation, + disableClickPropagation: disableClickPropagation, + preventDefault: preventDefault, + stop: stop, + getPropagationPath: getPropagationPath, + getMousePosition: getMousePosition, + getWheelDelta: getWheelDelta, + isExternalTarget: isExternalTarget, + addListener: on, + removeListener: off +}; + +/* + * @class PosAnimation + * @aka L.PosAnimation + * @inherits Evented + * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. + * + * @example + * ```js + * var myPositionMarker = L.marker([48.864716, 2.294694]).addTo(map); + * + * myPositionMarker.on("click", function() { + * var pos = map.latLngToLayerPoint(myPositionMarker.getLatLng()); + * pos.y -= 25; + * var fx = new L.PosAnimation(); + * + * fx.once('end',function() { + * pos.y += 25; + * fx.run(myPositionMarker._icon, pos, 0.8); + * }); + * + * fx.run(myPositionMarker._icon, pos, 0.3); + * }); + * + * ``` + * + * @constructor L.PosAnimation() + * Creates a `PosAnimation` object. + * + */ + +var PosAnimation = Evented.extend({ + + // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) + // Run an animation of a given element to a new position, optionally setting + // duration in seconds (`0.25` by default) and easing linearity factor (3rd + // argument of the [cubic bezier curve](https://cubic-bezier.com/#0,0,.5,1), + // `0.5` by default). + run: function (el, newPos, duration, easeLinearity) { + this.stop(); + + this._el = el; + this._inProgress = true; + this._duration = duration || 0.25; + this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); + + this._startPos = getPosition(el); + this._offset = newPos.subtract(this._startPos); + this._startTime = +new Date(); + + // @event start: Event + // Fired when the animation starts + this.fire('start'); + + this._animate(); + }, + + // @method stop() + // Stops the animation (if currently running). + stop: function () { + if (!this._inProgress) { return; } + + this._step(true); + this._complete(); + }, + + _animate: function () { + // animation loop + this._animId = requestAnimFrame(this._animate, this); + this._step(); + }, + + _step: function (round) { + var elapsed = (+new Date()) - this._startTime, + duration = this._duration * 1000; + + if (elapsed < duration) { + this._runFrame(this._easeOut(elapsed / duration), round); + } else { + this._runFrame(1); + this._complete(); + } + }, + + _runFrame: function (progress, round) { + var pos = this._startPos.add(this._offset.multiplyBy(progress)); + if (round) { + pos._round(); + } + setPosition(this._el, pos); + + // @event step: Event + // Fired continuously during the animation. + this.fire('step'); + }, + + _complete: function () { + cancelAnimFrame(this._animId); + + this._inProgress = false; + // @event end: Event + // Fired when the animation ends. + this.fire('end'); + }, + + _easeOut: function (t) { + return 1 - Math.pow(1 - t, this._easeOutPower); + } +}); + +/* + * @class Map + * @aka L.Map + * @inherits Evented + * + * The central class of the API — it is used to create a map on a page and manipulate it. + * + * @example + * + * ```js + * // initialize the map on the "map" div with a given center and zoom + * var map = L.map('map', { + * center: [51.505, -0.09], + * zoom: 13 + * }); + * ``` + * + */ + +var Map = Evented.extend({ + + options: { + // @section Map State Options + // @option crs: CRS = L.CRS.EPSG3857 + // The [Coordinate Reference System](#crs) to use. Don't change this if you're not + // sure what it means. + crs: EPSG3857, + + // @option center: LatLng = undefined + // Initial geographic center of the map + center: undefined, + + // @option zoom: Number = undefined + // Initial map zoom level + zoom: undefined, + + // @option minZoom: Number = * + // Minimum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the lowest of their `minZoom` options will be used instead. + minZoom: undefined, + + // @option maxZoom: Number = * + // Maximum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the highest of their `maxZoom` options will be used instead. + maxZoom: undefined, + + // @option layers: Layer[] = [] + // Array of layers that will be added to the map initially + layers: [], + + // @option maxBounds: LatLngBounds = null + // When this option is set, the map restricts the view to the given + // geographical bounds, bouncing the user back if the user tries to pan + // outside the view. To set the restriction dynamically, use + // [`setMaxBounds`](#map-setmaxbounds) method. + maxBounds: undefined, + + // @option renderer: Renderer = * + // The default method for drawing vector layers on the map. `L.SVG` + // or `L.Canvas` by default depending on browser support. + renderer: undefined, + + + // @section Animation Options + // @option zoomAnimation: Boolean = true + // Whether the map zoom animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + zoomAnimation: true, + + // @option zoomAnimationThreshold: Number = 4 + // Won't animate zoom if the zoom difference exceeds this value. + zoomAnimationThreshold: 4, + + // @option fadeAnimation: Boolean = true + // Whether the tile fade animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + fadeAnimation: true, + + // @option markerZoomAnimation: Boolean = true + // Whether markers animate their zoom with the zoom animation, if disabled + // they will disappear for the length of the animation. By default it's + // enabled in all browsers that support CSS3 Transitions except Android. + markerZoomAnimation: true, + + // @option transform3DLimit: Number = 2^23 + // Defines the maximum size of a CSS translation transform. The default + // value should not be changed unless a web browser positions layers in + // the wrong place after doing a large `panBy`. + transform3DLimit: 8388608, // Precision limit of a 32-bit float + + // @section Interaction Options + // @option zoomSnap: Number = 1 + // Forces the map's zoom level to always be a multiple of this, particularly + // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. + // By default, the zoom level snaps to the nearest integer; lower values + // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` + // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. + zoomSnap: 1, + + // @option zoomDelta: Number = 1 + // Controls how much the map's zoom level will change after a + // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` + // or `-` on the keyboard, or using the [zoom controls](#control-zoom). + // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. + zoomDelta: 1, + + // @option trackResize: Boolean = true + // Whether the map automatically handles browser window resize to update itself. + trackResize: true + }, + + initialize: function (id, options) { // (HTMLElement or String, Object) + options = setOptions(this, options); + + // Make sure to assign internal flags at the beginning, + // to avoid inconsistent state in some edge cases. + this._handlers = []; + this._layers = {}; + this._zoomBoundLayers = {}; + this._sizeChanged = true; + + this._initContainer(id); + this._initLayout(); + + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = bind(this._onResize, this); + + this._initEvents(); + + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); + } + + if (options.zoom !== undefined) { + this._zoom = this._limitZoom(options.zoom); + } + + if (options.center && options.zoom !== undefined) { + this.setView(toLatLng(options.center), options.zoom, {reset: true}); + } + + this.callInitHooks(); + + // don't animate on browsers without hardware-accelerated transitions or old Android/Opera + this._zoomAnimated = TRANSITION && Browser.any3d && !Browser.mobileOpera && + this.options.zoomAnimation; + + // zoom transitions run with the same duration for all layers, so if one of transitionend events + // happens after starting zoom animation (propagating to the map pane), we know that it ended globally + if (this._zoomAnimated) { + this._createAnimProxy(); + on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); + } + + this._addLayers(this.options.layers); + }, + + + // @section Methods for modifying map state + + // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) with the given + // animation options. + setView: function (center, zoom, options) { + + zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); + center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); + options = options || {}; + + this._stop(); + + if (this._loaded && !options.reset && options !== true) { + + if (options.animate !== undefined) { + options.zoom = extend({animate: options.animate}, options.zoom); + options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); + } + + // try animating pan or zoom + var moved = (this._zoom !== zoom) ? + this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : + this._tryAnimatedPan(center, options.pan); + + if (moved) { + // prevent resize handler call, the view will refresh after animation anyway + clearTimeout(this._sizeTimer); + return this; + } + } + + // animation didn't start, just reset the map view + this._resetView(center, zoom, options.pan && options.pan.noMoveStart); + + return this; + }, + + // @method setZoom(zoom: Number, options?: Zoom/pan options): this + // Sets the zoom of the map. + setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = zoom; + return this; + } + return this.setView(this.getCenter(), zoom, {zoom: options}); + }, + + // @method zoomIn(delta?: Number, options?: Zoom options): this + // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomIn: function (delta, options) { + delta = delta || (Browser.any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom + delta, options); + }, + + // @method zoomOut(delta?: Number, options?: Zoom options): this + // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomOut: function (delta, options) { + delta = delta || (Browser.any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom - delta, options); + }, + + // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified geographical point on the map + // stationary (e.g. used internally for scroll zoom and double-click zoom). + // @alternative + // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. + setZoomAround: function (latlng, zoom, options) { + var scale = this.getZoomScale(zoom), + viewHalf = this.getSize().divideBy(2), + containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), + + centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); + + return this.setView(newCenter, zoom, {zoom: options}); + }, + + _getBoundsCenterZoom: function (bounds, options) { + + options = options || {}; + bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); + + var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), + + zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); + + zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; + + if (zoom === Infinity) { + return { + center: bounds.getCenter(), + zoom: zoom + }; + } + + var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), + + swPoint = this.project(bounds.getSouthWest(), zoom), + nePoint = this.project(bounds.getNorthEast(), zoom), + center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); + + return { + center: center, + zoom: zoom + }; + }, + + // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets a map view that contains the given geographical bounds with the + // maximum zoom level possible. + fitBounds: function (bounds, options) { + + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + throw new Error('Bounds are not valid.'); + } + + var target = this._getBoundsCenterZoom(bounds, options); + return this.setView(target.center, target.zoom, options); + }, + + // @method fitWorld(options?: fitBounds options): this + // Sets a map view that mostly contains the whole world with the maximum + // zoom level possible. + fitWorld: function (options) { + return this.fitBounds([[-90, -180], [90, 180]], options); + }, + + // @method panTo(latlng: LatLng, options?: Pan options): this + // Pans the map to a given center. + panTo: function (center, options) { // (LatLng) + return this.setView(center, this._zoom, {pan: options}); + }, + + // @method panBy(offset: Point, options?: Pan options): this + // Pans the map by a given number of pixels (animated). + panBy: function (offset, options) { + offset = toPoint(offset).round(); + options = options || {}; + + if (!offset.x && !offset.y) { + return this.fire('moveend'); + } + // If we pan too far, Chrome gets issues with tiles + // and makes them disappear or appear in the wrong place (slightly offset) #2602 + if (options.animate !== true && !this.getSize().contains(offset)) { + this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); + return this; + } + + if (!this._panAnim) { + this._panAnim = new PosAnimation(); + + this._panAnim.on({ + 'step': this._onPanTransitionStep, + 'end': this._onPanTransitionEnd + }, this); + } + + // don't fire movestart if animating inertia + if (!options.noMoveStart) { + this.fire('movestart'); + } + + // animate pan unless animate: false specified + if (options.animate !== false) { + addClass(this._mapPane, 'leaflet-pan-anim'); + + var newPos = this._getMapPanePos().subtract(offset).round(); + this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); + } else { + this._rawPanBy(offset); + this.fire('move').fire('moveend'); + } + + return this; + }, + + // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) performing a smooth + // pan-zoom animation. + flyTo: function (targetCenter, targetZoom, options) { + + options = options || {}; + if (options.animate === false || !Browser.any3d) { + return this.setView(targetCenter, targetZoom, options); + } + + this._stop(); + + var from = this.project(this.getCenter()), + to = this.project(targetCenter), + size = this.getSize(), + startZoom = this._zoom; + + targetCenter = toLatLng(targetCenter); + targetZoom = targetZoom === undefined ? startZoom : targetZoom; + + var w0 = Math.max(size.x, size.y), + w1 = w0 * this.getZoomScale(startZoom, targetZoom), + u1 = (to.distanceTo(from)) || 1, + rho = 1.42, + rho2 = rho * rho; + + function r(i) { + var s1 = i ? -1 : 1, + s2 = i ? w1 : w0, + t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, + b1 = 2 * s2 * rho2 * u1, + b = t1 / b1, + sq = Math.sqrt(b * b + 1) - b; + + // workaround for floating point precision bug when sq = 0, log = -Infinite, + // thus triggering an infinite loop in flyTo + var log = sq < 0.000000001 ? -18 : Math.log(sq); + + return log; + } + + function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } + function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } + function tanh(n) { return sinh(n) / cosh(n); } + + var r0 = r(0); + + function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } + function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } + + function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } + + var start = Date.now(), + S = (r(1) - r0) / rho, + duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; + + function frame() { + var t = (Date.now() - start) / duration, + s = easeOut(t) * S; + + if (t <= 1) { + this._flyToFrame = requestAnimFrame(frame, this); + + this._move( + this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), + this.getScaleZoom(w0 / w(s), startZoom), + {flyTo: true}); + + } else { + this + ._move(targetCenter, targetZoom) + ._moveEnd(true); + } + } + + this._moveStart(true, options.noMoveStart); + + frame.call(this); + return this; + }, + + // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), + // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). + flyToBounds: function (bounds, options) { + var target = this._getBoundsCenterZoom(bounds, options); + return this.flyTo(target.center, target.zoom, options); + }, + + // @method setMaxBounds(bounds: LatLngBounds): this + // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). + setMaxBounds: function (bounds) { + bounds = toLatLngBounds(bounds); + + if (this.listens('moveend', this._panInsideMaxBounds)) { + this.off('moveend', this._panInsideMaxBounds); + } + + if (!bounds.isValid()) { + this.options.maxBounds = null; + return this; + } + + this.options.maxBounds = bounds; + + if (this._loaded) { + this._panInsideMaxBounds(); + } + + return this.on('moveend', this._panInsideMaxBounds); + }, + + // @method setMinZoom(zoom: Number): this + // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). + setMinZoom: function (zoom) { + var oldZoom = this.options.minZoom; + this.options.minZoom = zoom; + + if (this._loaded && oldZoom !== zoom) { + this.fire('zoomlevelschange'); + + if (this.getZoom() < this.options.minZoom) { + return this.setZoom(zoom); + } + } + + return this; + }, + + // @method setMaxZoom(zoom: Number): this + // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). + setMaxZoom: function (zoom) { + var oldZoom = this.options.maxZoom; + this.options.maxZoom = zoom; + + if (this._loaded && oldZoom !== zoom) { + this.fire('zoomlevelschange'); + + if (this.getZoom() > this.options.maxZoom) { + return this.setZoom(zoom); + } + } + + return this; + }, + + // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this + // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. + panInsideBounds: function (bounds, options) { + this._enforcingBounds = true; + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); + + if (!center.equals(newCenter)) { + this.panTo(newCenter, options); + } + + this._enforcingBounds = false; + return this; + }, + + // @method panInside(latlng: LatLng, options?: padding options): this + // Pans the map the minimum amount to make the `latlng` visible. Use + // padding options to fit the display to more restricted bounds. + // If `latlng` is already within the (optionally padded) display bounds, + // the map will not be panned. + panInside: function (latlng, options) { + options = options || {}; + + var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), + pixelCenter = this.project(this.getCenter()), + pixelPoint = this.project(latlng), + pixelBounds = this.getPixelBounds(), + paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]), + paddedSize = paddedBounds.getSize(); + + if (!paddedBounds.contains(pixelPoint)) { + this._enforcingBounds = true; + var centerOffset = pixelPoint.subtract(paddedBounds.getCenter()); + var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize); + pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x; + pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y; + this.panTo(this.unproject(pixelCenter), options); + this._enforcingBounds = false; + } + return this; + }, + + // @method invalidateSize(options: Zoom/pan options): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. If `options.pan` is `false`, panning will not occur. + // If `options.debounceMoveend` is `true`, it will delay `moveend` event so + // that it doesn't happen often even if the method is called many + // times in a row. + + // @alternative + // @method invalidateSize(animate: Boolean): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. + invalidateSize: function (options) { + if (!this._loaded) { return this; } + + options = extend({ + animate: false, + pan: true + }, options === true ? {animate: true} : options); + + var oldSize = this.getSize(); + this._sizeChanged = true; + this._lastCenter = null; + + var newSize = this.getSize(), + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); + + if (!offset.x && !offset.y) { return this; } + + if (options.animate && options.pan) { + this.panBy(offset); + + } else { + if (options.pan) { + this._rawPanBy(offset); + } + + this.fire('move'); + + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } + } + + // @section Map state change events + // @event resize: ResizeEvent + // Fired when the map is resized. + return this.fire('resize', { + oldSize: oldSize, + newSize: newSize + }); + }, + + // @section Methods for modifying map state + // @method stop(): this + // Stops the currently running `panTo` or `flyTo` animation, if any. + stop: function () { + this.setZoom(this._limitZoom(this._zoom)); + if (!this.options.zoomSnap) { + this.fire('viewreset'); + } + return this._stop(); + }, + + // @section Geolocation methods + // @method locate(options?: Locate options): this + // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) + // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, + // and optionally sets the map view to the user's location with respect to + // detection accuracy (or to the world view if geolocation failed). + // Note that, if your page doesn't use HTTPS, this method will fail in + // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) + // See `Locate options` for more details. + locate: function (options) { + + options = this._locateOptions = extend({ + timeout: 10000, + watch: false + // setView: false + // maxZoom: + // maximumAge: 0 + // enableHighAccuracy: false + }, options); + + if (!('geolocation' in navigator)) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + var onResponse = bind(this._handleGeolocationResponse, this), + onError = bind(this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + return this; + }, + + // @method stopLocate(): this + // Stops watching location previously initiated by `map.locate({watch: true})` + // and aborts resetting the map view if map.locate was called with + // `{setView: true}`. + stopLocate: function () { + if (navigator.geolocation && navigator.geolocation.clearWatch) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + if (this._locateOptions) { + this._locateOptions.setView = false; + } + return this; + }, + + _handleGeolocationError: function (error) { + if (!this._container._leaflet_id) { return; } + + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); + + if (this._locateOptions.setView && !this._loaded) { + this.fitWorld(); + } + + // @section Location events + // @event locationerror: ErrorEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) failed. + this.fire('locationerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); + }, + + _handleGeolocationResponse: function (pos) { + if (!this._container._leaflet_id) { return; } + + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new LatLng(lat, lng), + bounds = latlng.toBounds(pos.coords.accuracy * 2), + options = this._locateOptions; + + if (options.setView) { + var zoom = this.getBoundsZoom(bounds); + this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); + } + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + // @event locationfound: LocationEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) + // went successfully. + this.fire('locationfound', data); + }, + + // TODO Appropriate docs section? + // @section Other Methods + // @method addHandler(name: String, HandlerClass: Function): this + // Adds a new `Handler` to the map, given its name and constructor function. + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return this; } + + var handler = this[name] = new HandlerClass(this); + + this._handlers.push(handler); + + if (this.options[name]) { + handler.enable(); + } + + return this; + }, + + // @method remove(): this + // Destroys the map and clears all related event listeners. + remove: function () { + + this._initEvents(true); + if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); } + + if (this._containerId !== this._container._leaflet_id) { + throw new Error('Map container is being reused by another instance'); + } + + try { + // throws error in IE6-8 + delete this._container._leaflet_id; + delete this._containerId; + } catch (e) { + /*eslint-disable */ + this._container._leaflet_id = undefined; + /* eslint-enable */ + this._containerId = undefined; + } + + if (this._locationWatchId !== undefined) { + this.stopLocate(); + } + + this._stop(); + + remove(this._mapPane); + + if (this._clearControlPos) { + this._clearControlPos(); + } + if (this._resizeRequest) { + cancelAnimFrame(this._resizeRequest); + this._resizeRequest = null; + } + + this._clearHandlers(); + + if (this._loaded) { + // @section Map state change events + // @event unload: Event + // Fired when the map is destroyed with [remove](#map-remove) method. + this.fire('unload'); + } + + var i; + for (i in this._layers) { + this._layers[i].remove(); + } + for (i in this._panes) { + remove(this._panes[i]); + } + + this._layers = []; + this._panes = []; + delete this._mapPane; + delete this._renderer; + + return this; + }, + + // @section Other Methods + // @method createPane(name: String, container?: HTMLElement): HTMLElement + // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, + // then returns it. The pane is created as a child of `container`, or + // as a child of the main map pane if not set. + createPane: function (name, container) { + var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), + pane = create$1('div', className, container || this._mapPane); + + if (name) { + this._panes[name] = pane; + } + return pane; + }, + + // @section Methods for Getting Map State + + // @method getCenter(): LatLng + // Returns the geographical center of the map view + getCenter: function () { + this._checkIfLoaded(); + + if (this._lastCenter && !this._moved()) { + return this._lastCenter.clone(); + } + return this.layerPointToLatLng(this._getCenterLayerPoint()); + }, + + // @method getZoom(): Number + // Returns the current zoom level of the map view + getZoom: function () { + return this._zoom; + }, + + // @method getBounds(): LatLngBounds + // Returns the geographical bounds visible in the current map view + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); + + return new LatLngBounds(sw, ne); + }, + + // @method getMinZoom(): Number + // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. + getMinZoom: function () { + return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; + }, + + // @method getMaxZoom(): Number + // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). + getMaxZoom: function () { + return this.options.maxZoom === undefined ? + (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : + this.options.maxZoom; + }, + + // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number + // Returns the maximum zoom level on which the given bounds fit to the map + // view in its entirety. If `inside` (optional) is set to `true`, the method + // instead returns the minimum zoom level on which the map view fits into + // the given bounds in its entirety. + getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number + bounds = toLatLngBounds(bounds); + padding = toPoint(padding || [0, 0]); + + var zoom = this.getZoom() || 0, + min = this.getMinZoom(), + max = this.getMaxZoom(), + nw = bounds.getNorthWest(), + se = bounds.getSouthEast(), + size = this.getSize().subtract(padding), + boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), + snap = Browser.any3d ? this.options.zoomSnap : 1, + scalex = size.x / boundsSize.x, + scaley = size.y / boundsSize.y, + scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); + + zoom = this.getScaleZoom(scale, zoom); + + if (snap) { + zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level + zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; + } + + return Math.max(min, Math.min(max, zoom)); + }, + + // @method getSize(): Point + // Returns the current size of the map container (in pixels). + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new Point( + this._container.clientWidth || 0, + this._container.clientHeight || 0); + + this._sizeChanged = false; + } + return this._size.clone(); + }, + + // @method getPixelBounds(): Bounds + // Returns the bounds of the current map view in projected pixel + // coordinates (sometimes useful in layer and overlay implementations). + getPixelBounds: function (center, zoom) { + var topLeftPoint = this._getTopLeftPoint(center, zoom); + return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + }, + + // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to + // the map pane? "left point of the map layer" can be confusing, specially + // since there can be negative offsets. + // @method getPixelOrigin(): Point + // Returns the projected pixel coordinates of the top left point of + // the map layer (useful in custom layer and overlay implementations). + getPixelOrigin: function () { + this._checkIfLoaded(); + return this._pixelOrigin; + }, + + // @method getPixelWorldBounds(zoom?: Number): Bounds + // Returns the world's bounds in pixel coordinates for zoom level `zoom`. + // If `zoom` is omitted, the map's current zoom level is used. + getPixelWorldBounds: function (zoom) { + return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); + }, + + // @section Other Methods + + // @method getPane(pane: String|HTMLElement): HTMLElement + // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). + getPane: function (pane) { + return typeof pane === 'string' ? this._panes[pane] : pane; + }, + + // @method getPanes(): Object + // Returns a plain object containing the names of all [panes](#map-pane) as keys and + // the panes as values. + getPanes: function () { + return this._panes; + }, + + // @method getContainer: HTMLElement + // Returns the HTML element that contains the map. + getContainer: function () { + return this._container; + }, + + + // @section Conversion Methods + + // @method getZoomScale(toZoom: Number, fromZoom: Number): Number + // Returns the scale factor to be applied to a map transition from zoom level + // `fromZoom` to `toZoom`. Used internally to help with zoom animations. + getZoomScale: function (toZoom, fromZoom) { + // TODO replace with universal implementation after refactoring projections + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + return crs.scale(toZoom) / crs.scale(fromZoom); + }, + + // @method getScaleZoom(scale: Number, fromZoom: Number): Number + // Returns the zoom level that the map would end up at, if it is at `fromZoom` + // level and everything is scaled by a factor of `scale`. Inverse of + // [`getZoomScale`](#map-getZoomScale). + getScaleZoom: function (scale, fromZoom) { + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + var zoom = crs.zoom(scale * crs.scale(fromZoom)); + return isNaN(zoom) ? Infinity : zoom; + }, + + // @method project(latlng: LatLng, zoom: Number): Point + // Projects a geographical coordinate `LatLng` according to the projection + // of the map's CRS, then scales it according to `zoom` and the CRS's + // `Transformation`. The result is pixel coordinate relative to + // the CRS origin. + project: function (latlng, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); + }, + + // @method unproject(point: Point, zoom: Number): LatLng + // Inverse of [`project`](#map-project). + unproject: function (point, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(toPoint(point), zoom); + }, + + // @method layerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding geographical coordinate (for the current zoom level). + layerPointToLatLng: function (point) { + var projectedPoint = toPoint(point).add(this.getPixelOrigin()); + return this.unproject(projectedPoint); + }, + + // @method latLngToLayerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the [origin pixel](#map-getpixelorigin). + latLngToLayerPoint: function (latlng) { + var projectedPoint = this.project(toLatLng(latlng))._round(); + return projectedPoint._subtract(this.getPixelOrigin()); + }, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the + // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the + // CRS's bounds. + // By default this means longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees. + wrapLatLng: function (latlng) { + return this.options.crs.wrapLatLng(toLatLng(latlng)); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring that + // its center is within the CRS's bounds. + // By default this means the center longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees, and the majority of the bounds + // overlaps the CRS's bounds. + wrapLatLngBounds: function (latlng) { + return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates according to + // the map's CRS. By default this measures distance in meters. + distance: function (latlng1, latlng2) { + return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); + }, + + // @method containerPointToLayerPoint(point: Point): Point + // Given a pixel coordinate relative to the map container, returns the corresponding + // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). + containerPointToLayerPoint: function (point) { // (Point) + return toPoint(point).subtract(this._getMapPanePos()); + }, + + // @method layerPointToContainerPoint(point: Point): Point + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding pixel coordinate relative to the map container. + layerPointToContainerPoint: function (point) { // (Point) + return toPoint(point).add(this._getMapPanePos()); + }, + + // @method containerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the map container, returns + // the corresponding geographical coordinate (for the current zoom level). + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(toPoint(point)); + return this.layerPointToLatLng(layerPoint); + }, + + // @method latLngToContainerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the map container. + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); + }, + + // @method mouseEventToContainerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to the + // map container where the event took place. + mouseEventToContainerPoint: function (e) { + return getMousePosition(e, this._container); + }, + + // @method mouseEventToLayerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to + // the [origin pixel](#map-getpixelorigin) where the event took place. + mouseEventToLayerPoint: function (e) { + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, + + // @method mouseEventToLatLng(ev: MouseEvent): LatLng + // Given a MouseEvent object, returns geographical coordinate where the + // event took place. + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, + + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = get(id); + + if (!container) { + throw new Error('Map container not found.'); + } else if (container._leaflet_id) { + throw new Error('Map container is already initialized.'); + } + + on(container, 'scroll', this._onScroll, this); + this._containerId = stamp(container); + }, + + _initLayout: function () { + var container = this._container; + + this._fadeAnimated = this.options.fadeAnimation && Browser.any3d; + + addClass(container, 'leaflet-container' + + (Browser.touch ? ' leaflet-touch' : '') + + (Browser.retina ? ' leaflet-retina' : '') + + (Browser.ielt9 ? ' leaflet-oldie' : '') + + (Browser.safari ? ' leaflet-safari' : '') + + (this._fadeAnimated ? ' leaflet-fade-anim' : '')); + + var position = getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed' && position !== 'sticky') { + container.style.position = 'relative'; + } + + this._initPanes(); + + if (this._initControlPos) { + this._initControlPos(); + } + }, + + _initPanes: function () { + var panes = this._panes = {}; + this._paneRenderers = {}; + + // @section + // + // Panes are DOM elements used to control the ordering of layers on the map. You + // can access panes with [`map.getPane`](#map-getpane) or + // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the + // [`map.createPane`](#map-createpane) method. + // + // Every map has the following default panes that differ only in zIndex. + // + // @pane mapPane: HTMLElement = 'auto' + // Pane that contains all other map panes + + this._mapPane = this.createPane('mapPane', this._container); + setPosition(this._mapPane, new Point(0, 0)); + + // @pane tilePane: HTMLElement = 200 + // Pane for `GridLayer`s and `TileLayer`s + this.createPane('tilePane'); + // @pane overlayPane: HTMLElement = 400 + // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s + this.createPane('overlayPane'); + // @pane shadowPane: HTMLElement = 500 + // Pane for overlay shadows (e.g. `Marker` shadows) + this.createPane('shadowPane'); + // @pane markerPane: HTMLElement = 600 + // Pane for `Icon`s of `Marker`s + this.createPane('markerPane'); + // @pane tooltipPane: HTMLElement = 650 + // Pane for `Tooltip`s. + this.createPane('tooltipPane'); + // @pane popupPane: HTMLElement = 700 + // Pane for `Popup`s. + this.createPane('popupPane'); + + if (!this.options.markerZoomAnimation) { + addClass(panes.markerPane, 'leaflet-zoom-hide'); + addClass(panes.shadowPane, 'leaflet-zoom-hide'); + } + }, + + + // private methods that modify map state + + // @section Map state change events + _resetView: function (center, zoom, noMoveStart) { + setPosition(this._mapPane, new Point(0, 0)); + + var loading = !this._loaded; + this._loaded = true; + zoom = this._limitZoom(zoom); + + this.fire('viewprereset'); + + var zoomChanged = this._zoom !== zoom; + this + ._moveStart(zoomChanged, noMoveStart) + ._move(center, zoom) + ._moveEnd(zoomChanged); + + // @event viewreset: Event + // Fired when the map needs to redraw its content (this usually happens + // on map zoom or load). Very useful for creating custom overlays. + this.fire('viewreset'); + + // @event load: Event + // Fired when the map is initialized (when its center and zoom are set + // for the first time). + if (loading) { + this.fire('load'); + } + }, + + _moveStart: function (zoomChanged, noMoveStart) { + // @event zoomstart: Event + // Fired when the map zoom is about to change (e.g. before zoom animation). + // @event movestart: Event + // Fired when the view of the map starts changing (e.g. user starts dragging the map). + if (zoomChanged) { + this.fire('zoomstart'); + } + if (!noMoveStart) { + this.fire('movestart'); + } + return this; + }, + + _move: function (center, zoom, data, supressEvent) { + if (zoom === undefined) { + zoom = this._zoom; + } + var zoomChanged = this._zoom !== zoom; + + this._zoom = zoom; + this._lastCenter = center; + this._pixelOrigin = this._getNewPixelOrigin(center); + + if (!supressEvent) { + // @event zoom: Event + // Fired repeatedly during any change in zoom level, + // including zoom and fly animations. + if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 + this.fire('zoom', data); + } + + // @event move: Event + // Fired repeatedly during any movement of the map, + // including pan and fly animations. + this.fire('move', data); + } else if (data && data.pinch) { // Always fire 'zoom' if pinching because #3530 + this.fire('zoom', data); + } + return this; + }, + + _moveEnd: function (zoomChanged) { + // @event zoomend: Event + // Fired when the map zoom changed, after any animations. + if (zoomChanged) { + this.fire('zoomend'); + } + + // @event moveend: Event + // Fired when the center of the map stops changing + // (e.g. user stopped dragging the map or after non-centered zoom). + return this.fire('moveend'); + }, + + _stop: function () { + cancelAnimFrame(this._flyToFrame); + if (this._panAnim) { + this._panAnim.stop(); + } + return this; + }, + + _rawPanBy: function (offset) { + setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, + + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, + + _panInsideMaxBounds: function () { + if (!this._enforcingBounds) { + this.panInsideBounds(this.options.maxBounds); + } + }, + + _checkIfLoaded: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + }, + + // DOM event handling + + // @section Interaction events + _initEvents: function (remove) { + this._targets = {}; + this._targets[stamp(this._container)] = this; + + var onOff = remove ? off : on; + + // @event click: MouseEvent + // Fired when the user clicks (or taps) the map. + // @event dblclick: MouseEvent + // Fired when the user double-clicks (or double-taps) the map. + // @event mousedown: MouseEvent + // Fired when the user pushes the mouse button on the map. + // @event mouseup: MouseEvent + // Fired when the user releases the mouse button on the map. + // @event mouseover: MouseEvent + // Fired when the mouse enters the map. + // @event mouseout: MouseEvent + // Fired when the mouse leaves the map. + // @event mousemove: MouseEvent + // Fired while the mouse moves over the map. + // @event contextmenu: MouseEvent + // Fired when the user pushes the right mouse button on the map, prevents + // default browser context menu from showing if there are listeners on + // this event. Also fired on mobile when the user holds a single touch + // for a second (also called long press). + // @event keypress: KeyboardEvent + // Fired when the user presses a key from the keyboard that produces a character value while the map is focused. + // @event keydown: KeyboardEvent + // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event, + // the `keydown` event is fired for keys that produce a character value and for keys + // that do not produce a character value. + // @event keyup: KeyboardEvent + // Fired when the user releases a key from the keyboard while the map is focused. + onOff(this._container, 'click dblclick mousedown mouseup ' + + 'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this); + + if (this.options.trackResize) { + onOff(window, 'resize', this._onResize, this); + } + + if (Browser.any3d && this.options.transform3DLimit) { + (remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); + } + }, + + _onResize: function () { + cancelAnimFrame(this._resizeRequest); + this._resizeRequest = requestAnimFrame( + function () { this.invalidateSize({debounceMoveend: true}); }, this); + }, + + _onScroll: function () { + this._container.scrollTop = 0; + this._container.scrollLeft = 0; + }, + + _onMoveEnd: function () { + var pos = this._getMapPanePos(); + if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have + // a pixel offset on very high values, see: https://jsfiddle.net/dg6r5hhb/ + this._resetView(this.getCenter(), this.getZoom()); + } + }, + + _findEventTargets: function (e, type) { + var targets = [], + target, + isHover = type === 'mouseout' || type === 'mouseover', + src = e.target || e.srcElement, + dragging = false; + + while (src) { + target = this._targets[stamp(src)]; + if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) { + // Prevent firing click after you just dragged an object. + dragging = true; + break; + } + if (target && target.listens(type, true)) { + if (isHover && !isExternalTarget(src, e)) { break; } + targets.push(target); + if (isHover) { break; } + } + if (src === this._container) { break; } + src = src.parentNode; + } + if (!targets.length && !dragging && !isHover && this.listens(type, true)) { + targets = [this]; + } + return targets; + }, + + _isClickDisabled: function (el) { + while (el && el !== this._container) { + if (el['_leaflet_disable_click']) { return true; } + el = el.parentNode; + } + }, + + _handleDOMEvent: function (e) { + var el = (e.target || e.srcElement); + if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) { + return; + } + + var type = e.type; + + if (type === 'mousedown') { + // prevents outline when clicking on keyboard-focusable element + preventOutline(el); + } + + this._fireDOMEvent(e, type); + }, + + _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], + + _fireDOMEvent: function (e, type, canvasTargets) { + + if (e.type === 'click') { + // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). + // @event preclick: MouseEvent + // Fired before mouse click on the map (sometimes useful when you + // want something to happen on click before any existing click + // handlers start running). + var synth = extend({}, e); + synth.type = 'preclick'; + this._fireDOMEvent(synth, synth.type, canvasTargets); + } + + // Find the layer the event is propagating from and its parents. + var targets = this._findEventTargets(e, type); + + if (canvasTargets) { + var filtered = []; // pick only targets with listeners + for (var i = 0; i < canvasTargets.length; i++) { + if (canvasTargets[i].listens(type, true)) { + filtered.push(canvasTargets[i]); + } + } + targets = filtered.concat(targets); + } + + if (!targets.length) { return; } + + if (type === 'contextmenu') { + preventDefault(e); + } + + var target = targets[0]; + var data = { + originalEvent: e + }; + + if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') { + var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); + data.containerPoint = isMarker ? + this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); + data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); + data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); + } + + for (i = 0; i < targets.length; i++) { + targets[i].fire(type, data, true); + if (data.originalEvent._stopped || + (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } + } + }, + + _draggableMoved: function (obj) { + obj = obj.dragging && obj.dragging.enabled() ? obj : this; + return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); + }, + + _clearHandlers: function () { + for (var i = 0, len = this._handlers.length; i < len; i++) { + this._handlers[i].disable(); + } + }, + + // @section Other Methods + + // @method whenReady(fn: Function, context?: Object): this + // Runs the given function `fn` when the map gets initialized with + // a view (center and zoom) and at least one layer, or immediately + // if it's already initialized, optionally passing a function context. + whenReady: function (callback, context) { + if (this._loaded) { + callback.call(context || this, {target: this}); + } else { + this.on('load', callback, context); + } + return this; + }, + + + // private methods for getting map state + + _getMapPanePos: function () { + return getPosition(this._mapPane) || new Point(0, 0); + }, + + _moved: function () { + var pos = this._getMapPanePos(); + return pos && !pos.equals([0, 0]); + }, + + _getTopLeftPoint: function (center, zoom) { + var pixelOrigin = center && zoom !== undefined ? + this._getNewPixelOrigin(center, zoom) : + this.getPixelOrigin(); + return pixelOrigin.subtract(this._getMapPanePos()); + }, + + _getNewPixelOrigin: function (center, zoom) { + var viewHalf = this.getSize()._divideBy(2); + return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); + }, + + _latLngToNewLayerPoint: function (latlng, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return this.project(latlng, zoom)._subtract(topLeft); + }, + + _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return toBounds([ + this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) + ]); + }, + + // layer point of the current center + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + }, + + // offset of the specified place to the current center in pixels + _getCenterOffset: function (latlng) { + return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); + }, + + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { + + if (!bounds) { return center; } + + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); + + // If offset is less than a pixel, ignore. + // This prevents unstable projections from getting into + // an infinite loop of tiny offsets. + if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) { + return center; + } + + return this.unproject(centerPoint.add(offset), zoom); + }, + + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } + + var viewBounds = this.getPixelBounds(), + newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var projectedMaxBounds = toBounds( + this.project(maxBounds.getNorthEast(), zoom), + this.project(maxBounds.getSouthWest(), zoom) + ), + minOffset = projectedMaxBounds.min.subtract(pxBounds.min), + maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), + + dx = this._rebound(minOffset.x, -maxOffset.x), + dy = this._rebound(minOffset.y, -maxOffset.y); + + return new Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(), + snap = Browser.any3d ? this.options.zoomSnap : 1; + if (snap) { + zoom = Math.round(zoom / snap) * snap; + } + return Math.max(min, Math.min(max, zoom)); + }, + + _onPanTransitionStep: function () { + this.fire('move'); + }, + + _onPanTransitionEnd: function () { + removeClass(this._mapPane, 'leaflet-pan-anim'); + this.fire('moveend'); + }, + + _tryAnimatedPan: function (center, options) { + // difference between the new and current centers in pixels + var offset = this._getCenterOffset(center)._trunc(); + + // don't animate too far unless animate: true specified in options + if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } + + this.panBy(offset, options); + + return true; + }, + + _createAnimProxy: function () { + + var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); + this._panes.mapPane.appendChild(proxy); + + this.on('zoomanim', function (e) { + var prop = TRANSFORM, + transform = this._proxy.style[prop]; + + setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + + // workaround for case when transform is the same and so transitionend event is not fired + if (transform === this._proxy.style[prop] && this._animatingZoom) { + this._onZoomTransitionEnd(); + } + }, this); + + this.on('load moveend', this._animMoveEnd, this); + + this._on('unload', this._destroyAnimProxy, this); + }, + + _destroyAnimProxy: function () { + remove(this._proxy); + this.off('load moveend', this._animMoveEnd, this); + delete this._proxy; + }, + + _animMoveEnd: function () { + var c = this.getCenter(), + z = this.getZoom(); + setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); + }, + + _catchTransitionEnd: function (e) { + if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { + this._onZoomTransitionEnd(); + } + }, + + _nothingToAnimate: function () { + return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + }, + + _tryAnimatedZoom: function (center, zoom, options) { + + if (this._animatingZoom) { return true; } + + options = options || {}; + + // don't animate if disabled, not supported or zoom difference is too large + if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || + Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + + // offset is the pixel coords of the zoom origin relative to the current center + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); + + // don't animate if the zoom origin isn't within one screen from the current center, unless forced + if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + + requestAnimFrame(function () { + this + ._moveStart(true, false) + ._animateZoom(center, zoom, true); + }, this); + + return true; + }, + + _animateZoom: function (center, zoom, startAnim, noUpdate) { + if (!this._mapPane) { return; } + + if (startAnim) { + this._animatingZoom = true; + + // remember what center/zoom to set after animation + this._animateToCenter = center; + this._animateToZoom = zoom; + + addClass(this._mapPane, 'leaflet-zoom-anim'); + } + + // @section Other Events + // @event zoomanim: ZoomAnimEvent + // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom. + this.fire('zoomanim', { + center: center, + zoom: zoom, + noUpdate: noUpdate + }); + + if (!this._tempFireZoomEvent) { + this._tempFireZoomEvent = this._zoom !== this._animateToZoom; + } + + this._move(this._animateToCenter, this._animateToZoom, undefined, true); + + // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 + setTimeout(bind(this._onZoomTransitionEnd, this), 250); + }, + + _onZoomTransitionEnd: function () { + if (!this._animatingZoom) { return; } + + if (this._mapPane) { + removeClass(this._mapPane, 'leaflet-zoom-anim'); + } + + this._animatingZoom = false; + + this._move(this._animateToCenter, this._animateToZoom, undefined, true); + + if (this._tempFireZoomEvent) { + this.fire('zoom'); + } + delete this._tempFireZoomEvent; + + this.fire('move'); + + this._moveEnd(true); + } +}); + +// @section + +// @factory L.map(id: String, options?: Map options) +// Instantiates a map object given the DOM ID of a `
` element +// and optionally an object literal with `Map options`. +// +// @alternative +// @factory L.map(el: HTMLElement, options?: Map options) +// Instantiates a map object given an instance of a `
` HTML element +// and optionally an object literal with `Map options`. +function createMap(id, options) { + return new Map(id, options); +} + +/* + * @class Control + * @aka L.Control + * @inherits Class + * + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ + +var Control = Class.extend({ + // @section + // @aka Control Options + options: { + // @option position: String = 'topright' + // The position of the control (one of the map corners). Possible values are `'topleft'`, + // `'topright'`, `'bottomleft'` or `'bottomright'` + position: 'topright' + }, + + initialize: function (options) { + setOptions(this, options); + }, + + /* @section + * Classes extending L.Control will inherit the following methods: + * + * @method getPosition: string + * Returns the position of the control. + */ + getPosition: function () { + return this.options.position; + }, + + // @method setPosition(position: string): this + // Sets the position of the control. + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + // @method getContainer: HTMLElement + // Returns the HTMLElement that contains the control. + getContainer: function () { + return this._container; + }, + + // @method addTo(map: Map): this + // Adds the control to the given map. + addTo: function (map) { + this.remove(); + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + this._map.on('unload', this.remove, this); + + return this; + }, + + // @method remove: this + // Removes the control from the map it is currently active on. + remove: function () { + if (!this._map) { + return this; + } + + remove(this._container); + + if (this.onRemove) { + this.onRemove(this._map); + } + + this._map.off('unload', this.remove, this); + this._map = null; + + return this; + }, + + _refocusOnMap: function (e) { + // if map exists and event is not a keyboard event + if (this._map && e && e.screenX > 0 && e.screenY > 0) { + this._map.getContainer().focus(); + } + } +}); + +var control = function (options) { + return new Control(options); +}; + +/* @section Extension methods + * @uninheritable + * + * Every control should extend from `L.Control` and (re-)implement the following methods. + * + * @method onAdd(map: Map): HTMLElement + * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). + * + * @method onRemove(map: Map) + * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). + */ + +/* @namespace Map + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addControl(control: Control): this + // Adds the given control to the map + addControl: function (control) { + control.addTo(this); + return this; + }, + + // @method removeControl(control: Control): this + // Removes the given control from the map + removeControl: function (control) { + control.remove(); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + create$1('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = create$1('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + _clearControlPos: function () { + for (var i in this._controlCorners) { + remove(this._controlCorners[i]); + } + remove(this._controlContainer); + delete this._controlCorners; + delete this._controlContainer; + } +}); + +/* + * @class Control.Layers + * @aka L.Control.Layers + * @inherits Control + * + * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](https://leafletjs.com/examples/layers-control/)). Extends `Control`. + * + * @example + * + * ```js + * var baseLayers = { + * "Mapbox": mapbox, + * "OpenStreetMap": osm + * }; + * + * var overlays = { + * "Marker": marker, + * "Roads": roadsLayer + * }; + * + * L.control.layers(baseLayers, overlays).addTo(map); + * ``` + * + * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: + * + * ```js + * { + * "": layer1, + * "": layer2 + * } + * ``` + * + * The layer names can contain HTML, which allows you to add additional styling to the items: + * + * ```js + * {" My Layer": myLayer} + * ``` + */ + +var Layers = Control.extend({ + // @section + // @aka Control.Layers options + options: { + // @option collapsed: Boolean = true + // If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation. + collapsed: true, + position: 'topright', + + // @option autoZIndex: Boolean = true + // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. + autoZIndex: true, + + // @option hideSingleBase: Boolean = false + // If `true`, the base layers in the control will be hidden when there is only one. + hideSingleBase: false, + + // @option sortLayers: Boolean = false + // Whether to sort the layers. When `false`, layers will keep the order + // in which they were added to the control. + sortLayers: false, + + // @option sortFunction: Function = * + // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) + // that will be used for sorting the layers, when `sortLayers` is `true`. + // The function receives both the `L.Layer` instances and their names, as in + // `sortFunction(layerA, layerB, nameA, nameB)`. + // By default, it sorts layers alphabetically by their name. + sortFunction: function (layerA, layerB, nameA, nameB) { + return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); + } + }, + + initialize: function (baseLayers, overlays, options) { + setOptions(this, options); + + this._layerControlInputs = []; + this._layers = []; + this._lastZIndex = 0; + this._handlingClick = false; + + for (var i in baseLayers) { + this._addLayer(baseLayers[i], i); + } + + for (i in overlays) { + this._addLayer(overlays[i], i, true); + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + this._map = map; + map.on('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.on('add remove', this._onLayerChange, this); + } + + return this._container; + }, + + addTo: function (map) { + Control.prototype.addTo.call(this, map); + // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. + return this._expandIfNotCollapsed(); + }, + + onRemove: function () { + this._map.off('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.off('add remove', this._onLayerChange, this); + } + }, + + // @method addBaseLayer(layer: Layer, name: String): this + // Adds a base layer (radio button entry) with the given name to the control. + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + return (this._map) ? this._update() : this; + }, + + // @method addOverlay(layer: Layer, name: String): this + // Adds an overlay (checkbox entry) with the given name to the control. + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + return (this._map) ? this._update() : this; + }, + + // @method removeLayer(layer: Layer): this + // Remove the given layer from the control. + removeLayer: function (layer) { + layer.off('add remove', this._onLayerChange, this); + + var obj = this._getLayer(stamp(layer)); + if (obj) { + this._layers.splice(this._layers.indexOf(obj), 1); + } + return (this._map) ? this._update() : this; + }, + + // @method expand(): this + // Expand the control container if collapsed. + expand: function () { + addClass(this._container, 'leaflet-control-layers-expanded'); + this._section.style.height = null; + var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); + if (acceptableHeight < this._section.clientHeight) { + addClass(this._section, 'leaflet-control-layers-scrollbar'); + this._section.style.height = acceptableHeight + 'px'; + } else { + removeClass(this._section, 'leaflet-control-layers-scrollbar'); + } + this._checkDisabledLayers(); + return this; + }, + + // @method collapse(): this + // Collapse the control container if expanded. + collapse: function () { + removeClass(this._container, 'leaflet-control-layers-expanded'); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = create$1('div', className), + collapsed = this.options.collapsed; + + // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); + + disableClickPropagation(container); + disableScrollPropagation(container); + + var section = this._section = create$1('section', className + '-list'); + + if (collapsed) { + this._map.on('click', this.collapse, this); + + on(container, { + mouseenter: this._expandSafely, + mouseleave: this.collapse + }, this); + } + + var link = this._layersLink = create$1('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + link.setAttribute('role', 'button'); + + on(link, { + keydown: function (e) { + if (e.keyCode === 13) { + this._expandSafely(); + } + }, + // Certain screen readers intercept the key event and instead send a click event + click: function (e) { + preventDefault(e); + this._expandSafely(); + } + }, this); + + if (!collapsed) { + this.expand(); + } + + this._baseLayersList = create$1('div', className + '-base', section); + this._separator = create$1('div', className + '-separator', section); + this._overlaysList = create$1('div', className + '-overlays', section); + + container.appendChild(section); + }, + + _getLayer: function (id) { + for (var i = 0; i < this._layers.length; i++) { + + if (this._layers[i] && stamp(this._layers[i].layer) === id) { + return this._layers[i]; + } + } + }, + + _addLayer: function (layer, name, overlay) { + if (this._map) { + layer.on('add remove', this._onLayerChange, this); + } + + this._layers.push({ + layer: layer, + name: name, + overlay: overlay + }); + + if (this.options.sortLayers) { + this._layers.sort(bind(function (a, b) { + return this.options.sortFunction(a.layer, b.layer, a.name, b.name); + }, this)); + } + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + + this._expandIfNotCollapsed(); + }, + + _update: function () { + if (!this._container) { return this; } + + empty(this._baseLayersList); + empty(this._overlaysList); + + this._layerControlInputs = []; + var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; + + for (i = 0; i < this._layers.length; i++) { + obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + baseLayersCount += !obj.overlay ? 1 : 0; + } + + // Hide base layers section if there's only one layer. + if (this.options.hideSingleBase) { + baseLayersPresent = baseLayersPresent && baseLayersCount > 1; + this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; + } + + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; + + return this; + }, + + _onLayerChange: function (e) { + if (!this._handlingClick) { + this._update(); + } + + var obj = this._getLayer(stamp(e.target)); + + // @namespace Map + // @section Layer events + // @event baselayerchange: LayersControlEvent + // Fired when the base layer is changed through the [layers control](#control-layers). + // @event overlayadd: LayersControlEvent + // Fired when an overlay is selected through the [layers control](#control-layers). + // @event overlayremove: LayersControlEvent + // Fired when an overlay is deselected through the [layers control](#control-layers). + // @namespace Control.Layers + var type = obj.overlay ? + (e.type === 'add' ? 'overlayadd' : 'overlayremove') : + (e.type === 'add' ? 'baselayerchange' : null); + + if (type) { + this._map.fire(type, obj); + } + }, + + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079) + _createRadioElement: function (name, checked) { + + var radioHtml = ''; + + var radioFragment = document.createElement('div'); + radioFragment.innerHTML = radioHtml; + + return radioFragment.firstChild; + }, + + _addItem: function (obj) { + var label = document.createElement('label'), + checked = this._map.hasLayer(obj.layer), + input; + + if (obj.overlay) { + input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'leaflet-control-layers-selector'; + input.defaultChecked = checked; + } else { + input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked); + } + + this._layerControlInputs.push(input); + input.layerId = stamp(obj.layer); + + on(input, 'click', this._onInputClick, this); + + var name = document.createElement('span'); + name.innerHTML = ' ' + obj.name; + + // Helps from preventing layer control flicker when checkboxes are disabled + // https://github.com/Leaflet/Leaflet/issues/2771 + var holder = document.createElement('span'); + + label.appendChild(holder); + holder.appendChild(input); + holder.appendChild(name); + + var container = obj.overlay ? this._overlaysList : this._baseLayersList; + container.appendChild(label); + + this._checkDisabledLayers(); + return label; + }, + + _onInputClick: function () { + var inputs = this._layerControlInputs, + input, layer; + var addedLayers = [], + removedLayers = []; + + this._handlingClick = true; + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + + if (input.checked) { + addedLayers.push(layer); + } else if (!input.checked) { + removedLayers.push(layer); + } + } + + // Bugfix issue 2318: Should remove all old layers before readding new ones + for (i = 0; i < removedLayers.length; i++) { + if (this._map.hasLayer(removedLayers[i])) { + this._map.removeLayer(removedLayers[i]); + } + } + for (i = 0; i < addedLayers.length; i++) { + if (!this._map.hasLayer(addedLayers[i])) { + this._map.addLayer(addedLayers[i]); + } + } + + this._handlingClick = false; + + this._refocusOnMap(); + }, + + _checkDisabledLayers: function () { + var inputs = this._layerControlInputs, + input, + layer, + zoom = this._map.getZoom(); + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || + (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); + + } + }, + + _expandIfNotCollapsed: function () { + if (this._map && !this.options.collapsed) { + this.expand(); + } + return this; + }, + + _expandSafely: function () { + var section = this._section; + on(section, 'click', preventDefault); + this.expand(); + setTimeout(function () { + off(section, 'click', preventDefault); + }); + } + +}); + + +// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) +// Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. +var layers = function (baseLayers, overlays, options) { + return new Layers(baseLayers, overlays, options); +}; + +/* + * @class Control.Zoom + * @aka L.Control.Zoom + * @inherits Control + * + * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. + */ + +var Zoom = Control.extend({ + // @section + // @aka Control.Zoom options + options: { + position: 'topleft', + + // @option zoomInText: String = '' + // The text set on the 'zoom in' button. + zoomInText: '', + + // @option zoomInTitle: String = 'Zoom in' + // The title set on the 'zoom in' button. + zoomInTitle: 'Zoom in', + + // @option zoomOutText: String = '' + // The text set on the 'zoom out' button. + zoomOutText: '', + + // @option zoomOutTitle: String = 'Zoom out' + // The title set on the 'zoom out' button. + zoomOutTitle: 'Zoom out' + }, + + onAdd: function (map) { + var zoomName = 'leaflet-control-zoom', + container = create$1('div', zoomName + ' leaflet-bar'), + options = this.options; + + this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, + zoomName + '-in', container, this._zoomIn); + this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut); + + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); + + return container; + }, + + onRemove: function (map) { + map.off('zoomend zoomlevelschange', this._updateDisabled, this); + }, + + disable: function () { + this._disabled = true; + this._updateDisabled(); + return this; + }, + + enable: function () { + this._disabled = false; + this._updateDisabled(); + return this; + }, + + _zoomIn: function (e) { + if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { + this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _zoomOut: function (e) { + if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { + this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _createButton: function (html, title, className, container, fn) { + var link = create$1('a', className, container); + link.innerHTML = html; + link.href = '#'; + link.title = title; + + /* + * Will force screen readers like VoiceOver to read this as "Zoom in - button" + */ + link.setAttribute('role', 'button'); + link.setAttribute('aria-label', title); + + disableClickPropagation(link); + on(link, 'click', stop); + on(link, 'click', fn, this); + on(link, 'click', this._refocusOnMap, this); + + return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-disabled'; + + removeClass(this._zoomInButton, className); + removeClass(this._zoomOutButton, className); + this._zoomInButton.setAttribute('aria-disabled', 'false'); + this._zoomOutButton.setAttribute('aria-disabled', 'false'); + + if (this._disabled || map._zoom === map.getMinZoom()) { + addClass(this._zoomOutButton, className); + this._zoomOutButton.setAttribute('aria-disabled', 'true'); + } + if (this._disabled || map._zoom === map.getMaxZoom()) { + addClass(this._zoomInButton, className); + this._zoomInButton.setAttribute('aria-disabled', 'true'); + } + } +}); + +// @namespace Map +// @section Control options +// @option zoomControl: Boolean = true +// Whether a [zoom control](#control-zoom) is added to the map by default. +Map.mergeOptions({ + zoomControl: true +}); + +Map.addInitHook(function () { + if (this.options.zoomControl) { + // @section Controls + // @property zoomControl: Control.Zoom + // The default zoom control (only available if the + // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map). + this.zoomControl = new Zoom(); + this.addControl(this.zoomControl); + } +}); + +// @namespace Control.Zoom +// @factory L.control.zoom(options: Control.Zoom options) +// Creates a zoom control +var zoom = function (options) { + return new Zoom(options); +}; + +/* + * @class Control.Scale + * @aka L.Control.Scale + * @inherits Control + * + * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. + * + * @example + * + * ```js + * L.control.scale().addTo(map); + * ``` + */ + +var Scale = Control.extend({ + // @section + // @aka Control.Scale options + options: { + position: 'bottomleft', + + // @option maxWidth: Number = 100 + // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). + maxWidth: 100, + + // @option metric: Boolean = True + // Whether to show the metric scale line (m/km). + metric: true, + + // @option imperial: Boolean = True + // Whether to show the imperial scale line (mi/ft). + imperial: true + + // @option updateWhenIdle: Boolean = false + // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). + }, + + onAdd: function (map) { + var className = 'leaflet-control-scale', + container = create$1('div', className), + options = this.options; + + this._addScales(options, className + '-line', container); + + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + map.whenReady(this._update, this); + + return container; + }, + + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + }, + + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = create$1('div', className, container); + } + if (options.imperial) { + this._iScale = create$1('div', className, container); + } + }, + + _update: function () { + var map = this._map, + y = map.getSize().y / 2; + + var maxMeters = map.distance( + map.containerPointToLatLng([0, y]), + map.containerPointToLatLng([this.options.maxWidth, y])); + + this._updateScales(maxMeters); + }, + + _updateScales: function (maxMeters) { + if (this.options.metric && maxMeters) { + this._updateMetric(maxMeters); + } + if (this.options.imperial && maxMeters) { + this._updateImperial(maxMeters); + } + }, + + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters), + label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; + + this._updateScale(this._mScale, label, meters / maxMeters); + }, + + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + maxMiles, miles, feet; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); + + } else { + feet = this._getRoundNum(maxFeet); + this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); + } + }, + + _updateScale: function (scale, text, ratio) { + scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; + scale.innerHTML = text; + }, + + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; + + d = d >= 10 ? 10 : + d >= 5 ? 5 : + d >= 3 ? 3 : + d >= 2 ? 2 : 1; + + return pow10 * d; + } +}); + + +// @factory L.control.scale(options?: Control.Scale options) +// Creates an scale control with the given options. +var scale = function (options) { + return new Scale(options); +}; + +var ukrainianFlag = ''; + + +/* + * @class Control.Attribution + * @aka L.Control.Attribution + * @inherits Control + * + * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. + */ + +var Attribution = Control.extend({ + // @section + // @aka Control.Attribution options + options: { + position: 'bottomright', + + // @option prefix: String|false = 'Leaflet' + // The HTML text shown before the attributions. Pass `false` to disable. + prefix: '' + (Browser.inlineSvg ? ukrainianFlag + ' ' : '') + 'Leaflet' + }, + + initialize: function (options) { + setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + map.attributionControl = this; + this._container = create$1('div', 'leaflet-control-attribution'); + disableClickPropagation(this._container); + + // TODO ugly, refactor + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + + this._update(); + + map.on('layeradd', this._addAttribution, this); + + return this._container; + }, + + onRemove: function (map) { + map.off('layeradd', this._addAttribution, this); + }, + + _addAttribution: function (ev) { + if (ev.layer.getAttribution) { + this.addAttribution(ev.layer.getAttribution()); + ev.layer.once('remove', function () { + this.removeAttribution(ev.layer.getAttribution()); + }, this); + } + }, + + // @method setPrefix(prefix: String|false): this + // The HTML text shown before the attributions. Pass `false` to disable. + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + // @method addAttribution(text: String): this + // Adds an attribution text (e.g. `'© OpenStreetMap contributors'`). + addAttribution: function (text) { + if (!text) { return this; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + // @method removeAttribution(text: String): this + // Removes an attribution text. + removeAttribution: function (text) { + if (!text) { return this; } + + if (this._attributions[text]) { + this._attributions[text]--; + this._update(); + } + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' '); + } +}); + +// @namespace Map +// @section Control options +// @option attributionControl: Boolean = true +// Whether a [attribution control](#control-attribution) is added to the map by default. +Map.mergeOptions({ + attributionControl: true +}); + +Map.addInitHook(function () { + if (this.options.attributionControl) { + new Attribution().addTo(this); + } +}); + +// @namespace Control.Attribution +// @factory L.control.attribution(options: Control.Attribution options) +// Creates an attribution control. +var attribution = function (options) { + return new Attribution(options); +}; + +Control.Layers = Layers; +Control.Zoom = Zoom; +Control.Scale = Scale; +Control.Attribution = Attribution; + +control.layers = layers; +control.zoom = zoom; +control.scale = scale; +control.attribution = attribution; + +/* + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ + +// @class Handler +// @aka L.Handler +// Abstract class for map interaction handlers + +var Handler = Class.extend({ + initialize: function (map) { + this._map = map; + }, + + // @method enable(): this + // Enables the handler + enable: function () { + if (this._enabled) { return this; } + + this._enabled = true; + this.addHooks(); + return this; + }, + + // @method disable(): this + // Disables the handler + disable: function () { + if (!this._enabled) { return this; } + + this._enabled = false; + this.removeHooks(); + return this; + }, + + // @method enabled(): Boolean + // Returns `true` if the handler is enabled + enabled: function () { + return !!this._enabled; + } + + // @section Extension methods + // Classes inheriting from `Handler` must implement the two following methods: + // @method addHooks() + // Called when the handler is enabled, should add event hooks. + // @method removeHooks() + // Called when the handler is disabled, should remove the event hooks added previously. +}); + +// @section There is static function which can be called without instantiating L.Handler: +// @function addTo(map: Map, name: String): this +// Adds a new Handler to the given map with the given name. +Handler.addTo = function (map, name) { + map.addHandler(name, this); + return this; +}; + +var Mixin = {Events: Events}; + +/* + * @class Draggable + * @aka L.Draggable + * @inherits Evented + * + * A class for making DOM elements draggable (including touch support). + * Used internally for map and marker dragging. Only works for elements + * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). + * + * @example + * ```js + * var draggable = new L.Draggable(elementToDrag); + * draggable.enable(); + * ``` + */ + +var START = Browser.touch ? 'touchstart mousedown' : 'mousedown'; + +var Draggable = Evented.extend({ + + options: { + // @section + // @aka Draggable options + // @option clickTolerance: Number = 3 + // The max number of pixels a user can shift the mouse pointer during a click + // for it to be considered a valid click (as opposed to a mouse drag). + clickTolerance: 3 + }, + + // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) + // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). + initialize: function (element, dragStartTarget, preventOutline, options) { + setOptions(this, options); + + this._element = element; + this._dragStartTarget = dragStartTarget || element; + this._preventOutline = preventOutline; + }, + + // @method enable() + // Enables the dragging ability + enable: function () { + if (this._enabled) { return; } + + on(this._dragStartTarget, START, this._onDown, this); + + this._enabled = true; + }, + + // @method disable() + // Disables the dragging ability + disable: function () { + if (!this._enabled) { return; } + + // If we're currently dragging this draggable, + // disabling it counts as first ending the drag. + if (Draggable._dragging === this) { + this.finishDrag(true); + } + + off(this._dragStartTarget, START, this._onDown, this); + + this._enabled = false; + this._moved = false; + }, + + _onDown: function (e) { + // Ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (!this._enabled) { return; } + + this._moved = false; + + if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } + + if (e.touches && e.touches.length !== 1) { + // Finish dragging to avoid conflict with touchZoom + if (Draggable._dragging === this) { + this.finishDrag(); + } + return; + } + + if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } + Draggable._dragging = this; // Prevent dragging multiple objects at once. + + if (this._preventOutline) { + preventOutline(this._element); + } + + disableImageDrag(); + disableTextSelection(); + + if (this._moving) { return; } + + // @event down: Event + // Fired when a drag is about to start. + this.fire('down'); + + var first = e.touches ? e.touches[0] : e, + sizedParent = getSizedParentNode(this._element); + + this._startPoint = new Point(first.clientX, first.clientY); + this._startPos = getPosition(this._element); + + // Cache the scale, so that we can continuously compensate for it during drag (_onMove). + this._parentScale = getScale(sizedParent); + + var mouseevent = e.type === 'mousedown'; + on(document, mouseevent ? 'mousemove' : 'touchmove', this._onMove, this); + on(document, mouseevent ? 'mouseup' : 'touchend touchcancel', this._onUp, this); + }, + + _onMove: function (e) { + // Ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (!this._enabled) { return; } + + if (e.touches && e.touches.length > 1) { + this._moved = true; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint); + + if (!offset.x && !offset.y) { return; } + if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } + + // We assume that the parent container's position, border and scale do not change for the duration of the drag. + // Therefore there is no need to account for the position and border (they are eliminated by the subtraction) + // and we can use the cached value for the scale. + offset.x /= this._parentScale.x; + offset.y /= this._parentScale.y; + + preventDefault(e); + + if (!this._moved) { + // @event dragstart: Event + // Fired when a drag starts + this.fire('dragstart'); + + this._moved = true; + + addClass(document.body, 'leaflet-dragging'); + + this._lastTarget = e.target || e.srcElement; + // IE and Edge do not give the element, so fetch it + // if necessary + if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) { + this._lastTarget = this._lastTarget.correspondingUseElement; + } + addClass(this._lastTarget, 'leaflet-drag-target'); + } + + this._newPos = this._startPos.add(offset); + this._moving = true; + + this._lastEvent = e; + this._updatePosition(); + }, + + _updatePosition: function () { + var e = {originalEvent: this._lastEvent}; + + // @event predrag: Event + // Fired continuously during dragging *before* each corresponding + // update of the element's position. + this.fire('predrag', e); + setPosition(this._element, this._newPos); + + // @event drag: Event + // Fired continuously during dragging. + this.fire('drag', e); + }, + + _onUp: function () { + // Ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (!this._enabled) { return; } + this.finishDrag(); + }, + + finishDrag: function (noInertia) { + removeClass(document.body, 'leaflet-dragging'); + + if (this._lastTarget) { + removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } + + off(document, 'mousemove touchmove', this._onMove, this); + off(document, 'mouseup touchend touchcancel', this._onUp, this); + + enableImageDrag(); + enableTextSelection(); + + if (this._moved && this._moving) { + + // @event dragend: DragEndEvent + // Fired when the drag ends. + this.fire('dragend', { + noInertia: noInertia, + distance: this._newPos.distanceTo(this._startPos) + }); + } + + this._moving = false; + Draggable._dragging = false; + } + +}); + +/* + * @namespace LineUtil + * + * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast. + */ + +// Simplify polyline with vertex reduction and Douglas-Peucker simplification. +// Improves rendering performance dramatically by lessening the number of points to draw. + +// @function simplify(points: Point[], tolerance: Number): Point[] +// Dramatically reduces the number of points in a polyline while retaining +// its shape and returns a new array of simplified points, using the +// [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm). +// Used for a huge performance boost when processing/displaying Leaflet polylines for +// each zoom level and also reducing visual noise. tolerance affects the amount of +// simplification (lesser value means higher quality but slower and with more points). +// Also released as a separated micro-library [Simplify.js](https://mourner.github.io/simplify-js/). +function simplify(points, tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } + + var sqTolerance = tolerance * tolerance; + + // stage 1: vertex reduction + points = _reducePoints(points, sqTolerance); + + // stage 2: Douglas-Peucker simplification + points = _simplifyDP(points, sqTolerance); + + return points; +} + +// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number +// Returns the distance between point `p` and segment `p1` to `p2`. +function pointToSegmentDistance(p, p1, p2) { + return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); +} + +// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number +// Returns the closest point from a point `p` on a segment `p1` to `p2`. +function closestPointOnSegment(p, p1, p2) { + return _sqClosestPointOnSegment(p, p1, p2); +} + +// Ramer-Douglas-Peucker simplification, see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm +function _simplifyDP(points, sqTolerance) { + + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); + + markers[0] = markers[len - 1] = 1; + + _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + + var i, + newPoints = []; + + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); + } + } + + return newPoints; +} + +function _simplifyDPStep(points, markers, sqTolerance, first, last) { + + var maxSqDist = 0, + index, i, sqDist; + + for (i = first + 1; i <= last - 1; i++) { + sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + _simplifyDPStep(points, markers, sqTolerance, first, index); + _simplifyDPStep(points, markers, sqTolerance, index, last); + } +} + +// reduce points that are too close to each other to a single point +function _reducePoints(points, sqTolerance) { + var reducedPoints = [points[0]]; + + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (_sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; + } + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; +} + +var _lastCode; + +// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean +// Clips the segment a to b by rectangular bounds with the +// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) +// (modifying the segment points directly!). Used by Leaflet to only show polyline +// points that are on the screen or near, increasing performance. +function clipSegment(a, b, bounds, useLastCode, round) { + var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), + codeB = _getBitCode(b, bounds), + + codeOut, p, newCode; + + // save 2nd code to avoid calculating it on the next segment + _lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; + } + + // if a,b is outside the clip window (trivial reject) + if (codeA & codeB) { + return false; + } + + // other cases + codeOut = codeA || codeB; + p = _getEdgeIntersection(a, b, codeOut, bounds, round); + newCode = _getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } +} + +function _getEdgeIntersection(a, b, code, bounds, round) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max, + x, y; + + if (code & 8) { // top + x = a.x + dx * (max.y - a.y) / dy; + y = max.y; + + } else if (code & 4) { // bottom + x = a.x + dx * (min.y - a.y) / dy; + y = min.y; + + } else if (code & 2) { // right + x = max.x; + y = a.y + dy * (max.x - a.x) / dx; + + } else if (code & 1) { // left + x = min.x; + y = a.y + dy * (min.x - a.x) / dx; + } + + return new Point(x, y, round); +} + +function _getBitCode(p, bounds) { + var code = 0; + + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } + + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } + + return code; +} + +// square distance (to avoid unnecessary Math.sqrt calls) +function _sqDist(p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; +} + +// return closest point on segment or distance to that point +function _sqClosestPointOnSegment(p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return sqDist ? dx * dx + dy * dy : new Point(x, y); +} + + +// @function isFlat(latlngs: LatLng[]): Boolean +// Returns true if `latlngs` is a flat array, false is nested. +function isFlat(latlngs) { + return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); +} + +function _flat(latlngs) { + console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.'); + return isFlat(latlngs); +} + +/* @function polylineCenter(latlngs: LatLng[], crs: CRS): LatLng + * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polyline. + */ +function polylineCenter(latlngs, crs) { + var i, halfDist, segDist, dist, p1, p2, ratio, center; + + if (!latlngs || latlngs.length === 0) { + throw new Error('latlngs not passed'); + } + + if (!isFlat(latlngs)) { + console.warn('latlngs are not flat! Only the first ring will be used'); + latlngs = latlngs[0]; + } + + var points = []; + for (var j in latlngs) { + points.push(crs.project(toLatLng(latlngs[j]))); + } + + var len = points.length; + + for (i = 0, halfDist = 0; i < len - 1; i++) { + halfDist += points[i].distanceTo(points[i + 1]) / 2; + } + + // The line is so small in the current view that all points are on the same pixel. + if (halfDist === 0) { + center = points[0]; + } else { + for (i = 0, dist = 0; i < len - 1; i++) { + p1 = points[i]; + p2 = points[i + 1]; + segDist = p1.distanceTo(p2); + dist += segDist; + + if (dist > halfDist) { + ratio = (dist - halfDist) / segDist; + center = [ + p2.x - ratio * (p2.x - p1.x), + p2.y - ratio * (p2.y - p1.y) + ]; + break; + } + } + } + return crs.unproject(toPoint(center)); +} + +var LineUtil = { + __proto__: null, + simplify: simplify, + pointToSegmentDistance: pointToSegmentDistance, + closestPointOnSegment: closestPointOnSegment, + clipSegment: clipSegment, + _getEdgeIntersection: _getEdgeIntersection, + _getBitCode: _getBitCode, + _sqClosestPointOnSegment: _sqClosestPointOnSegment, + isFlat: isFlat, + _flat: _flat, + polylineCenter: polylineCenter +}; + +/* + * @namespace PolyUtil + * Various utility functions for polygon geometries. + */ + +/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] + * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). + * Used by Leaflet to only show polygon points that are on the screen or near, increasing + * performance. Note that polygon points needs different algorithm for clipping + * than polyline, so there's a separate method for it. + */ +function clipPolygon(points, bounds, round) { + var clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = _getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + } + points = clippedPoints; + } + + return points; +} + +/* @function polygonCenter(latlngs: LatLng[] crs: CRS): LatLng + * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polygon. + */ +function polygonCenter(latlngs, crs) { + var i, j, p1, p2, f, area, x, y, center; + + if (!latlngs || latlngs.length === 0) { + throw new Error('latlngs not passed'); + } + + if (!isFlat(latlngs)) { + console.warn('latlngs are not flat! Only the first ring will be used'); + latlngs = latlngs[0]; + } + + var points = []; + for (var k in latlngs) { + points.push(crs.project(toLatLng(latlngs[k]))); + } + + var len = points.length; + area = x = y = 0; + + // polygon centroid algorithm; + for (i = 0, j = len - 1; i < len; j = i++) { + p1 = points[i]; + p2 = points[j]; + + f = p1.y * p2.x - p2.y * p1.x; + x += (p1.x + p2.x) * f; + y += (p1.y + p2.y) * f; + area += f * 3; + } + + if (area === 0) { + // Polygon is so small that all points are on same pixel. + center = points[0]; + } else { + center = [x / area, y / area]; + } + return crs.unproject(toPoint(center)); +} + +var PolyUtil = { + __proto__: null, + clipPolygon: clipPolygon, + polygonCenter: polygonCenter +}; + +/* + * @namespace Projection + * @section + * Leaflet comes with a set of already defined Projections out of the box: + * + * @projection L.Projection.LonLat + * + * Equirectangular, or Plate Carree projection — the most simple projection, + * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as + * latitude. Also suitable for flat worlds, e.g. game maps. Used by the + * `EPSG:4326` and `Simple` CRS. + */ + +var LonLat = { + project: function (latlng) { + return new Point(latlng.lng, latlng.lat); + }, + + unproject: function (point) { + return new LatLng(point.y, point.x); + }, + + bounds: new Bounds([-180, -90], [180, 90]) +}; + +/* + * @namespace Projection + * @projection L.Projection.Mercator + * + * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS. + */ + +var Mercator = { + R: 6378137, + R_MINOR: 6356752.314245179, + + bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), + + project: function (latlng) { + var d = Math.PI / 180, + r = this.R, + y = latlng.lat * d, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + con = e * Math.sin(y); + + var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); + y = -r * Math.log(Math.max(ts, 1E-10)); + + return new Point(latlng.lng * d * r, y); + }, + + unproject: function (point) { + var d = 180 / Math.PI, + r = this.R, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + ts = Math.exp(-point.y / r), + phi = Math.PI / 2 - 2 * Math.atan(ts); + + for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { + con = e * Math.sin(phi); + con = Math.pow((1 - con) / (1 + con), e / 2); + dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; + phi += dphi; + } + + return new LatLng(phi * d, point.x * d / r); + } +}; + +/* + * @class Projection + + * An object with methods for projecting geographical coordinates of the world onto + * a flat surface (and back). See [Map projection](https://en.wikipedia.org/wiki/Map_projection). + + * @property bounds: Bounds + * The bounds (specified in CRS units) where the projection is valid + + * @method project(latlng: LatLng): Point + * Projects geographical coordinates into a 2D point. + * Only accepts actual `L.LatLng` instances, not arrays. + + * @method unproject(point: Point): LatLng + * The inverse of `project`. Projects a 2D point into a geographical location. + * Only accepts actual `L.Point` instances, not arrays. + + * Note that the projection instances do not inherit from Leaflet's `Class` object, + * and can't be instantiated. Also, new classes can't inherit from them, + * and methods can't be added to them with the `include` function. + + */ + +var index = { + __proto__: null, + LonLat: LonLat, + Mercator: Mercator, + SphericalMercator: SphericalMercator +}; + +/* + * @namespace CRS + * @crs L.CRS.EPSG3395 + * + * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. + */ +var EPSG3395 = extend({}, Earth, { + code: 'EPSG:3395', + projection: Mercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * Mercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +/* + * @namespace CRS + * @crs L.CRS.EPSG4326 + * + * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. + * + * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), + * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` + * with this CRS, ensure that there are two 256x256 pixel tiles covering the + * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), + * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. + */ + +var EPSG4326 = extend({}, Earth, { + code: 'EPSG:4326', + projection: LonLat, + transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) +}); + +/* + * @namespace CRS + * @crs L.CRS.Simple + * + * A simple CRS that maps longitude and latitude into `x` and `y` directly. + * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` + * axis should still be inverted (going from bottom to top). `distance()` returns + * simple euclidean distance. + */ + +var Simple = extend({}, CRS, { + projection: LonLat, + transformation: toTransformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + }, + + zoom: function (scale) { + return Math.log(scale) / Math.LN2; + }, + + distance: function (latlng1, latlng2) { + var dx = latlng2.lng - latlng1.lng, + dy = latlng2.lat - latlng1.lat; + + return Math.sqrt(dx * dx + dy * dy); + }, + + infinite: true +}); + +CRS.Earth = Earth; +CRS.EPSG3395 = EPSG3395; +CRS.EPSG3857 = EPSG3857; +CRS.EPSG900913 = EPSG900913; +CRS.EPSG4326 = EPSG4326; +CRS.Simple = Simple; + +/* + * @class Layer + * @inherits Evented + * @aka L.Layer + * @aka ILayer + * + * A set of methods from the Layer base class that all Leaflet layers use. + * Inherits all methods, options and events from `L.Evented`. + * + * @example + * + * ```js + * var layer = L.marker(latlng).addTo(map); + * layer.addTo(map); + * layer.remove(); + * ``` + * + * @event add: Event + * Fired after the layer is added to a map + * + * @event remove: Event + * Fired after the layer is removed from a map + */ + + +var Layer = Evented.extend({ + + // Classes extending `L.Layer` will inherit the following options: + options: { + // @option pane: String = 'overlayPane' + // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. + pane: 'overlayPane', + + // @option attribution: String = null + // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers. + attribution: null, + + bubblingMouseEvents: true + }, + + /* @section + * Classes extending `L.Layer` will inherit the following methods: + * + * @method addTo(map: Map|LayerGroup): this + * Adds the layer to the given map or layer group. + */ + addTo: function (map) { + map.addLayer(this); + return this; + }, + + // @method remove: this + // Removes the layer from the map it is currently active on. + remove: function () { + return this.removeFrom(this._map || this._mapToAdd); + }, + + // @method removeFrom(map: Map): this + // Removes the layer from the given map + // + // @alternative + // @method removeFrom(group: LayerGroup): this + // Removes the layer from the given `LayerGroup` + removeFrom: function (obj) { + if (obj) { + obj.removeLayer(this); + } + return this; + }, + + // @method getPane(name? : String): HTMLElement + // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. + getPane: function (name) { + return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); + }, + + addInteractiveTarget: function (targetEl) { + this._map._targets[stamp(targetEl)] = this; + return this; + }, + + removeInteractiveTarget: function (targetEl) { + delete this._map._targets[stamp(targetEl)]; + return this; + }, + + // @method getAttribution: String + // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). + getAttribution: function () { + return this.options.attribution; + }, + + _layerAdd: function (e) { + var map = e.target; + + // check in case layer gets added and then removed before the map is ready + if (!map.hasLayer(this)) { return; } + + this._map = map; + this._zoomAnimated = map._zoomAnimated; + + if (this.getEvents) { + var events = this.getEvents(); + map.on(events, this); + this.once('remove', function () { + map.off(events, this); + }, this); + } + + this.onAdd(map); + + this.fire('add'); + map.fire('layeradd', {layer: this}); + } +}); + +/* @section Extension methods + * @uninheritable + * + * Every layer should extend from `L.Layer` and (re-)implement the following methods. + * + * @method onAdd(map: Map): this + * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). + * + * @method onRemove(map: Map): this + * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). + * + * @method getEvents(): Object + * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. + * + * @method getAttribution(): String + * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. + * + * @method beforeAdd(map: Map): this + * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. + */ + + +/* @namespace Map + * @section Layer events + * + * @event layeradd: LayerEvent + * Fired when a new layer is added to the map. + * + * @event layerremove: LayerEvent + * Fired when some layer is removed from the map + * + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addLayer(layer: Layer): this + // Adds the given layer to the map + addLayer: function (layer) { + if (!layer._layerAdd) { + throw new Error('The provided object is not a Layer.'); + } + + var id = stamp(layer); + if (this._layers[id]) { return this; } + this._layers[id] = layer; + + layer._mapToAdd = this; + + if (layer.beforeAdd) { + layer.beforeAdd(this); + } + + this.whenReady(layer._layerAdd, layer); + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the map. + removeLayer: function (layer) { + var id = stamp(layer); + + if (!this._layers[id]) { return this; } + + if (this._loaded) { + layer.onRemove(this); + } + + delete this._layers[id]; + + if (this._loaded) { + this.fire('layerremove', {layer: layer}); + layer.fire('remove'); + } + + layer._map = layer._mapToAdd = null; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the map + hasLayer: function (layer) { + return stamp(layer) in this._layers; + }, + + /* @method eachLayer(fn: Function, context?: Object): this + * Iterates over the layers of the map, optionally specifying context of the iterator function. + * ``` + * map.eachLayer(function(layer){ + * layer.bindPopup('Hello'); + * }); + * ``` + */ + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + _addLayers: function (layers) { + layers = layers ? (isArray(layers) ? layers : [layers]) : []; + + for (var i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + }, + + _addZoomLimit: function (layer) { + if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { + this._zoomBoundLayers[stamp(layer)] = layer; + this._updateZoomLevels(); + } + }, + + _removeZoomLimit: function (layer) { + var id = stamp(layer); + + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } + }, + + _updateZoomLevels: function () { + var minZoom = Infinity, + maxZoom = -Infinity, + oldZoomSpan = this._getZoomSpan(); + + for (var i in this._zoomBoundLayers) { + var options = this._zoomBoundLayers[i].options; + + minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); + maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); + } + + this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; + this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; + + // @section Map state change events + // @event zoomlevelschange: Event + // Fired when the number of zoomlevels on the map is changed due + // to adding or removing a layer. + if (oldZoomSpan !== this._getZoomSpan()) { + this.fire('zoomlevelschange'); + } + + if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { + this.setZoom(this._layersMaxZoom); + } + if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { + this.setZoom(this._layersMinZoom); + } + } +}); + +/* + * @class LayerGroup + * @aka L.LayerGroup + * @inherits Interactive layer + * + * Used to group several layers and handle them as one. If you add it to the map, + * any layers added or removed from the group will be added/removed on the map as + * well. Extends `Layer`. + * + * @example + * + * ```js + * L.layerGroup([marker1, marker2]) + * .addLayer(polyline) + * .addTo(map); + * ``` + */ + +var LayerGroup = Layer.extend({ + + initialize: function (layers, options) { + setOptions(this, options); + + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + // @method addLayer(layer: Layer): this + // Adds the given layer to the group. + addLayer: function (layer) { + var id = this.getLayerId(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the group. + // @alternative + // @method removeLayer(id: Number): this + // Removes the layer with the given internal ID from the group. + removeLayer: function (layer) { + var id = layer in this._layers ? layer : this.getLayerId(layer); + + if (this._map && this._layers[id]) { + this._map.removeLayer(this._layers[id]); + } + + delete this._layers[id]; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the group. + // @alternative + // @method hasLayer(id: Number): Boolean + // Returns `true` if the given internal ID is currently added to the group. + hasLayer: function (layer) { + var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer); + return layerId in this._layers; + }, + + // @method clearLayers(): this + // Removes all the layers from the group. + clearLayers: function () { + return this.eachLayer(this.removeLayer, this); + }, + + // @method invoke(methodName: String, …): this + // Calls `methodName` on every layer contained in this group, passing any + // additional parameters. Has no effect if the layers contained do not + // implement `methodName`. + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + + return this; + }, + + onAdd: function (map) { + this.eachLayer(map.addLayer, map); + }, + + onRemove: function (map) { + this.eachLayer(map.removeLayer, map); + }, + + // @method eachLayer(fn: Function, context?: Object): this + // Iterates over the layers of the group, optionally specifying context of the iterator function. + // ```js + // group.eachLayer(function (layer) { + // layer.bindPopup('Hello'); + // }); + // ``` + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + // @method getLayer(id: Number): Layer + // Returns the layer with the given internal ID. + getLayer: function (id) { + return this._layers[id]; + }, + + // @method getLayers(): Layer[] + // Returns an array of all the layers added to the group. + getLayers: function () { + var layers = []; + this.eachLayer(layers.push, layers); + return layers; + }, + + // @method setZIndex(zIndex: Number): this + // Calls `setZIndex` on every layer contained in this group, passing the z-index. + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); + }, + + // @method getLayerId(layer: Layer): Number + // Returns the internal ID for a layer + getLayerId: function (layer) { + return stamp(layer); + } +}); + + +// @factory L.layerGroup(layers?: Layer[], options?: Object) +// Create a layer group, optionally given an initial set of layers and an `options` object. +var layerGroup = function (layers, options) { + return new LayerGroup(layers, options); +}; + +/* + * @class FeatureGroup + * @aka L.FeatureGroup + * @inherits LayerGroup + * + * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: + * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) + * * Events are propagated to the `FeatureGroup`, so if the group has an event + * handler, it will handle events from any of the layers. This includes mouse events + * and custom events. + * * Has `layeradd` and `layerremove` events + * + * @example + * + * ```js + * L.featureGroup([marker1, marker2, polyline]) + * .bindPopup('Hello world!') + * .on('click', function() { alert('Clicked on a member of the group!'); }) + * .addTo(map); + * ``` + */ + +var FeatureGroup = LayerGroup.extend({ + + addLayer: function (layer) { + if (this.hasLayer(layer)) { + return this; + } + + layer.addEventParent(this); + + LayerGroup.prototype.addLayer.call(this, layer); + + // @event layeradd: LayerEvent + // Fired when a layer is added to this `FeatureGroup` + return this.fire('layeradd', {layer: layer}); + }, + + removeLayer: function (layer) { + if (!this.hasLayer(layer)) { + return this; + } + if (layer in this._layers) { + layer = this._layers[layer]; + } + + layer.removeEventParent(this); + + LayerGroup.prototype.removeLayer.call(this, layer); + + // @event layerremove: LayerEvent + // Fired when a layer is removed from this `FeatureGroup` + return this.fire('layerremove', {layer: layer}); + }, + + // @method setStyle(style: Path options): this + // Sets the given path options to each layer of the group that has a `setStyle` method. + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + // @method bringToFront(): this + // Brings the layer group to the top of all other layers + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + // @method bringToBack(): this + // Brings the layer group to the back of all other layers + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + // @method getBounds(): LatLngBounds + // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). + getBounds: function () { + var bounds = new LatLngBounds(); + + for (var id in this._layers) { + var layer = this._layers[id]; + bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); + } + return bounds; + } +}); + +// @factory L.featureGroup(layers?: Layer[], options?: Object) +// Create a feature group, optionally given an initial set of layers and an `options` object. +var featureGroup = function (layers, options) { + return new FeatureGroup(layers, options); +}; + +/* + * @class Icon + * @aka L.Icon + * + * Represents an icon to provide when creating a marker. + * + * @example + * + * ```js + * var myIcon = L.icon({ + * iconUrl: 'my-icon.png', + * iconRetinaUrl: 'my-icon@2x.png', + * iconSize: [38, 95], + * iconAnchor: [22, 94], + * popupAnchor: [-3, -76], + * shadowUrl: 'my-icon-shadow.png', + * shadowRetinaUrl: 'my-icon-shadow@2x.png', + * shadowSize: [68, 95], + * shadowAnchor: [22, 94] + * }); + * + * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); + * ``` + * + * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. + * + */ + +var Icon = Class.extend({ + + /* @section + * @aka Icon options + * + * @option iconUrl: String = null + * **(required)** The URL to the icon image (absolute or relative to your script path). + * + * @option iconRetinaUrl: String = null + * The URL to a retina sized version of the icon image (absolute or relative to your + * script path). Used for Retina screen devices. + * + * @option iconSize: Point = null + * Size of the icon image in pixels. + * + * @option iconAnchor: Point = null + * The coordinates of the "tip" of the icon (relative to its top left corner). The icon + * will be aligned so that this point is at the marker's geographical location. Centered + * by default if size is specified, also can be set in CSS with negative margins. + * + * @option popupAnchor: Point = [0, 0] + * The coordinates of the point from which popups will "open", relative to the icon anchor. + * + * @option tooltipAnchor: Point = [0, 0] + * The coordinates of the point from which tooltips will "open", relative to the icon anchor. + * + * @option shadowUrl: String = null + * The URL to the icon shadow image. If not specified, no shadow image will be created. + * + * @option shadowRetinaUrl: String = null + * + * @option shadowSize: Point = null + * Size of the shadow image in pixels. + * + * @option shadowAnchor: Point = null + * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same + * as iconAnchor if not specified). + * + * @option className: String = '' + * A custom class name to assign to both icon and shadow images. Empty by default. + */ + + options: { + popupAnchor: [0, 0], + tooltipAnchor: [0, 0], + + // @option crossOrigin: Boolean|String = false + // Whether the crossOrigin attribute will be added to the tiles. + // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data. + // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. + crossOrigin: false + }, + + initialize: function (options) { + setOptions(this, options); + }, + + // @method createIcon(oldIcon?: HTMLElement): HTMLElement + // Called internally when the icon has to be shown, returns a `` HTML element + // styled according to the options. + createIcon: function (oldIcon) { + return this._createIcon('icon', oldIcon); + }, + + // @method createShadow(oldIcon?: HTMLElement): HTMLElement + // As `createIcon`, but for the shadow beneath it. + createShadow: function (oldIcon) { + return this._createIcon('shadow', oldIcon); + }, + + _createIcon: function (name, oldIcon) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error('iconUrl not set in Icon options (see the docs).'); + } + return null; + } + + var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); + this._setIconStyles(img, name); + + if (this.options.crossOrigin || this.options.crossOrigin === '') { + img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; + } + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options; + var sizeOption = options[name + 'Size']; + + if (typeof sizeOption === 'number') { + sizeOption = [sizeOption, sizeOption]; + } + + var size = toPoint(sizeOption), + anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || + size && size.divideBy(2, true)); + + img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.src = src; + return el; + }, + + _getIconUrl: function (name) { + return Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; + } +}); + + +// @factory L.icon(options: Icon options) +// Creates an icon instance with the given options. +function icon(options) { + return new Icon(options); +} + +/* + * @miniclass Icon.Default (Icon) + * @aka L.Icon.Default + * @section + * + * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when + * no icon is specified. Points to the blue marker image distributed with Leaflet + * releases. + * + * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` + * (which is a set of `Icon options`). + * + * If you want to _completely_ replace the default icon, override the + * `L.Marker.prototype.options.icon` with your own icon instead. + */ + +var IconDefault = Icon.extend({ + + options: { + iconUrl: 'marker-icon.png', + iconRetinaUrl: 'marker-icon-2x.png', + shadowUrl: 'marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + shadowSize: [41, 41] + }, + + _getIconUrl: function (name) { + if (typeof IconDefault.imagePath !== 'string') { // Deprecated, backwards-compatibility only + IconDefault.imagePath = this._detectIconPath(); + } + + // @option imagePath: String + // `Icon.Default` will try to auto-detect the location of the + // blue icon images. If you are placing these images in a non-standard + // way, set this option to point to the right path. + return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); + }, + + _stripUrl: function (path) { // separate function to use in tests + var strip = function (str, re, idx) { + var match = re.exec(str); + return match && match[idx]; + }; + path = strip(path, /^url\((['"])?(.+)\1\)$/, 2); + return path && strip(path, /^(.*)marker-icon\.png$/, 1); + }, + + _detectIconPath: function () { + var el = create$1('div', 'leaflet-default-icon-path', document.body); + var path = getStyle(el, 'background-image') || + getStyle(el, 'backgroundImage'); // IE8 + + document.body.removeChild(el); + path = this._stripUrl(path); + if (path) { return path; } + var link = document.querySelector('link[href$="leaflet.css"]'); + if (!link) { return ''; } + return link.href.substring(0, link.href.length - 'leaflet.css'.length - 1); + } +}); + +/* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ + + +/* @namespace Marker + * @section Interaction handlers + * + * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: + * + * ```js + * marker.dragging.disable(); + * ``` + * + * @property dragging: Handler + * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). + */ + +var MarkerDrag = Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, + + addHooks: function () { + var icon = this._marker._icon; + + if (!this._draggable) { + this._draggable = new Draggable(icon, icon, true); + } + + this._draggable.on({ + dragstart: this._onDragStart, + predrag: this._onPreDrag, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).enable(); + + addClass(icon, 'leaflet-marker-draggable'); + }, + + removeHooks: function () { + this._draggable.off({ + dragstart: this._onDragStart, + predrag: this._onPreDrag, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).disable(); + + if (this._marker._icon) { + removeClass(this._marker._icon, 'leaflet-marker-draggable'); + } + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _adjustPan: function (e) { + var marker = this._marker, + map = marker._map, + speed = this._marker.options.autoPanSpeed, + padding = this._marker.options.autoPanPadding, + iconPos = getPosition(marker._icon), + bounds = map.getPixelBounds(), + origin = map.getPixelOrigin(); + + var panBounds = toBounds( + bounds.min._subtract(origin).add(padding), + bounds.max._subtract(origin).subtract(padding) + ); + + if (!panBounds.contains(iconPos)) { + // Compute incremental movement + var movement = toPoint( + (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - + (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), + + (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - + (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) + ).multiplyBy(speed); + + map.panBy(movement, {animate: false}); + + this._draggable._newPos._add(movement); + this._draggable._startPos._add(movement); + + setPosition(marker._icon, this._draggable._newPos); + this._onDrag(e); + + this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); + } + }, + + _onDragStart: function () { + // @section Dragging events + // @event dragstart: Event + // Fired when the user starts dragging the marker. + + // @event movestart: Event + // Fired when the marker starts moving (because of dragging). + + this._oldLatLng = this._marker.getLatLng(); + + // When using ES6 imports it could not be set when `Popup` was not imported as well + this._marker.closePopup && this._marker.closePopup(); + + this._marker + .fire('movestart') + .fire('dragstart'); + }, + + _onPreDrag: function (e) { + if (this._marker.options.autoPan) { + cancelAnimFrame(this._panRequest); + this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); + } + }, + + _onDrag: function (e) { + var marker = this._marker, + shadow = marker._shadow, + iconPos = getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); + + // update shadow position + if (shadow) { + setPosition(shadow, iconPos); + } + + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; + + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('move', e) + .fire('drag', e); + }, + + _onDragEnd: function (e) { + // @event dragend: DragEndEvent + // Fired when the user stops dragging the marker. + + cancelAnimFrame(this._panRequest); + + // @event moveend: Event + // Fired when the marker stops moving (because of dragging). + delete this._oldLatLng; + this._marker + .fire('moveend') + .fire('dragend', e); + } +}); + +/* + * @class Marker + * @inherits Interactive layer + * @aka L.Marker + * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. + * + * @example + * + * ```js + * L.marker([50.5, 30.5]).addTo(map); + * ``` + */ + +var Marker = Layer.extend({ + + // @section + // @aka Marker options + options: { + // @option icon: Icon = * + // Icon instance to use for rendering the marker. + // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. + // If not specified, a common instance of `L.Icon.Default` is used. + icon: new IconDefault(), + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option keyboard: Boolean = true + // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. + keyboard: true, + + // @option title: String = '' + // Text for the browser tooltip that appear on marker hover (no tooltip by default). + // [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled). + title: '', + + // @option alt: String = 'Marker' + // Text for the `alt` attribute of the icon image. + // [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled). + alt: 'Marker', + + // @option zIndexOffset: Number = 0 + // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). + zIndexOffset: 0, + + // @option opacity: Number = 1.0 + // The opacity of the marker. + opacity: 1, + + // @option riseOnHover: Boolean = false + // If `true`, the marker will get on top of others when you hover the mouse over it. + riseOnHover: false, + + // @option riseOffset: Number = 250 + // The z-index offset used for the `riseOnHover` feature. + riseOffset: 250, + + // @option pane: String = 'markerPane' + // `Map pane` where the markers icon will be added. + pane: 'markerPane', + + // @option shadowPane: String = 'shadowPane' + // `Map pane` where the markers shadow will be added. + shadowPane: 'shadowPane', + + // @option bubblingMouseEvents: Boolean = false + // When `true`, a mouse event on this marker will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: false, + + // @option autoPanOnFocus: Boolean = true + // When `true`, the map will pan whenever the marker is focused (via + // e.g. pressing `tab` on the keyboard) to ensure the marker is + // visible within the map's bounds + autoPanOnFocus: true, + + // @section Draggable marker options + // @option draggable: Boolean = false + // Whether the marker is draggable with mouse/touch or not. + draggable: false, + + // @option autoPan: Boolean = false + // Whether to pan the map when dragging this marker near its edge or not. + autoPan: false, + + // @option autoPanPadding: Point = Point(50, 50) + // Distance (in pixels to the left/right and to the top/bottom) of the + // map edge to start panning the map. + autoPanPadding: [50, 50], + + // @option autoPanSpeed: Number = 10 + // Number of pixels the map should pan by. + autoPanSpeed: 10 + }, + + /* @section + * + * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: + */ + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + }, + + onAdd: function (map) { + this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; + + if (this._zoomAnimated) { + map.on('zoomanim', this._animateZoom, this); + } + + this._initIcon(); + this.update(); + }, + + onRemove: function (map) { + if (this.dragging && this.dragging.enabled()) { + this.options.draggable = true; + this.dragging.removeHooks(); + } + delete this.dragging; + + if (this._zoomAnimated) { + map.off('zoomanim', this._animateZoom, this); + } + + this._removeIcon(); + this._removeShadow(); + }, + + getEvents: function () { + return { + zoom: this.update, + viewreset: this.update + }; + }, + + // @method getLatLng: LatLng + // Returns the current geographical position of the marker. + getLatLng: function () { + return this._latlng; + }, + + // @method setLatLng(latlng: LatLng): this + // Changes the marker position to the given point. + setLatLng: function (latlng) { + var oldLatLng = this._latlng; + this._latlng = toLatLng(latlng); + this.update(); + + // @event move: Event + // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); + }, + + // @method setZIndexOffset(offset: Number): this + // Changes the [zIndex offset](#marker-zindexoffset) of the marker. + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + return this.update(); + }, + + // @method getIcon: Icon + // Returns the current icon used by the marker + getIcon: function () { + return this.options.icon; + }, + + // @method setIcon(icon: Icon): this + // Changes the marker icon. + setIcon: function (icon) { + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + + if (this._popup) { + this.bindPopup(this._popup, this._popup.options); + } + + return this; + }, + + getElement: function () { + return this._icon; + }, + + update: function () { + + if (this._icon && this._map) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } + + return this; + }, + + _initIcon: function () { + var options = this.options, + classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); + + var icon = options.icon.createIcon(this._icon), + addIcon = false; + + // if we're not reusing the icon, remove the old one and init new one + if (icon !== this._icon) { + if (this._icon) { + this._removeIcon(); + } + addIcon = true; + + if (options.title) { + icon.title = options.title; + } + + if (icon.tagName === 'IMG') { + icon.alt = options.alt || ''; + } + } + + addClass(icon, classToAdd); + + if (options.keyboard) { + icon.tabIndex = '0'; + icon.setAttribute('role', 'button'); + } + + this._icon = icon; + + if (options.riseOnHover) { + this.on({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + if (this.options.autoPanOnFocus) { + on(icon, 'focus', this._panOnFocus, this); + } + + var newShadow = options.icon.createShadow(this._shadow), + addShadow = false; + + if (newShadow !== this._shadow) { + this._removeShadow(); + addShadow = true; + } + + if (newShadow) { + addClass(newShadow, classToAdd); + newShadow.alt = ''; + } + this._shadow = newShadow; + + + if (options.opacity < 1) { + this._updateOpacity(); + } + + + if (addIcon) { + this.getPane().appendChild(this._icon); + } + this._initInteraction(); + if (newShadow && addShadow) { + this.getPane(options.shadowPane).appendChild(this._shadow); + } + }, + + _removeIcon: function () { + if (this.options.riseOnHover) { + this.off({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + if (this.options.autoPanOnFocus) { + off(this._icon, 'focus', this._panOnFocus, this); + } + + remove(this._icon); + this.removeInteractiveTarget(this._icon); + + this._icon = null; + }, + + _removeShadow: function () { + if (this._shadow) { + remove(this._shadow); + } + this._shadow = null; + }, + + _setPos: function (pos) { + + if (this._icon) { + setPosition(this._icon, pos); + } + + if (this._shadow) { + setPosition(this._shadow, pos); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + _updateZIndex: function (offset) { + if (this._icon) { + this._icon.style.zIndex = this._zIndex + offset; + } + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); + + this._setPos(pos); + }, + + _initInteraction: function () { + + if (!this.options.interactive) { return; } + + addClass(this._icon, 'leaflet-interactive'); + + this.addInteractiveTarget(this._icon); + + if (MarkerDrag) { + var draggable = this.options.draggable; + if (this.dragging) { + draggable = this.dragging.enabled(); + this.dragging.disable(); + } + + this.dragging = new MarkerDrag(this); + + if (draggable) { + this.dragging.enable(); + } + } + }, + + // @method setOpacity(opacity: Number): this + // Changes the opacity of the marker. + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + _updateOpacity: function () { + var opacity = this.options.opacity; + + if (this._icon) { + setOpacity(this._icon, opacity); + } + + if (this._shadow) { + setOpacity(this._shadow, opacity); + } + }, + + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + + _resetZIndex: function () { + this._updateZIndex(0); + }, + + _panOnFocus: function () { + var map = this._map; + if (!map) { return; } + + var iconOpts = this.options.icon.options; + var size = iconOpts.iconSize ? toPoint(iconOpts.iconSize) : toPoint(0, 0); + var anchor = iconOpts.iconAnchor ? toPoint(iconOpts.iconAnchor) : toPoint(0, 0); + + map.panInside(this._latlng, { + paddingTopLeft: anchor, + paddingBottomRight: size.subtract(anchor) + }); + }, + + _getPopupAnchor: function () { + return this.options.icon.options.popupAnchor; + }, + + _getTooltipAnchor: function () { + return this.options.icon.options.tooltipAnchor; + } +}); + + +// factory L.marker(latlng: LatLng, options? : Marker options) + +// @factory L.marker(latlng: LatLng, options? : Marker options) +// Instantiates a Marker object given a geographical point and optionally an options object. +function marker(latlng, options) { + return new Marker(latlng, options); +} + +/* + * @class Path + * @aka L.Path + * @inherits Interactive layer + * + * An abstract class that contains options and constants shared between vector + * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. + */ + +var Path = Layer.extend({ + + // @section + // @aka Path options + options: { + // @option stroke: Boolean = true + // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. + stroke: true, + + // @option color: String = '#3388ff' + // Stroke color + color: '#3388ff', + + // @option weight: Number = 3 + // Stroke width in pixels + weight: 3, + + // @option opacity: Number = 1.0 + // Stroke opacity + opacity: 1, + + // @option lineCap: String= 'round' + // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. + lineCap: 'round', + + // @option lineJoin: String = 'round' + // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. + lineJoin: 'round', + + // @option dashArray: String = null + // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashArray: null, + + // @option dashOffset: String = null + // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashOffset: null, + + // @option fill: Boolean = depends + // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. + fill: false, + + // @option fillColor: String = * + // Fill color. Defaults to the value of the [`color`](#path-color) option + fillColor: null, + + // @option fillOpacity: Number = 0.2 + // Fill opacity. + fillOpacity: 0.2, + + // @option fillRule: String = 'evenodd' + // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. + fillRule: 'evenodd', + + // className: '', + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option bubblingMouseEvents: Boolean = true + // When `true`, a mouse event on this path will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: true + }, + + beforeAdd: function (map) { + // Renderer is set here because we need to call renderer.getEvents + // before this.getEvents. + this._renderer = map.getRenderer(this); + }, + + onAdd: function () { + this._renderer._initPath(this); + this._reset(); + this._renderer._addPath(this); + }, + + onRemove: function () { + this._renderer._removePath(this); + }, + + // @method redraw(): this + // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. + redraw: function () { + if (this._map) { + this._renderer._updatePath(this); + } + return this; + }, + + // @method setStyle(style: Path options): this + // Changes the appearance of a Path based on the options in the `Path options` object. + setStyle: function (style) { + setOptions(this, style); + if (this._renderer) { + this._renderer._updateStyle(this); + if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) { + this._updateBounds(); + } + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all path layers. + bringToFront: function () { + if (this._renderer) { + this._renderer._bringToFront(this); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all path layers. + bringToBack: function () { + if (this._renderer) { + this._renderer._bringToBack(this); + } + return this; + }, + + getElement: function () { + return this._path; + }, + + _reset: function () { + // defined in child classes + this._project(); + this._update(); + }, + + _clickTolerance: function () { + // used when doing hit detection for Canvas layers + return (this.options.stroke ? this.options.weight / 2 : 0) + + (this._renderer.options.tolerance || 0); + } +}); + +/* + * @class CircleMarker + * @aka L.CircleMarker + * @inherits Path + * + * A circle of a fixed size with radius specified in pixels. Extends `Path`. + */ + +var CircleMarker = Path.extend({ + + // @section + // @aka CircleMarker options + options: { + fill: true, + + // @option radius: Number = 10 + // Radius of the circle marker, in pixels + radius: 10 + }, + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + this._radius = this.options.radius; + }, + + // @method setLatLng(latLng: LatLng): this + // Sets the position of a circle marker to a new location. + setLatLng: function (latlng) { + var oldLatLng = this._latlng; + this._latlng = toLatLng(latlng); + this.redraw(); + + // @event move: Event + // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); + }, + + // @method getLatLng(): LatLng + // Returns the current geographical position of the circle marker + getLatLng: function () { + return this._latlng; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle marker. Units are in pixels. + setRadius: function (radius) { + this.options.radius = this._radius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of the circle + getRadius: function () { + return this._radius; + }, + + setStyle : function (options) { + var radius = options && options.radius || this._radius; + Path.prototype.setStyle.call(this, options); + this.setRadius(radius); + return this; + }, + + _project: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + this._updateBounds(); + }, + + _updateBounds: function () { + var r = this._radius, + r2 = this._radiusY || r, + w = this._clickTolerance(), + p = [r + w, r2 + w]; + this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); + }, + + _update: function () { + if (this._map) { + this._updatePath(); + } + }, + + _updatePath: function () { + this._renderer._updateCircle(this); + }, + + _empty: function () { + return this._radius && !this._renderer._bounds.intersects(this._pxBounds); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); + } +}); + + +// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) +// Instantiates a circle marker object given a geographical point, and an optional options object. +function circleMarker(latlng, options) { + return new CircleMarker(latlng, options); +} + +/* + * @class Circle + * @aka L.Circle + * @inherits CircleMarker + * + * A class for drawing circle overlays on a map. Extends `CircleMarker`. + * + * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). + * + * @example + * + * ```js + * L.circle([50.5, 30.5], {radius: 200}).addTo(map); + * ``` + */ + +var Circle = CircleMarker.extend({ + + initialize: function (latlng, options, legacyOptions) { + if (typeof options === 'number') { + // Backwards compatibility with 0.7.x factory (latlng, radius, options?) + options = extend({}, legacyOptions, {radius: options}); + } + setOptions(this, options); + this._latlng = toLatLng(latlng); + + if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } + + // @section + // @aka Circle options + // @option radius: Number; Radius of the circle, in meters. + this._mRadius = this.options.radius; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle. Units are in meters. + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of a circle. Units are in meters. + getRadius: function () { + return this._mRadius; + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + var half = [this._radius, this._radiusY || this._radius]; + + return new LatLngBounds( + this._map.layerPointToLatLng(this._point.subtract(half)), + this._map.layerPointToLatLng(this._point.add(half))); + }, + + setStyle: Path.prototype.setStyle, + + _project: function () { + + var lng = this._latlng.lng, + lat = this._latlng.lat, + map = this._map, + crs = map.options.crs; + + if (crs.distance === Earth.distance) { + var d = Math.PI / 180, + latR = (this._mRadius / Earth.R) / d, + top = map.project([lat + latR, lng]), + bottom = map.project([lat - latR, lng]), + p = top.add(bottom).divideBy(2), + lat2 = map.unproject(p).lat, + lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / + (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; + + if (isNaN(lngR) || lngR === 0) { + lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 + } + + this._point = p.subtract(map.getPixelOrigin()); + this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x; + this._radiusY = p.y - top.y; + + } else { + var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); + + this._point = map.latLngToLayerPoint(this._latlng); + this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; + } + + this._updateBounds(); + } +}); + +// @factory L.circle(latlng: LatLng, options?: Circle options) +// Instantiates a circle object given a geographical point, and an options object +// which contains the circle radius. +// @alternative +// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) +// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. +// Do not use in new applications or plugins. +function circle(latlng, options, legacyOptions) { + return new Circle(latlng, options, legacyOptions); +} + +/* + * @class Polyline + * @aka L.Polyline + * @inherits Path + * + * A class for drawing polyline overlays on a map. Extends `Path`. + * + * @example + * + * ```js + * // create a red polyline from an array of LatLng points + * var latlngs = [ + * [45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2] + * ]; + * + * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polyline + * map.fitBounds(polyline.getBounds()); + * ``` + * + * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: + * + * ```js + * // create a red polyline from an array of arrays of LatLng points + * var latlngs = [ + * [[45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2]], + * [[40.78, -73.91], + * [41.83, -87.62], + * [32.76, -96.72]] + * ]; + * ``` + */ + + +var Polyline = Path.extend({ + + // @section + // @aka Polyline options + options: { + // @option smoothFactor: Number = 1.0 + // How much to simplify the polyline on each zoom level. More means + // better performance and smoother look, and less means more accurate representation. + smoothFactor: 1.0, + + // @option noClip: Boolean = false + // Disable polyline clipping. + noClip: false + }, + + initialize: function (latlngs, options) { + setOptions(this, options); + this._setLatLngs(latlngs); + }, + + // @method getLatLngs(): LatLng[] + // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. + getLatLngs: function () { + return this._latlngs; + }, + + // @method setLatLngs(latlngs: LatLng[]): this + // Replaces all the points in the polyline with the given array of geographical points. + setLatLngs: function (latlngs) { + this._setLatLngs(latlngs); + return this.redraw(); + }, + + // @method isEmpty(): Boolean + // Returns `true` if the Polyline has no LatLngs. + isEmpty: function () { + return !this._latlngs.length; + }, + + // @method closestLayerPoint(p: Point): Point + // Returns the point closest to `p` on the Polyline. + closestLayerPoint: function (p) { + var minDistance = Infinity, + minPoint = null, + closest = _sqClosestPointOnSegment, + p1, p2; + + for (var j = 0, jLen = this._parts.length; j < jLen; j++) { + var points = this._parts[j]; + + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; + + var sqDist = closest(p, p1, p2, true); + + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = closest(p, p1, p2); + } + } + } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; + }, + + // @method getCenter(): LatLng + // Returns the center ([centroid](https://en.wikipedia.org/wiki/Centroid)) of the polyline. + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + return polylineCenter(this._defaultShape(), this._map.options.crs); + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + return this._bounds; + }, + + // @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this + // Adds a given point to the polyline. By default, adds to the first ring of + // the polyline in case of a multi-polyline, but can be overridden by passing + // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). + addLatLng: function (latlng, latlngs) { + latlngs = latlngs || this._defaultShape(); + latlng = toLatLng(latlng); + latlngs.push(latlng); + this._bounds.extend(latlng); + return this.redraw(); + }, + + _setLatLngs: function (latlngs) { + this._bounds = new LatLngBounds(); + this._latlngs = this._convertLatLngs(latlngs); + }, + + _defaultShape: function () { + return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + + // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way + _convertLatLngs: function (latlngs) { + var result = [], + flat = isFlat(latlngs); + + for (var i = 0, len = latlngs.length; i < len; i++) { + if (flat) { + result[i] = toLatLng(latlngs[i]); + this._bounds.extend(result[i]); + } else { + result[i] = this._convertLatLngs(latlngs[i]); + } + } + + return result; + }, + + _project: function () { + var pxBounds = new Bounds(); + this._rings = []; + this._projectLatlngs(this._latlngs, this._rings, pxBounds); + + if (this._bounds.isValid() && pxBounds.isValid()) { + this._rawPxBounds = pxBounds; + this._updateBounds(); + } + }, + + _updateBounds: function () { + var w = this._clickTolerance(), + p = new Point(w, w); + + if (!this._rawPxBounds) { + return; + } + + this._pxBounds = new Bounds([ + this._rawPxBounds.min.subtract(p), + this._rawPxBounds.max.add(p) + ]); + }, + + // recursively turns latlngs into a set of rings with projected coordinates + _projectLatlngs: function (latlngs, result, projectedBounds) { + var flat = latlngs[0] instanceof LatLng, + len = latlngs.length, + i, ring; + + if (flat) { + ring = []; + for (i = 0; i < len; i++) { + ring[i] = this._map.latLngToLayerPoint(latlngs[i]); + projectedBounds.extend(ring[i]); + } + result.push(ring); + } else { + for (i = 0; i < len; i++) { + this._projectLatlngs(latlngs[i], result, projectedBounds); + } + } + }, + + // clip polyline by renderer bounds so that we have less to render for performance + _clipPoints: function () { + var bounds = this._renderer._bounds; + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + var parts = this._parts, + i, j, k, len, len2, segment, points; + + for (i = 0, k = 0, len = this._rings.length; i < len; i++) { + points = this._rings[i]; + + for (j = 0, len2 = points.length; j < len2 - 1; j++) { + segment = clipSegment(points[j], points[j + 1], bounds, j, true); + + if (!segment) { continue; } + + parts[k] = parts[k] || []; + parts[k].push(segment[0]); + + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { + parts[k].push(segment[1]); + k++; + } + } + } + }, + + // simplify each clipped part of the polyline for performance + _simplifyPoints: function () { + var parts = this._parts, + tolerance = this.options.smoothFactor; + + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = simplify(parts[i], tolerance); + } + }, + + _update: function () { + if (!this._map) { return; } + + this._clipPoints(); + this._simplifyPoints(); + this._updatePath(); + }, + + _updatePath: function () { + this._renderer._updatePoly(this); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p, closed) { + var i, j, k, len, len2, part, + w = this._clickTolerance(); + + if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } + + // hit detection for polylines + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { continue; } + + if (pointToSegmentDistance(p, part[k], part[j]) <= w) { + return true; + } + } + } + return false; + } +}); + +// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) +// Instantiates a polyline object given an array of geographical points and +// optionally an options object. You can create a `Polyline` object with +// multiple separate lines (`MultiPolyline`) by passing an array of arrays +// of geographic points. +function polyline(latlngs, options) { + return new Polyline(latlngs, options); +} + +// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1. +Polyline._flat = _flat; + +/* + * @class Polygon + * @aka L.Polygon + * @inherits Polyline + * + * A class for drawing polygon overlays on a map. Extends `Polyline`. + * + * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points. + * + * + * @example + * + * ```js + * // create a red polygon from an array of LatLng points + * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; + * + * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polygon + * map.fitBounds(polygon.getBounds()); + * ``` + * + * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: + * + * ```js + * var latlngs = [ + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ]; + * ``` + * + * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. + * + * ```js + * var latlngs = [ + * [ // first polygon + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ], + * [ // second polygon + * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] + * ] + * ]; + * ``` + */ + +var Polygon = Polyline.extend({ + + options: { + fill: true + }, + + isEmpty: function () { + return !this._latlngs.length || !this._latlngs[0].length; + }, + + // @method getCenter(): LatLng + // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the Polygon. + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + return polygonCenter(this._defaultShape(), this._map.options.crs); + }, + + _convertLatLngs: function (latlngs) { + var result = Polyline.prototype._convertLatLngs.call(this, latlngs), + len = result.length; + + // remove last point if it equals first one + if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { + result.pop(); + } + return result; + }, + + _setLatLngs: function (latlngs) { + Polyline.prototype._setLatLngs.call(this, latlngs); + if (isFlat(this._latlngs)) { + this._latlngs = [this._latlngs]; + } + }, + + _defaultShape: function () { + return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; + }, + + _clipPoints: function () { + // polygons need a different clipping algorithm so we redefine that + + var bounds = this._renderer._bounds, + w = this.options.weight, + p = new Point(w, w); + + // increase clip padding by stroke width to avoid stroke on clip edges + bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + for (var i = 0, len = this._rings.length, clipped; i < len; i++) { + clipped = clipPolygon(this._rings[i], bounds, true); + if (clipped.length) { + this._parts.push(clipped); + } + } + }, + + _updatePath: function () { + this._renderer._updatePoly(this, true); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + var inside = false, + part, p1, p2, i, j, k, len, len2; + + if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } + + // ray casting algorithm for detecting if point is in polygon + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; + + if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } + } + } + + // also check if it's on polygon stroke + return inside || Polyline.prototype._containsPoint.call(this, p, true); + } + +}); + + +// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) +function polygon(latlngs, options) { + return new Polygon(latlngs, options); +} + +/* + * @class GeoJSON + * @aka L.GeoJSON + * @inherits FeatureGroup + * + * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse + * GeoJSON data and display it on the map. Extends `FeatureGroup`. + * + * @example + * + * ```js + * L.geoJSON(data, { + * style: function (feature) { + * return {color: feature.properties.color}; + * } + * }).bindPopup(function (layer) { + * return layer.feature.properties.description; + * }).addTo(map); + * ``` + */ + +var GeoJSON = FeatureGroup.extend({ + + /* @section + * @aka GeoJSON options + * + * @option pointToLayer: Function = * + * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally + * called when data is added, passing the GeoJSON point feature and its `LatLng`. + * The default is to spawn a default `Marker`: + * ```js + * function(geoJsonPoint, latlng) { + * return L.marker(latlng); + * } + * ``` + * + * @option style: Function = * + * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, + * called internally when data is added. + * The default value is to not override any defaults: + * ```js + * function (geoJsonFeature) { + * return {} + * } + * ``` + * + * @option onEachFeature: Function = * + * A `Function` that will be called once for each created `Feature`, after it has + * been created and styled. Useful for attaching events and popups to features. + * The default is to do nothing with the newly created layers: + * ```js + * function (feature, layer) {} + * ``` + * + * @option filter: Function = * + * A `Function` that will be used to decide whether to include a feature or not. + * The default is to include all features: + * ```js + * function (geoJsonFeature) { + * return true; + * } + * ``` + * Note: dynamically changing the `filter` option will have effect only on newly + * added data. It will _not_ re-evaluate already included features. + * + * @option coordsToLatLng: Function = * + * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. + * The default is the `coordsToLatLng` static method. + * + * @option markersInheritOptions: Boolean = false + * Whether default Markers for "Point" type Features inherit from group options. + */ + + initialize: function (geojson, options) { + setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + // @method addData( data ): this + // Adds a GeoJSON object to the layer. + addData: function (geojson) { + var features = isArray(geojson) ? geojson : geojson.features, + i, len, feature; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + // only add this if geometry or geometries are set and not null + feature = features[i]; + if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { + this.addData(feature); + } + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return this; } + + var layer = geometryToLayer(geojson, options); + if (!layer) { + return this; + } + layer.feature = asFeature(geojson); + + layer.defaultOptions = layer.options; + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + // @method resetStyle( layer? ): this + // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. + // If `layer` is omitted, the style of all features in the current layer is reset. + resetStyle: function (layer) { + if (layer === undefined) { + return this.eachLayer(this.resetStyle, this); + } + // reset any custom styles + layer.options = extend({}, layer.defaultOptions); + this._setLayerStyle(layer, this.options.style); + return this; + }, + + // @method setStyle( style ): this + // Changes styles of GeoJSON vector layers with the given style function. + setStyle: function (style) { + return this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (layer.setStyle) { + if (typeof style === 'function') { + style = style(layer.feature); + } + layer.setStyle(style); + } + } +}); + +// @section +// There are several static functions which can be called without instantiating L.GeoJSON: + +// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer +// Creates a `Layer` from a given GeoJSON feature. Can use a custom +// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) +// functions if provided as options. +function geometryToLayer(geojson, options) { + + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry ? geometry.coordinates : null, + layers = [], + pointToLayer = options && options.pointToLayer, + _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, + latlng, latlngs, i, len; + + if (!coords && !geometry) { + return null; + } + + switch (geometry.type) { + case 'Point': + latlng = _coordsToLatLng(coords); + return _pointToLayer(pointToLayer, geojson, latlng, options); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = _coordsToLatLng(coords[i]); + layers.push(_pointToLayer(pointToLayer, geojson, latlng, options)); + } + return new FeatureGroup(layers); + + case 'LineString': + case 'MultiLineString': + latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); + return new Polyline(latlngs, options); + + case 'Polygon': + case 'MultiPolygon': + latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); + return new Polygon(latlngs, options); + + case 'GeometryCollection': + for (i = 0, len = geometry.geometries.length; i < len; i++) { + var geoLayer = geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, options); + + if (geoLayer) { + layers.push(geoLayer); + } + } + return new FeatureGroup(layers); + + case 'FeatureCollection': + for (i = 0, len = geometry.features.length; i < len; i++) { + var featureLayer = geometryToLayer(geometry.features[i], options); + + if (featureLayer) { + layers.push(featureLayer); + } + } + return new FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } +} + +function _pointToLayer(pointToLayerFn, geojson, latlng, options) { + return pointToLayerFn ? + pointToLayerFn(geojson, latlng) : + new Marker(latlng, options && options.markersInheritOptions && options); +} + +// @function coordsToLatLng(coords: Array): LatLng +// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) +// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. +function coordsToLatLng(coords) { + return new LatLng(coords[1], coords[0], coords[2]); +} + +// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array +// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. +// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). +// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. +function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { + var latlngs = []; + + for (var i = 0, len = coords.length, latlng; i < len; i++) { + latlng = levelsDeep ? + coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : + (_coordsToLatLng || coordsToLatLng)(coords[i]); + + latlngs.push(latlng); + } + + return latlngs; +} + +// @function latLngToCoords(latlng: LatLng, precision?: Number|false): Array +// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) +// Coordinates values are rounded with [`formatNum`](#util-formatnum) function. +function latLngToCoords(latlng, precision) { + latlng = toLatLng(latlng); + return latlng.alt !== undefined ? + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; +} + +// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean, precision?: Number|false): Array +// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) +// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. +// Coordinates values are rounded with [`formatNum`](#util-formatnum) function. +function latLngsToCoords(latlngs, levelsDeep, closed, precision) { + var coords = []; + + for (var i = 0, len = latlngs.length; i < len; i++) { + // Check for flat arrays required to ensure unbalanced arrays are correctly converted in recursion + coords.push(levelsDeep ? + latLngsToCoords(latlngs[i], isFlat(latlngs[i]) ? 0 : levelsDeep - 1, closed, precision) : + latLngToCoords(latlngs[i], precision)); + } + + if (!levelsDeep && closed) { + coords.push(coords[0].slice()); + } + + return coords; +} + +function getFeature(layer, newGeometry) { + return layer.feature ? + extend({}, layer.feature, {geometry: newGeometry}) : + asFeature(newGeometry); +} + +// @function asFeature(geojson: Object): Object +// Normalize GeoJSON geometries/features into GeoJSON features. +function asFeature(geojson) { + if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { + return geojson; + } + + return { + type: 'Feature', + properties: {}, + geometry: geojson + }; +} + +var PointToGeoJSON = { + toGeoJSON: function (precision) { + return getFeature(this, { + type: 'Point', + coordinates: latLngToCoords(this.getLatLng(), precision) + }); + } +}; + +// @namespace Marker +// @section Other methods +// @method toGeoJSON(precision?: Number|false): Object +// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`. +// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). +Marker.include(PointToGeoJSON); + +// @namespace CircleMarker +// @method toGeoJSON(precision?: Number|false): Object +// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`. +// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). +Circle.include(PointToGeoJSON); +CircleMarker.include(PointToGeoJSON); + + +// @namespace Polyline +// @method toGeoJSON(precision?: Number|false): Object +// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`. +// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). +Polyline.include({ + toGeoJSON: function (precision) { + var multi = !isFlat(this._latlngs); + + var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'LineString', + coordinates: coords + }); + } +}); + +// @namespace Polygon +// @method toGeoJSON(precision?: Number|false): Object +// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`. +// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). +Polygon.include({ + toGeoJSON: function (precision) { + var holes = !isFlat(this._latlngs), + multi = holes && !isFlat(this._latlngs[0]); + + var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); + + if (!holes) { + coords = [coords]; + } + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'Polygon', + coordinates: coords + }); + } +}); + + +// @namespace LayerGroup +LayerGroup.include({ + toMultiPoint: function (precision) { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON(precision).geometry.coordinates); + }); + + return getFeature(this, { + type: 'MultiPoint', + coordinates: coords + }); + }, + + // @method toGeoJSON(precision?: Number|false): Object + // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`. + // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). + toGeoJSON: function (precision) { + + var type = this.feature && this.feature.geometry && this.feature.geometry.type; + + if (type === 'MultiPoint') { + return this.toMultiPoint(precision); + } + + var isGeometryCollection = type === 'GeometryCollection', + jsons = []; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + var json = layer.toGeoJSON(precision); + if (isGeometryCollection) { + jsons.push(json.geometry); + } else { + var feature = asFeature(json); + // Squash nested feature collections + if (feature.type === 'FeatureCollection') { + jsons.push.apply(jsons, feature.features); + } else { + jsons.push(feature); + } + } + } + }); + + if (isGeometryCollection) { + return getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } +}); + +// @namespace GeoJSON +// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) +// Creates a GeoJSON layer. Optionally accepts an object in +// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map +// (you can alternatively add it later with `addData` method) and an `options` object. +function geoJSON(geojson, options) { + return new GeoJSON(geojson, options); +} + +// Backward compatibility. +var geoJson = geoJSON; + +/* + * @class ImageOverlay + * @aka L.ImageOverlay + * @inherits Interactive layer + * + * Used to load and display a single image over specific bounds of the map. Extends `Layer`. + * + * @example + * + * ```js + * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', + * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; + * L.imageOverlay(imageUrl, imageBounds).addTo(map); + * ``` + */ + +var ImageOverlay = Layer.extend({ + + // @section + // @aka ImageOverlay options + options: { + // @option opacity: Number = 1.0 + // The opacity of the image overlay. + opacity: 1, + + // @option alt: String = '' + // Text for the `alt` attribute of the image (useful for accessibility). + alt: '', + + // @option interactive: Boolean = false + // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. + interactive: false, + + // @option crossOrigin: Boolean|String = false + // Whether the crossOrigin attribute will be added to the image. + // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data. + // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. + crossOrigin: false, + + // @option errorOverlayUrl: String = '' + // URL to the overlay image to show in place of the overlay that failed to load. + errorOverlayUrl: '', + + // @option zIndex: Number = 1 + // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer. + zIndex: 1, + + // @option className: String = '' + // A custom class name to assign to the image. Empty by default. + className: '' + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = toLatLngBounds(bounds); + + setOptions(this, options); + }, + + onAdd: function () { + if (!this._image) { + this._initImage(); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + + if (this.options.interactive) { + addClass(this._image, 'leaflet-interactive'); + this.addInteractiveTarget(this._image); + } + + this.getPane().appendChild(this._image); + this._reset(); + }, + + onRemove: function () { + remove(this._image); + if (this.options.interactive) { + this.removeInteractiveTarget(this._image); + } + }, + + // @method setOpacity(opacity: Number): this + // Sets the opacity of the overlay. + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._image) { + this._updateOpacity(); + } + return this; + }, + + setStyle: function (styleOpts) { + if (styleOpts.opacity) { + this.setOpacity(styleOpts.opacity); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all overlays. + bringToFront: function () { + if (this._map) { + toFront(this._image); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all overlays. + bringToBack: function () { + if (this._map) { + toBack(this._image); + } + return this; + }, + + // @method setUrl(url: String): this + // Changes the URL of the image. + setUrl: function (url) { + this._url = url; + + if (this._image) { + this._image.src = url; + } + return this; + }, + + // @method setBounds(bounds: LatLngBounds): this + // Update the bounds that this ImageOverlay covers + setBounds: function (bounds) { + this._bounds = toLatLngBounds(bounds); + + if (this._map) { + this._reset(); + } + return this; + }, + + getEvents: function () { + var events = { + zoom: this._reset, + viewreset: this._reset + }; + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + + return events; + }, + + // @method setZIndex(value: Number): this + // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. + setZIndex: function (value) { + this.options.zIndex = value; + this._updateZIndex(); + return this; + }, + + // @method getBounds(): LatLngBounds + // Get the bounds that this ImageOverlay covers + getBounds: function () { + return this._bounds; + }, + + // @method getElement(): HTMLElement + // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) + // used by this overlay. + getElement: function () { + return this._image; + }, + + _initImage: function () { + var wasElementSupplied = this._url.tagName === 'IMG'; + var img = this._image = wasElementSupplied ? this._url : create$1('img'); + + addClass(img, 'leaflet-image-layer'); + if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); } + if (this.options.className) { addClass(img, this.options.className); } + + img.onselectstart = falseFn; + img.onmousemove = falseFn; + + // @event load: Event + // Fired when the ImageOverlay layer has loaded its image + img.onload = bind(this.fire, this, 'load'); + img.onerror = bind(this._overlayOnError, this, 'error'); + + if (this.options.crossOrigin || this.options.crossOrigin === '') { + img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; + } + + if (this.options.zIndex) { + this._updateZIndex(); + } + + if (wasElementSupplied) { + this._url = img.src; + return; + } + + img.src = this._url; + img.alt = this.options.alt; + }, + + _animateZoom: function (e) { + var scale = this._map.getZoomScale(e.zoom), + offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; + + setTransform(this._image, offset, scale); + }, + + _reset: function () { + var image = this._image, + bounds = new Bounds( + this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + this._map.latLngToLayerPoint(this._bounds.getSouthEast())), + size = bounds.getSize(); + + setPosition(image, bounds.min); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _updateOpacity: function () { + setOpacity(this._image, this.options.opacity); + }, + + _updateZIndex: function () { + if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { + this._image.style.zIndex = this.options.zIndex; + } + }, + + _overlayOnError: function () { + // @event error: Event + // Fired when the ImageOverlay layer fails to load its image + this.fire('error'); + + var errorUrl = this.options.errorOverlayUrl; + if (errorUrl && this._url !== errorUrl) { + this._url = errorUrl; + this._image.src = errorUrl; + } + }, + + // @method getCenter(): LatLng + // Returns the center of the ImageOverlay. + getCenter: function () { + return this._bounds.getCenter(); + } +}); + +// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) +// Instantiates an image overlay object given the URL of the image and the +// geographical bounds it is tied to. +var imageOverlay = function (url, bounds, options) { + return new ImageOverlay(url, bounds, options); +}; + +/* + * @class VideoOverlay + * @aka L.VideoOverlay + * @inherits ImageOverlay + * + * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. + * + * A video overlay uses the [`