aboutsummaryrefslogtreecommitdiffstats
path: root/app/assets/javascripts
diff options
context:
space:
mode:
authorjpl2017-01-17 15:00:40 +0100
committerjpl2017-01-17 15:00:40 +0100
commitca371715123aa629925b05a7690697852c47b40d (patch)
tree63d95946e92a486e68168986364b95dab87b45c5 /app/assets/javascripts
parentef1199167b7075a1911386dcbf09873bdb1dc606 (diff)
downloadchouette-core-ca371715123aa629925b05a7690697852c47b40d.tar.bz2
Refs #2402: addding openlayers lib
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/OpenLayers/ol-debug.js92558
-rw-r--r--app/assets/javascripts/OpenLayers/ol.js1008
-rw-r--r--app/assets/javascripts/application.js1
3 files changed, 93567 insertions, 0 deletions
diff --git a/app/assets/javascripts/OpenLayers/ol-debug.js b/app/assets/javascripts/OpenLayers/ol-debug.js
new file mode 100644
index 000000000..5513c545a
--- /dev/null
+++ b/app/assets/javascripts/OpenLayers/ol-debug.js
@@ -0,0 +1,92558 @@
+// OpenLayers 3. See https://openlayers.org/
+// License: https://raw.githubusercontent.com/openlayers/ol3/master/LICENSE.md
+// Version: v3.20.1
+;(function (root, factory) {
+ if (typeof exports === "object") {
+ module.exports = factory();
+ } else if (typeof define === "function" && define.amd) {
+ define([], factory);
+ } else {
+ root.ol = factory();
+ }
+}(this, function () {
+ var OPENLAYERS = {};
+ var goog = this.goog = {};
+this.CLOSURE_NO_DEPS = true;
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Bootstrap for the Google JS Library (Closure).
+ *
+ * In uncompiled mode base.js will write out Closure's deps file, unless the
+ * global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects to
+ * include their own deps file(s) from different locations.
+ *
+ * @author arv@google.com (Erik Arvidsson)
+ *
+ * @provideGoog
+ */
+
+
+/**
+ * @define {boolean} Overridden to true by the compiler when
+ * --process_closure_primitives is specified.
+ */
+var COMPILED = false;
+
+
+/**
+ * Base namespace for the Closure library. Checks to see goog is already
+ * defined in the current scope before assigning to prevent clobbering if
+ * base.js is loaded more than once.
+ *
+ * @const
+ */
+var goog = goog || {};
+
+
+/**
+ * Reference to the global context. In most cases this will be 'window'.
+ */
+goog.global = this;
+
+
+/**
+ * A hook for overriding the define values in uncompiled mode.
+ *
+ * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before
+ * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES},
+ * {@code goog.define} will use the value instead of the default value. This
+ * allows flags to be overwritten without compilation (this is normally
+ * accomplished with the compiler's "define" flag).
+ *
+ * Example:
+ * <pre>
+ * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * </pre>
+ *
+ * @type {Object<string, (string|number|boolean)>|undefined}
+ */
+goog.global.CLOSURE_UNCOMPILED_DEFINES;
+
+
+/**
+ * A hook for overriding the define values in uncompiled or compiled mode,
+ * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In
+ * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.
+ *
+ * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or
+ * string literals or the compiler will emit an error.
+ *
+ * While any @define value may be set, only those set with goog.define will be
+ * effective for uncompiled code.
+ *
+ * Example:
+ * <pre>
+ * var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * </pre>
+ *
+ * @type {Object<string, (string|number|boolean)>|undefined}
+ */
+goog.global.CLOSURE_DEFINES;
+
+
+/**
+ * Returns true if the specified value is not undefined.
+ * WARNING: Do not use this to test if an object has a property. Use the in
+ * operator instead.
+ *
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is defined.
+ */
+goog.isDef = function(val) {
+ // void 0 always evaluates to undefined and hence we do not need to depend on
+ // the definition of the global variable named 'undefined'.
+ return val !== void 0;
+};
+
+
+/**
+ * Builds an object structure for the provided namespace path, ensuring that
+ * names that already exist are not overwritten. For example:
+ * "a.b.c" -> a = {};a.b={};a.b.c={};
+ * Used by goog.provide and goog.exportSymbol.
+ * @param {string} name name of the object that this file defines.
+ * @param {*=} opt_object the object to expose at the end of the path.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ * is |goog.global|.
+ * @private
+ */
+goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
+ var parts = name.split('.');
+ var cur = opt_objectToExportTo || goog.global;
+
+ // Internet Explorer exhibits strange behavior when throwing errors from
+ // methods externed in this manner. See the testExportSymbolExceptions in
+ // base_test.html for an example.
+ if (!(parts[0] in cur) && cur.execScript) {
+ cur.execScript('var ' + parts[0]);
+ }
+
+ // Certain browsers cannot parse code in the form for((a in b); c;);
+ // This pattern is produced by the JSCompiler when it collapses the
+ // statement above into the conditional loop below. To prevent this from
+ // happening, use a for-loop and reserve the init logic as below.
+
+ // Parentheses added to eliminate strict JS warning in Firefox.
+ for (var part; parts.length && (part = parts.shift());) {
+ if (!parts.length && goog.isDef(opt_object)) {
+ // last part and we have an object; use it
+ cur[part] = opt_object;
+ } else if (cur[part]) {
+ cur = cur[part];
+ } else {
+ cur = cur[part] = {};
+ }
+ }
+};
+
+
+/**
+ * Defines a named value. In uncompiled mode, the value is retrieved from
+ * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
+ * has the property specified, and otherwise used the defined defaultValue.
+ * When compiled the default can be overridden using the compiler
+ * options or the value set in the CLOSURE_DEFINES object.
+ *
+ * @param {string} name The distinguished name to provide.
+ * @param {string|number|boolean} defaultValue
+ */
+goog.define = function(name, defaultValue) {
+ var value = defaultValue;
+ if (!COMPILED) {
+ if (goog.global.CLOSURE_UNCOMPILED_DEFINES &&
+ Object.prototype.hasOwnProperty.call(
+ goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) {
+ value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name];
+ } else if (
+ goog.global.CLOSURE_DEFINES &&
+ Object.prototype.hasOwnProperty.call(
+ goog.global.CLOSURE_DEFINES, name)) {
+ value = goog.global.CLOSURE_DEFINES[name];
+ }
+ }
+ goog.exportPath_(name, value);
+};
+
+
+/**
+ * @define {boolean} DEBUG is provided as a convenience so that debugging code
+ * that should not be included in a production js_binary can be easily stripped
+ * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
+ * toString() methods should be declared inside an "if (goog.DEBUG)" conditional
+ * because they are generally used for debugging purposes and it is difficult
+ * for the JSCompiler to statically determine whether they are used.
+ */
+goog.define('goog.DEBUG', true);
+
+
+/**
+ * @define {string} LOCALE defines the locale being used for compilation. It is
+ * used to select locale specific data to be compiled in js binary. BUILD rule
+ * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler
+ * option.
+ *
+ * Take into account that the locale code format is important. You should use
+ * the canonical Unicode format with hyphen as a delimiter. Language must be
+ * lowercase, Language Script - Capitalized, Region - UPPERCASE.
+ * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
+ *
+ * See more info about locale codes here:
+ * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
+ *
+ * For language codes you should use values defined by ISO 693-1. See it here
+ * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
+ * this rule: the Hebrew language. For legacy reasons the old code (iw) should
+ * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
+ */
+goog.define('goog.LOCALE', 'en'); // default to en
+
+
+/**
+ * @define {boolean} Whether this code is running on trusted sites.
+ *
+ * On untrusted sites, several native functions can be defined or overridden by
+ * external libraries like Prototype, Datejs, and JQuery and setting this flag
+ * to false forces closure to use its own implementations when possible.
+ *
+ * If your JavaScript can be loaded by a third party site and you are wary about
+ * relying on non-standard implementations, specify
+ * "--define goog.TRUSTED_SITE=false" to the JSCompiler.
+ */
+goog.define('goog.TRUSTED_SITE', true);
+
+
+/**
+ * @define {boolean} Whether a project is expected to be running in strict mode.
+ *
+ * This define can be used to trigger alternate implementations compatible with
+ * running in EcmaScript Strict mode or warn about unavailable functionality.
+ * @see https://goo.gl/PudQ4y
+ *
+ */
+goog.define('goog.STRICT_MODE_COMPATIBLE', false);
+
+
+/**
+ * @define {boolean} Whether code that calls {@link goog.setTestOnly} should
+ * be disallowed in the compilation unit.
+ */
+goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);
+
+
+/**
+ * @define {boolean} Whether to use a Chrome app CSP-compliant method for
+ * loading scripts via goog.require. @see appendScriptSrcNode_.
+ */
+goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);
+
+
+/**
+ * Defines a namespace in Closure.
+ *
+ * A namespace may only be defined once in a codebase. It may be defined using
+ * goog.provide() or goog.module().
+ *
+ * The presence of one or more goog.provide() calls in a file indicates
+ * that the file defines the given objects/namespaces.
+ * Provided symbols must not be null or undefined.
+ *
+ * In addition, goog.provide() creates the object stubs for a namespace
+ * (for example, goog.provide("goog.foo.bar") will create the object
+ * goog.foo.bar if it does not already exist).
+ *
+ * Build tools also scan for provide/require/module statements
+ * to discern dependencies, build dependency files (see deps.js), etc.
+ *
+ * @see goog.require
+ * @see goog.module
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part".
+ */
+goog.provide = function(name) {
+ if (goog.isInModuleLoader_()) {
+ throw Error('goog.provide can not be used within a goog.module.');
+ }
+ if (!COMPILED) {
+ // Ensure that the same namespace isn't provided twice.
+ // A goog.module/goog.provide maps a goog.require to a specific file
+ if (goog.isProvided_(name)) {
+ throw Error('Namespace "' + name + '" already declared.');
+ }
+ }
+
+ goog.constructNamespace_(name);
+};
+
+
+/**
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part".
+ * @param {Object=} opt_obj The object to embed in the namespace.
+ * @private
+ */
+goog.constructNamespace_ = function(name, opt_obj) {
+ if (!COMPILED) {
+ delete goog.implicitNamespaces_[name];
+
+ var namespace = name;
+ while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
+ if (goog.getObjectByName(namespace)) {
+ break;
+ }
+ goog.implicitNamespaces_[namespace] = true;
+ }
+ }
+
+ goog.exportPath_(name, opt_obj);
+};
+
+
+/**
+ * Module identifier validation regexp.
+ * Note: This is a conservative check, it is very possible to be more lenient,
+ * the primary exclusion here is "/" and "\" and a leading ".", these
+ * restrictions are intended to leave the door open for using goog.require
+ * with relative file paths rather than module identifiers.
+ * @private
+ */
+goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;
+
+
+/**
+ * Defines a module in Closure.
+ *
+ * Marks that this file must be loaded as a module and claims the namespace.
+ *
+ * A namespace may only be defined once in a codebase. It may be defined using
+ * goog.provide() or goog.module().
+ *
+ * goog.module() has three requirements:
+ * - goog.module may not be used in the same file as goog.provide.
+ * - goog.module must be the first statement in the file.
+ * - only one goog.module is allowed per file.
+ *
+ * When a goog.module annotated file is loaded, it is enclosed in
+ * a strict function closure. This means that:
+ * - any variables declared in a goog.module file are private to the file
+ * (not global), though the compiler is expected to inline the module.
+ * - The code must obey all the rules of "strict" JavaScript.
+ * - the file will be marked as "use strict"
+ *
+ * NOTE: unlike goog.provide, goog.module does not declare any symbols by
+ * itself. If declared symbols are desired, use
+ * goog.module.declareLegacyNamespace().
+ *
+ *
+ * See the public goog.module proposal: http://goo.gl/Va1hin
+ *
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part", is expected but not required.
+ */
+goog.module = function(name) {
+ if (!goog.isString(name) || !name ||
+ name.search(goog.VALID_MODULE_RE_) == -1) {
+ throw Error('Invalid module identifier');
+ }
+ if (!goog.isInModuleLoader_()) {
+ throw Error('Module ' + name + ' has been loaded incorrectly.');
+ }
+ if (goog.moduleLoaderState_.moduleName) {
+ throw Error('goog.module may only be called once per module.');
+ }
+
+ // Store the module name for the loader.
+ goog.moduleLoaderState_.moduleName = name;
+ if (!COMPILED) {
+ // Ensure that the same namespace isn't provided twice.
+ // A goog.module/goog.provide maps a goog.require to a specific file
+ if (goog.isProvided_(name)) {
+ throw Error('Namespace "' + name + '" already declared.');
+ }
+ delete goog.implicitNamespaces_[name];
+ }
+};
+
+
+/**
+ * @param {string} name The module identifier.
+ * @return {?} The module exports for an already loaded module or null.
+ *
+ * Note: This is not an alternative to goog.require, it does not
+ * indicate a hard dependency, instead it is used to indicate
+ * an optional dependency or to access the exports of a module
+ * that has already been loaded.
+ * @suppress {missingProvide}
+ */
+goog.module.get = function(name) {
+ return goog.module.getInternal_(name);
+};
+
+
+/**
+ * @param {string} name The module identifier.
+ * @return {?} The module exports for an already loaded module or null.
+ * @private
+ */
+goog.module.getInternal_ = function(name) {
+ if (!COMPILED) {
+ if (goog.isProvided_(name)) {
+ // goog.require only return a value with-in goog.module files.
+ return name in goog.loadedModules_ ? goog.loadedModules_[name] :
+ goog.getObjectByName(name);
+ } else {
+ return null;
+ }
+ }
+};
+
+
+/**
+ * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}}
+ */
+goog.moduleLoaderState_ = null;
+
+
+/**
+ * @private
+ * @return {boolean} Whether a goog.module is currently being initialized.
+ */
+goog.isInModuleLoader_ = function() {
+ return goog.moduleLoaderState_ != null;
+};
+
+
+/**
+ * Provide the module's exports as a globally accessible object under the
+ * module's declared name. This is intended to ease migration to goog.module
+ * for files that have existing usages.
+ * @suppress {missingProvide}
+ */
+goog.module.declareLegacyNamespace = function() {
+ if (!COMPILED && !goog.isInModuleLoader_()) {
+ throw new Error(
+ 'goog.module.declareLegacyNamespace must be called from ' +
+ 'within a goog.module');
+ }
+ if (!COMPILED && !goog.moduleLoaderState_.moduleName) {
+ throw Error(
+ 'goog.module must be called prior to ' +
+ 'goog.module.declareLegacyNamespace.');
+ }
+ goog.moduleLoaderState_.declareLegacyNamespace = true;
+};
+
+
+/**
+ * Marks that the current file should only be used for testing, and never for
+ * live code in production.
+ *
+ * In the case of unit tests, the message may optionally be an exact namespace
+ * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
+ * provide (if not explicitly defined in the code).
+ *
+ * @param {string=} opt_message Optional message to add to the error that's
+ * raised when used in production code.
+ */
+goog.setTestOnly = function(opt_message) {
+ if (goog.DISALLOW_TEST_ONLY_CODE) {
+ opt_message = opt_message || '';
+ throw Error(
+ 'Importing test-only code into non-debug environment' +
+ (opt_message ? ': ' + opt_message : '.'));
+ }
+};
+
+
+/**
+ * Forward declares a symbol. This is an indication to the compiler that the
+ * symbol may be used in the source yet is not required and may not be provided
+ * in compilation.
+ *
+ * The most common usage of forward declaration is code that takes a type as a
+ * function parameter but does not need to require it. By forward declaring
+ * instead of requiring, no hard dependency is made, and (if not required
+ * elsewhere) the namespace may never be required and thus, not be pulled
+ * into the JavaScript binary. If it is required elsewhere, it will be type
+ * checked as normal.
+ *
+ *
+ * @param {string} name The namespace to forward declare in the form of
+ * "goog.package.part".
+ */
+goog.forwardDeclare = function(name) {};
+
+
+/**
+ * Forward declare type information. Used to assign types to goog.global
+ * referenced object that would otherwise result in unknown type references
+ * and thus block property disambiguation.
+ */
+goog.forwardDeclare('Document');
+goog.forwardDeclare('HTMLScriptElement');
+goog.forwardDeclare('XMLHttpRequest');
+
+
+if (!COMPILED) {
+ /**
+ * Check if the given name has been goog.provided. This will return false for
+ * names that are available only as implicit namespaces.
+ * @param {string} name name of the object to look for.
+ * @return {boolean} Whether the name has been provided.
+ * @private
+ */
+ goog.isProvided_ = function(name) {
+ return (name in goog.loadedModules_) ||
+ (!goog.implicitNamespaces_[name] &&
+ goog.isDefAndNotNull(goog.getObjectByName(name)));
+ };
+
+ /**
+ * Namespaces implicitly defined by goog.provide. For example,
+ * goog.provide('goog.events.Event') implicitly declares that 'goog' and
+ * 'goog.events' must be namespaces.
+ *
+ * @type {!Object<string, (boolean|undefined)>}
+ * @private
+ */
+ goog.implicitNamespaces_ = {'goog.module': true};
+
+ // NOTE: We add goog.module as an implicit namespace as goog.module is defined
+ // here and because the existing module package has not been moved yet out of
+ // the goog.module namespace. This satisifies both the debug loader and
+ // ahead-of-time dependency management.
+}
+
+
+/**
+ * Returns an object based on its fully qualified external name. The object
+ * is not found if null or undefined. If you are using a compilation pass that
+ * renames property names beware that using this function will not find renamed
+ * properties.
+ *
+ * @param {string} name The fully qualified name.
+ * @param {Object=} opt_obj The object within which to look; default is
+ * |goog.global|.
+ * @return {?} The value (object or primitive) or, if not found, null.
+ */
+goog.getObjectByName = function(name, opt_obj) {
+ var parts = name.split('.');
+ var cur = opt_obj || goog.global;
+ for (var part; part = parts.shift();) {
+ if (goog.isDefAndNotNull(cur[part])) {
+ cur = cur[part];
+ } else {
+ return null;
+ }
+ }
+ return cur;
+};
+
+
+/**
+ * Globalizes a whole namespace, such as goog or goog.lang.
+ *
+ * @param {!Object} obj The namespace to globalize.
+ * @param {Object=} opt_global The object to add the properties to.
+ * @deprecated Properties may be explicitly exported to the global scope, but
+ * this should no longer be done in bulk.
+ */
+goog.globalize = function(obj, opt_global) {
+ var global = opt_global || goog.global;
+ for (var x in obj) {
+ global[x] = obj[x];
+ }
+};
+
+
+/**
+ * Adds a dependency from a file to the files it requires.
+ * @param {string} relPath The path to the js file.
+ * @param {!Array<string>} provides An array of strings with
+ * the names of the objects this file provides.
+ * @param {!Array<string>} requires An array of strings with
+ * the names of the objects this file requires.
+ * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating
+ * how the file must be loaded. The boolean 'true' is equivalent
+ * to {'module': 'goog'} for backwards-compatibility. Valid properties
+ * and values include {'module': 'goog'} and {'lang': 'es6'}.
+ */
+goog.addDependency = function(relPath, provides, requires, opt_loadFlags) {
+ if (goog.DEPENDENCIES_ENABLED) {
+ var provide, require;
+ var path = relPath.replace(/\\/g, '/');
+ var deps = goog.dependencies_;
+ if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') {
+ opt_loadFlags = opt_loadFlags ? {'module': 'goog'} : {};
+ }
+ for (var i = 0; provide = provides[i]; i++) {
+ deps.nameToPath[provide] = path;
+ deps.loadFlags[path] = opt_loadFlags;
+ }
+ for (var j = 0; require = requires[j]; j++) {
+ if (!(path in deps.requires)) {
+ deps.requires[path] = {};
+ }
+ deps.requires[path][require] = true;
+ }
+ }
+};
+
+
+
+
+// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
+// to do "debug-mode" development. The dependency system can sometimes be
+// confusing, as can the debug DOM loader's asynchronous nature.
+//
+// With the DOM loader, a call to goog.require() is not blocking -- the script
+// will not load until some point after the current script. If a namespace is
+// needed at runtime, it needs to be defined in a previous script, or loaded via
+// require() with its registered dependencies.
+//
+// User-defined namespaces may need their own deps file. For a reference on
+// creating a deps file, see:
+// Externally: https://developers.google.com/closure/library/docs/depswriter
+//
+// Because of legacy clients, the DOM loader can't be easily removed from
+// base.js. Work is being done to make it disableable or replaceable for
+// different environments (DOM-less JavaScript interpreters like Rhino or V8,
+// for example). See bootstrap/ for more information.
+
+
+/**
+ * @define {boolean} Whether to enable the debug loader.
+ *
+ * If enabled, a call to goog.require() will attempt to load the namespace by
+ * appending a script tag to the DOM (if the namespace has been registered).
+ *
+ * If disabled, goog.require() will simply assert that the namespace has been
+ * provided (and depend on the fact that some outside tool correctly ordered
+ * the script).
+ */
+goog.define('goog.ENABLE_DEBUG_LOADER', true);
+
+
+/**
+ * @param {string} msg
+ * @private
+ */
+goog.logToConsole_ = function(msg) {
+ if (goog.global.console) {
+ goog.global.console['error'](msg);
+ }
+};
+
+
+/**
+ * Implements a system for the dynamic resolution of dependencies that works in
+ * parallel with the BUILD system. Note that all calls to goog.require will be
+ * stripped by the JSCompiler when the --process_closure_primitives option is
+ * used.
+ * @see goog.provide
+ * @param {string} name Namespace to include (as was given in goog.provide()) in
+ * the form "goog.package.part".
+ * @return {?} If called within a goog.module file, the associated namespace or
+ * module otherwise null.
+ */
+goog.require = function(name) {
+ // If the object already exists we do not need do do anything.
+ if (!COMPILED) {
+ if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) {
+ goog.maybeProcessDeferredDep_(name);
+ }
+
+ if (goog.isProvided_(name)) {
+ if (goog.isInModuleLoader_()) {
+ return goog.module.getInternal_(name);
+ } else {
+ return null;
+ }
+ }
+
+ if (goog.ENABLE_DEBUG_LOADER) {
+ var path = goog.getPathFromDeps_(name);
+ if (path) {
+ goog.writeScripts_(path);
+ return null;
+ }
+ }
+
+ var errorMessage = 'goog.require could not find: ' + name;
+ goog.logToConsole_(errorMessage);
+
+ throw Error(errorMessage);
+ }
+};
+
+
+/**
+ * Path for included scripts.
+ * @type {string}
+ */
+goog.basePath = '';
+
+
+/**
+ * A hook for overriding the base path.
+ * @type {string|undefined}
+ */
+goog.global.CLOSURE_BASE_PATH;
+
+
+/**
+ * Whether to write out Closure's deps file. By default, the deps are written.
+ * @type {boolean|undefined}
+ */
+goog.global.CLOSURE_NO_DEPS;
+
+
+/**
+ * A function to import a single script. This is meant to be overridden when
+ * Closure is being run in non-HTML contexts, such as web workers. It's defined
+ * in the global scope so that it can be set before base.js is loaded, which
+ * allows deps.js to be imported properly.
+ *
+ * The function is passed the script source, which is a relative URI. It should
+ * return true if the script was imported, false otherwise.
+ * @type {(function(string): boolean)|undefined}
+ */
+goog.global.CLOSURE_IMPORT_SCRIPT;
+
+
+/**
+ * Null function used for default values of callbacks, etc.
+ * @return {void} Nothing.
+ */
+goog.nullFunction = function() {};
+
+
+/**
+ * When defining a class Foo with an abstract method bar(), you can do:
+ * Foo.prototype.bar = goog.abstractMethod
+ *
+ * Now if a subclass of Foo fails to override bar(), an error will be thrown
+ * when bar() is invoked.
+ *
+ * Note: This does not take the name of the function to override as an argument
+ * because that would make it more difficult to obfuscate our JavaScript code.
+ *
+ * @type {!Function}
+ * @throws {Error} when invoked to indicate the method should be overridden.
+ */
+goog.abstractMethod = function() {
+ throw Error('unimplemented abstract method');
+};
+
+
+/**
+ * Adds a {@code getInstance} static method that always returns the same
+ * instance object.
+ * @param {!Function} ctor The constructor for the class to add the static
+ * method to.
+ */
+goog.addSingletonGetter = function(ctor) {
+ ctor.getInstance = function() {
+ if (ctor.instance_) {
+ return ctor.instance_;
+ }
+ if (goog.DEBUG) {
+ // NOTE: JSCompiler can't optimize away Array#push.
+ goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
+ }
+ return ctor.instance_ = new ctor;
+ };
+};
+
+
+/**
+ * All singleton classes that have been instantiated, for testing. Don't read
+ * it directly, use the {@code goog.testing.singleton} module. The compiler
+ * removes this variable if unused.
+ * @type {!Array<!Function>}
+ * @private
+ */
+goog.instantiatedSingletons_ = [];
+
+
+/**
+ * @define {boolean} Whether to load goog.modules using {@code eval} when using
+ * the debug loader. This provides a better debugging experience as the
+ * source is unmodified and can be edited using Chrome Workspaces or similar.
+ * However in some environments the use of {@code eval} is banned
+ * so we provide an alternative.
+ */
+goog.define('goog.LOAD_MODULE_USING_EVAL', true);
+
+
+/**
+ * @define {boolean} Whether the exports of goog.modules should be sealed when
+ * possible.
+ */
+goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);
+
+
+/**
+ * The registry of initialized modules:
+ * the module identifier to module exports map.
+ * @private @const {!Object<string, ?>}
+ */
+goog.loadedModules_ = {};
+
+
+/**
+ * True if goog.dependencies_ is available.
+ * @const {boolean}
+ */
+goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
+
+
+/**
+ * @define {string} How to decide whether to transpile. Valid values
+ * are 'always', 'never', and 'detect'. The default ('detect') is to
+ * use feature detection to determine which language levels need
+ * transpilation.
+ */
+// NOTE(user): we could expand this to accept a language level to bypass
+// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but
+// would leave ES3 and ES5 files alone.
+goog.define('goog.TRANSPILE', 'detect');
+
+
+/**
+ * @define {string} Path to the transpiler. Executing the script at this
+ * path (relative to base.js) should define a function $jscomp.transpile.
+ */
+goog.define('goog.TRANSPILER', 'transpile.js');
+
+
+if (goog.DEPENDENCIES_ENABLED) {
+ /**
+ * This object is used to keep track of dependencies and other data that is
+ * used for loading scripts.
+ * @private
+ * @type {{
+ * loadFlags: !Object<string, !Object<string, string>>,
+ * nameToPath: !Object<string, string>,
+ * requires: !Object<string, !Object<string, boolean>>,
+ * visited: !Object<string, boolean>,
+ * written: !Object<string, boolean>,
+ * deferred: !Object<string, string>
+ * }}
+ */
+ goog.dependencies_ = {
+ loadFlags: {}, // 1 to 1
+
+ nameToPath: {}, // 1 to 1
+
+ requires: {}, // 1 to many
+
+ // Used when resolving dependencies to prevent us from visiting file twice.
+ visited: {},
+
+ written: {}, // Used to keep track of script files we have written.
+
+ deferred: {} // Used to track deferred module evaluations in old IEs
+ };
+
+
+ /**
+ * Tries to detect whether is in the context of an HTML document.
+ * @return {boolean} True if it looks like HTML document.
+ * @private
+ */
+ goog.inHtmlDocument_ = function() {
+ /** @type {Document} */
+ var doc = goog.global.document;
+ return doc != null && 'write' in doc; // XULDocument misses write.
+ };
+
+
+ /**
+ * Tries to detect the base path of base.js script that bootstraps Closure.
+ * @private
+ */
+ goog.findBasePath_ = function() {
+ if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) {
+ goog.basePath = goog.global.CLOSURE_BASE_PATH;
+ return;
+ } else if (!goog.inHtmlDocument_()) {
+ return;
+ }
+ /** @type {Document} */
+ var doc = goog.global.document;
+ var scripts = doc.getElementsByTagName('SCRIPT');
+ // Search backwards since the current script is in almost all cases the one
+ // that has base.js.
+ for (var i = scripts.length - 1; i >= 0; --i) {
+ var script = /** @type {!HTMLScriptElement} */ (scripts[i]);
+ var src = script.src;
+ var qmark = src.lastIndexOf('?');
+ var l = qmark == -1 ? src.length : qmark;
+ if (src.substr(l - 7, 7) == 'base.js') {
+ goog.basePath = src.substr(0, l - 7);
+ return;
+ }
+ }
+ };
+
+
+ /**
+ * Imports a script if, and only if, that script hasn't already been imported.
+ * (Must be called at execution time)
+ * @param {string} src Script source.
+ * @param {string=} opt_sourceText The optionally source text to evaluate
+ * @private
+ */
+ goog.importScript_ = function(src, opt_sourceText) {
+ var importScript =
+ goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_;
+ if (importScript(src, opt_sourceText)) {
+ goog.dependencies_.written[src] = true;
+ }
+ };
+
+
+ /**
+ * Whether the browser is IE9 or earlier, which needs special handling
+ * for deferred modules.
+ * @const @private {boolean}
+ */
+ goog.IS_OLD_IE_ =
+ !!(!goog.global.atob && goog.global.document && goog.global.document.all);
+
+
+ /**
+ * Given a URL initiate retrieval and execution of a script that needs
+ * pre-processing.
+ * @param {string} src Script source URL.
+ * @param {boolean} isModule Whether this is a goog.module.
+ * @param {boolean} needsTranspile Whether this source needs transpilation.
+ * @private
+ */
+ goog.importProcessedScript_ = function(src, isModule, needsTranspile) {
+ // In an attempt to keep browsers from timing out loading scripts using
+ // synchronous XHRs, put each load in its own script block.
+ var bootstrap = 'goog.retrieveAndExec_("' + src + '", ' + isModule + ', ' +
+ needsTranspile + ');';
+
+ goog.importScript_('', bootstrap);
+ };
+
+
+ /** @private {!Array<string>} */
+ goog.queuedModules_ = [];
+
+
+ /**
+ * Return an appropriate module text. Suitable to insert into
+ * a script tag (that is unescaped).
+ * @param {string} srcUrl
+ * @param {string} scriptText
+ * @return {string}
+ * @private
+ */
+ goog.wrapModule_ = function(srcUrl, scriptText) {
+ if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {
+ return '' +
+ 'goog.loadModule(function(exports) {' +
+ '"use strict";' + scriptText +
+ '\n' + // terminate any trailing single line comment.
+ ';return exports' +
+ '});' +
+ '\n//# sourceURL=' + srcUrl + '\n';
+ } else {
+ return '' +
+ 'goog.loadModule(' +
+ goog.global.JSON.stringify(
+ scriptText + '\n//# sourceURL=' + srcUrl + '\n') +
+ ');';
+ }
+ };
+
+ // On IE9 and earlier, it is necessary to handle
+ // deferred module loads. In later browsers, the
+ // code to be evaluated is simply inserted as a script
+ // block in the correct order. To eval deferred
+ // code at the right time, we piggy back on goog.require to call
+ // goog.maybeProcessDeferredDep_.
+ //
+ // The goog.requires are used both to bootstrap
+ // the loading process (when no deps are available) and
+ // declare that they should be available.
+ //
+ // Here we eval the sources, if all the deps are available
+ // either already eval'd or goog.require'd. This will
+ // be the case when all the dependencies have already
+ // been loaded, and the dependent module is loaded.
+ //
+ // But this alone isn't sufficient because it is also
+ // necessary to handle the case where there is no root
+ // that is not deferred. For that there we register for an event
+ // and trigger goog.loadQueuedModules_ handle any remaining deferred
+ // evaluations.
+
+ /**
+ * Handle any remaining deferred goog.module evals.
+ * @private
+ */
+ goog.loadQueuedModules_ = function() {
+ var count = goog.queuedModules_.length;
+ if (count > 0) {
+ var queue = goog.queuedModules_;
+ goog.queuedModules_ = [];
+ for (var i = 0; i < count; i++) {
+ var path = queue[i];
+ goog.maybeProcessDeferredPath_(path);
+ }
+ }
+ };
+
+
+ /**
+ * Eval the named module if its dependencies are
+ * available.
+ * @param {string} name The module to load.
+ * @private
+ */
+ goog.maybeProcessDeferredDep_ = function(name) {
+ if (goog.isDeferredModule_(name) && goog.allDepsAreAvailable_(name)) {
+ var path = goog.getPathFromDeps_(name);
+ goog.maybeProcessDeferredPath_(goog.basePath + path);
+ }
+ };
+
+ /**
+ * @param {string} name The module to check.
+ * @return {boolean} Whether the name represents a
+ * module whose evaluation has been deferred.
+ * @private
+ */
+ goog.isDeferredModule_ = function(name) {
+ var path = goog.getPathFromDeps_(name);
+ var loadFlags = path && goog.dependencies_.loadFlags[path] || {};
+ if (path && (loadFlags['module'] == 'goog' ||
+ goog.needsTranspile_(loadFlags['lang']))) {
+ var abspath = goog.basePath + path;
+ return (abspath) in goog.dependencies_.deferred;
+ }
+ return false;
+ };
+
+ /**
+ * @param {string} name The module to check.
+ * @return {boolean} Whether the name represents a
+ * module whose declared dependencies have all been loaded
+ * (eval'd or a deferred module load)
+ * @private
+ */
+ goog.allDepsAreAvailable_ = function(name) {
+ var path = goog.getPathFromDeps_(name);
+ if (path && (path in goog.dependencies_.requires)) {
+ for (var requireName in goog.dependencies_.requires[path]) {
+ if (!goog.isProvided_(requireName) &&
+ !goog.isDeferredModule_(requireName)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+
+ /**
+ * @param {string} abspath
+ * @private
+ */
+ goog.maybeProcessDeferredPath_ = function(abspath) {
+ if (abspath in goog.dependencies_.deferred) {
+ var src = goog.dependencies_.deferred[abspath];
+ delete goog.dependencies_.deferred[abspath];
+ goog.globalEval(src);
+ }
+ };
+
+
+ /**
+ * Load a goog.module from the provided URL. This is not a general purpose
+ * code loader and does not support late loading code, that is it should only
+ * be used during page load. This method exists to support unit tests and
+ * "debug" loaders that would otherwise have inserted script tags. Under the
+ * hood this needs to use a synchronous XHR and is not recommeneded for
+ * production code.
+ *
+ * The module's goog.requires must have already been satisified; an exception
+ * will be thrown if this is not the case. This assumption is that no
+ * "deps.js" file exists, so there is no way to discover and locate the
+ * module-to-be-loaded's dependencies and no attempt is made to do so.
+ *
+ * There should only be one attempt to load a module. If
+ * "goog.loadModuleFromUrl" is called for an already loaded module, an
+ * exception will be throw.
+ *
+ * @param {string} url The URL from which to attempt to load the goog.module.
+ */
+ goog.loadModuleFromUrl = function(url) {
+ // Because this executes synchronously, we don't need to do any additional
+ // bookkeeping. When "goog.loadModule" the namespace will be marked as
+ // having been provided which is sufficient.
+ goog.retrieveAndExec_(url, true, false);
+ };
+
+
+ /**
+ * Writes a new script pointing to {@code src} directly into the DOM.
+ *
+ * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for
+ * the fallback mechanism.
+ *
+ * @param {string} src The script URL.
+ * @private
+ */
+ goog.writeScriptSrcNode_ = function(src) {
+ goog.global.document.write(
+ '<script type="text/javascript" src="' + src + '"></' +
+ 'script>');
+ };
+
+
+ /**
+ * Appends a new script node to the DOM using a CSP-compliant mechanism. This
+ * method exists as a fallback for document.write (which is not allowed in a
+ * strict CSP context, e.g., Chrome apps).
+ *
+ * NOTE: This method is not analogous to using document.write to insert a
+ * <script> tag; specifically, the user agent will execute a script added by
+ * document.write immediately after the current script block finishes
+ * executing, whereas the DOM-appended script node will not be executed until
+ * the entire document is parsed and executed. That is to say, this script is
+ * added to the end of the script execution queue.
+ *
+ * The page must not attempt to call goog.required entities until after the
+ * document has loaded, e.g., in or after the window.onload callback.
+ *
+ * @param {string} src The script URL.
+ * @private
+ */
+ goog.appendScriptSrcNode_ = function(src) {
+ /** @type {Document} */
+ var doc = goog.global.document;
+ var scriptEl =
+ /** @type {HTMLScriptElement} */ (doc.createElement('script'));
+ scriptEl.type = 'text/javascript';
+ scriptEl.src = src;
+ scriptEl.defer = false;
+ scriptEl.async = false;
+ doc.head.appendChild(scriptEl);
+ };
+
+
+ /**
+ * The default implementation of the import function. Writes a script tag to
+ * import the script.
+ *
+ * @param {string} src The script url.
+ * @param {string=} opt_sourceText The optionally source text to evaluate
+ * @return {boolean} True if the script was imported, false otherwise.
+ * @private
+ */
+ goog.writeScriptTag_ = function(src, opt_sourceText) {
+ if (goog.inHtmlDocument_()) {
+ /** @type {!HTMLDocument} */
+ var doc = goog.global.document;
+
+ // If the user tries to require a new symbol after document load,
+ // something has gone terribly wrong. Doing a document.write would
+ // wipe out the page. This does not apply to the CSP-compliant method
+ // of writing script tags.
+ if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&
+ doc.readyState == 'complete') {
+ // Certain test frameworks load base.js multiple times, which tries
+ // to write deps.js each time. If that happens, just fail silently.
+ // These frameworks wipe the page between each load of base.js, so this
+ // is OK.
+ var isDeps = /\bdeps.js$/.test(src);
+ if (isDeps) {
+ return false;
+ } else {
+ throw Error('Cannot write "' + src + '" after document load');
+ }
+ }
+
+ if (opt_sourceText === undefined) {
+ if (!goog.IS_OLD_IE_) {
+ if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {
+ goog.appendScriptSrcNode_(src);
+ } else {
+ goog.writeScriptSrcNode_(src);
+ }
+ } else {
+ var state = " onreadystatechange='goog.onScriptLoad_(this, " +
+ ++goog.lastNonModuleScriptIndex_ + ")' ";
+ doc.write(
+ '<script type="text/javascript" src="' + src + '"' + state +
+ '></' +
+ 'script>');
+ }
+ } else {
+ doc.write(
+ '<script type="text/javascript">' + opt_sourceText + '</' +
+ 'script>');
+ }
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+
+ /**
+ * Determines whether the given language needs to be transpiled.
+ * @param {string} lang
+ * @return {boolean}
+ * @private
+ */
+ goog.needsTranspile_ = function(lang) {
+ if (goog.TRANSPILE == 'always') {
+ return true;
+ } else if (goog.TRANSPILE == 'never') {
+ return false;
+ } else if (!goog.transpiledLanguages_) {
+ goog.transpiledLanguages_ = {'es5': true, 'es6': true, 'es6-impl': true};
+ /** @preserveTry */
+ try {
+ // Perform some quick conformance checks, to distinguish
+ // between browsers that support es5, es6-impl, or es6.
+
+ // Identify ES3-only browsers by their incorrect treatment of commas.
+ goog.transpiledLanguages_['es5'] = eval('[1,].length!=1');
+
+ // As browsers mature, features will be moved from the full test
+ // into the impl test. This must happen before the corresponding
+ // features are changed in the Closure Compiler's FeatureSet object.
+
+ // Test 1: es6-impl [FF49, Edge 13, Chrome 49]
+ // (a) let/const keyword, (b) class expressions, (c) Map object,
+ // (d) iterable arguments, (e) spread operator
+ var es6implTest =
+ 'let a={};const X=class{constructor(){}x(z){return new Map([' +
+ '...arguments]).get(z[0])==3}};return new X().x([a,3])';
+
+ // Test 2: es6 [FF50 (?), Edge 14 (?), Chrome 50]
+ // (a) default params (specifically shadowing locals),
+ // (b) destructuring, (c) block-scoped functions,
+ // (d) for-of (const), (e) new.target/Reflect.construct
+ var es6fullTest =
+ 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' +
+ 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' +
+ 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' +
+ 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' +
+ '==3}';
+
+ if (eval('(()=>{"use strict";' + es6implTest + '})()')) {
+ goog.transpiledLanguages_['es6-impl'] = false;
+ }
+ if (eval('(()=>{"use strict";' + es6fullTest + '})()')) {
+ goog.transpiledLanguages_['es6'] = false;
+ }
+ } catch (err) {
+ }
+ }
+ return !!goog.transpiledLanguages_[lang];
+ };
+
+
+ /** @private {?Object<string, boolean>} */
+ goog.transpiledLanguages_ = null;
+
+
+ /** @private {number} */
+ goog.lastNonModuleScriptIndex_ = 0;
+
+
+ /**
+ * A readystatechange handler for legacy IE
+ * @param {!HTMLScriptElement} script
+ * @param {number} scriptIndex
+ * @return {boolean}
+ * @private
+ */
+ goog.onScriptLoad_ = function(script, scriptIndex) {
+ // for now load the modules when we reach the last script,
+ // later allow more inter-mingling.
+ if (script.readyState == 'complete' &&
+ goog.lastNonModuleScriptIndex_ == scriptIndex) {
+ goog.loadQueuedModules_();
+ }
+ return true;
+ };
+
+ /**
+ * Resolves dependencies based on the dependencies added using addDependency
+ * and calls importScript_ in the correct order.
+ * @param {string} pathToLoad The path from which to start discovering
+ * dependencies.
+ * @private
+ */
+ goog.writeScripts_ = function(pathToLoad) {
+ /** @type {!Array<string>} The scripts we need to write this time. */
+ var scripts = [];
+ var seenScript = {};
+ var deps = goog.dependencies_;
+
+ /** @param {string} path */
+ function visitNode(path) {
+ if (path in deps.written) {
+ return;
+ }
+
+ // We have already visited this one. We can get here if we have cyclic
+ // dependencies.
+ if (path in deps.visited) {
+ return;
+ }
+
+ deps.visited[path] = true;
+
+ if (path in deps.requires) {
+ for (var requireName in deps.requires[path]) {
+ // If the required name is defined, we assume that it was already
+ // bootstrapped by other means.
+ if (!goog.isProvided_(requireName)) {
+ if (requireName in deps.nameToPath) {
+ visitNode(deps.nameToPath[requireName]);
+ } else {
+ throw Error('Undefined nameToPath for ' + requireName);
+ }
+ }
+ }
+ }
+
+ if (!(path in seenScript)) {
+ seenScript[path] = true;
+ scripts.push(path);
+ }
+ }
+
+ visitNode(pathToLoad);
+
+ // record that we are going to load all these scripts.
+ for (var i = 0; i < scripts.length; i++) {
+ var path = scripts[i];
+ goog.dependencies_.written[path] = true;
+ }
+
+ // If a module is loaded synchronously then we need to
+ // clear the current inModuleLoader value, and restore it when we are
+ // done loading the current "requires".
+ var moduleState = goog.moduleLoaderState_;
+ goog.moduleLoaderState_ = null;
+
+ for (var i = 0; i < scripts.length; i++) {
+ var path = scripts[i];
+ if (path) {
+ var loadFlags = deps.loadFlags[path] || {};
+ var needsTranspile = goog.needsTranspile_(loadFlags['lang']);
+ if (loadFlags['module'] == 'goog' || needsTranspile) {
+ goog.importProcessedScript_(
+ goog.basePath + path, loadFlags['module'] == 'goog',
+ needsTranspile);
+ } else {
+ goog.importScript_(goog.basePath + path);
+ }
+ } else {
+ goog.moduleLoaderState_ = moduleState;
+ throw Error('Undefined script input');
+ }
+ }
+
+ // restore the current "module loading state"
+ goog.moduleLoaderState_ = moduleState;
+ };
+
+
+ /**
+ * Looks at the dependency rules and tries to determine the script file that
+ * fulfills a particular rule.
+ * @param {string} rule In the form goog.namespace.Class or project.script.
+ * @return {?string} Url corresponding to the rule, or null.
+ * @private
+ */
+ goog.getPathFromDeps_ = function(rule) {
+ if (rule in goog.dependencies_.nameToPath) {
+ return goog.dependencies_.nameToPath[rule];
+ } else {
+ return null;
+ }
+ };
+
+ goog.findBasePath_();
+
+ // Allow projects to manage the deps files themselves.
+ if (!goog.global.CLOSURE_NO_DEPS) {
+ goog.importScript_(goog.basePath + 'deps.js');
+ }
+}
+
+
+/**
+ * @param {function(?):?|string} moduleDef The module definition.
+ */
+goog.loadModule = function(moduleDef) {
+ // NOTE: we allow function definitions to be either in the from
+ // of a string to eval (which keeps the original source intact) or
+ // in a eval forbidden environment (CSP) we allow a function definition
+ // which in its body must call {@code goog.module}, and return the exports
+ // of the module.
+ var previousState = goog.moduleLoaderState_;
+ try {
+ goog.moduleLoaderState_ = {
+ moduleName: undefined,
+ declareLegacyNamespace: false
+ };
+ var exports;
+ if (goog.isFunction(moduleDef)) {
+ exports = moduleDef.call(undefined, {});
+ } else if (goog.isString(moduleDef)) {
+ exports = goog.loadModuleFromSource_.call(undefined, moduleDef);
+ } else {
+ throw Error('Invalid module definition');
+ }
+
+ var moduleName = goog.moduleLoaderState_.moduleName;
+ if (!goog.isString(moduleName) || !moduleName) {
+ throw Error('Invalid module name \"' + moduleName + '\"');
+ }
+
+ // Don't seal legacy namespaces as they may be uses as a parent of
+ // another namespace
+ if (goog.moduleLoaderState_.declareLegacyNamespace) {
+ goog.constructNamespace_(moduleName, exports);
+ } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) {
+ Object.seal(exports);
+ }
+
+ goog.loadedModules_[moduleName] = exports;
+ } finally {
+ goog.moduleLoaderState_ = previousState;
+ }
+};
+
+
+/**
+ * @private @const {function(string):?}
+ *
+ * The new type inference warns because this function has no formal
+ * parameters, but its jsdoc says that it takes one argument.
+ * (The argument is used via arguments[0], but NTI does not detect this.)
+ * @suppress {newCheckTypes}
+ */
+goog.loadModuleFromSource_ = function() {
+ // NOTE: we avoid declaring parameters or local variables here to avoid
+ // masking globals or leaking values into the module definition.
+ 'use strict';
+ var exports = {};
+ eval(arguments[0]);
+ return exports;
+};
+
+
+/**
+ * Normalize a file path by removing redundant ".." and extraneous "." file
+ * path components.
+ * @param {string} path
+ * @return {string}
+ * @private
+ */
+goog.normalizePath_ = function(path) {
+ var components = path.split('/');
+ var i = 0;
+ while (i < components.length) {
+ if (components[i] == '.') {
+ components.splice(i, 1);
+ } else if (
+ i && components[i] == '..' && components[i - 1] &&
+ components[i - 1] != '..') {
+ components.splice(--i, 2);
+ } else {
+ i++;
+ }
+ }
+ return components.join('/');
+};
+
+
+/**
+ * Loads file by synchronous XHR. Should not be used in production environments.
+ * @param {string} src Source URL.
+ * @return {?string} File contents, or null if load failed.
+ * @private
+ */
+goog.loadFileSync_ = function(src) {
+ if (goog.global.CLOSURE_LOAD_FILE_SYNC) {
+ return goog.global.CLOSURE_LOAD_FILE_SYNC(src);
+ } else {
+ try {
+ /** @type {XMLHttpRequest} */
+ var xhr = new goog.global['XMLHttpRequest']();
+ xhr.open('get', src, false);
+ xhr.send();
+ // NOTE: Successful http: requests have a status of 200, but successful
+ // file: requests may have a status of zero. Any other status, or a
+ // thrown exception (particularly in case of file: requests) indicates
+ // some sort of error, which we treat as a missing or unavailable file.
+ return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null;
+ } catch (err) {
+ // No need to rethrow or log, since errors should show up on their own.
+ return null;
+ }
+ }
+};
+
+
+/**
+ * Retrieve and execute a script that needs some sort of wrapping.
+ * @param {string} src Script source URL.
+ * @param {boolean} isModule Whether to load as a module.
+ * @param {boolean} needsTranspile Whether to transpile down to ES3.
+ * @private
+ */
+goog.retrieveAndExec_ = function(src, isModule, needsTranspile) {
+ if (!COMPILED) {
+ // The full but non-canonicalized URL for later use.
+ var originalPath = src;
+ // Canonicalize the path, removing any /./ or /../ since Chrome's debugging
+ // console doesn't auto-canonicalize XHR loads as it does <script> srcs.
+ src = goog.normalizePath_(src);
+
+ var importScript =
+ goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_;
+
+ var scriptText = goog.loadFileSync_(src);
+ if (scriptText == null) {
+ throw new Error('Load of "' + src + '" failed');
+ }
+
+ if (needsTranspile) {
+ scriptText = goog.transpile_.call(goog.global, scriptText, src);
+ }
+
+ if (isModule) {
+ scriptText = goog.wrapModule_(src, scriptText);
+ } else {
+ scriptText += '\n//# sourceURL=' + src;
+ }
+ var isOldIE = goog.IS_OLD_IE_;
+ if (isOldIE) {
+ goog.dependencies_.deferred[originalPath] = scriptText;
+ goog.queuedModules_.push(originalPath);
+ } else {
+ importScript(src, scriptText);
+ }
+ }
+};
+
+
+/**
+ * Lazily retrieves the transpiler and applies it to the source.
+ * @param {string} code JS code.
+ * @param {string} path Path to the code.
+ * @return {string} The transpiled code.
+ * @private
+ */
+goog.transpile_ = function(code, path) {
+ var jscomp = goog.global['$jscomp'];
+ if (!jscomp) {
+ goog.global['$jscomp'] = jscomp = {};
+ }
+ var transpile = jscomp.transpile;
+ if (!transpile) {
+ var transpilerPath = goog.basePath + goog.TRANSPILER;
+ var transpilerCode = goog.loadFileSync_(transpilerPath);
+ if (transpilerCode) {
+ // This must be executed synchronously, since by the time we know we
+ // need it, we're about to load and write the ES6 code synchronously,
+ // so a normal script-tag load will be too slow.
+ eval(transpilerCode + '\n//# sourceURL=' + transpilerPath);
+ // Note: transpile.js reassigns goog.global['$jscomp'] so pull it again.
+ jscomp = goog.global['$jscomp'];
+ transpile = jscomp.transpile;
+ }
+ }
+ if (!transpile) {
+ // The transpiler is an optional component. If it's not available then
+ // replace it with a pass-through function that simply logs.
+ var suffix = ' requires transpilation but no transpiler was found.';
+ transpile = jscomp.transpile = function(code, path) {
+ // TODO(user): figure out some way to get this error to show up
+ // in test results, noting that the failure may occur in many
+ // different ways, including in loadModule() before the test
+ // runner even comes up.
+ goog.logToConsole_(path + suffix);
+ return code;
+ };
+ }
+ // Note: any transpilation errors/warnings will be logged to the console.
+ return transpile(code, path);
+};
+
+
+//==============================================================================
+// Language Enhancements
+//==============================================================================
+
+
+/**
+ * This is a "fixed" version of the typeof operator. It differs from the typeof
+ * operator in such a way that null returns 'null' and arrays return 'array'.
+ * @param {?} value The value to get the type of.
+ * @return {string} The name of the type.
+ */
+goog.typeOf = function(value) {
+ var s = typeof value;
+ if (s == 'object') {
+ if (value) {
+ // Check these first, so we can avoid calling Object.prototype.toString if
+ // possible.
+ //
+ // IE improperly marshals typeof across execution contexts, but a
+ // cross-context object will still return false for "instanceof Object".
+ if (value instanceof Array) {
+ return 'array';
+ } else if (value instanceof Object) {
+ return s;
+ }
+
+ // HACK: In order to use an Object prototype method on the arbitrary
+ // value, the compiler requires the value be cast to type Object,
+ // even though the ECMA spec explicitly allows it.
+ var className = Object.prototype.toString.call(
+ /** @type {!Object} */ (value));
+ // In Firefox 3.6, attempting to access iframe window objects' length
+ // property throws an NS_ERROR_FAILURE, so we need to special-case it
+ // here.
+ if (className == '[object Window]') {
+ return 'object';
+ }
+
+ // We cannot always use constructor == Array or instanceof Array because
+ // different frames have different Array objects. In IE6, if the iframe
+ // where the array was created is destroyed, the array loses its
+ // prototype. Then dereferencing val.splice here throws an exception, so
+ // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
+ // so that will work. In this case, this function will return false and
+ // most array functions will still work because the array is still
+ // array-like (supports length and []) even though it has lost its
+ // prototype.
+ // Mark Miller noticed that Object.prototype.toString
+ // allows access to the unforgeable [[Class]] property.
+ // 15.2.4.2 Object.prototype.toString ( )
+ // When the toString method is called, the following steps are taken:
+ // 1. Get the [[Class]] property of this object.
+ // 2. Compute a string value by concatenating the three strings
+ // "[object ", Result(1), and "]".
+ // 3. Return Result(2).
+ // and this behavior survives the destruction of the execution context.
+ if ((className == '[object Array]' ||
+ // In IE all non value types are wrapped as objects across window
+ // boundaries (not iframe though) so we have to do object detection
+ // for this edge case.
+ typeof value.length == 'number' &&
+ typeof value.splice != 'undefined' &&
+ typeof value.propertyIsEnumerable != 'undefined' &&
+ !value.propertyIsEnumerable('splice')
+
+ )) {
+ return 'array';
+ }
+ // HACK: There is still an array case that fails.
+ // function ArrayImpostor() {}
+ // ArrayImpostor.prototype = [];
+ // var impostor = new ArrayImpostor;
+ // this can be fixed by getting rid of the fast path
+ // (value instanceof Array) and solely relying on
+ // (value && Object.prototype.toString.vall(value) === '[object Array]')
+ // but that would require many more function calls and is not warranted
+ // unless closure code is receiving objects from untrusted sources.
+
+ // IE in cross-window calls does not correctly marshal the function type
+ // (it appears just as an object) so we cannot use just typeof val ==
+ // 'function'. However, if the object has a call property, it is a
+ // function.
+ if ((className == '[object Function]' ||
+ typeof value.call != 'undefined' &&
+ typeof value.propertyIsEnumerable != 'undefined' &&
+ !value.propertyIsEnumerable('call'))) {
+ return 'function';
+ }
+
+ } else {
+ return 'null';
+ }
+
+ } else if (s == 'function' && typeof value.call == 'undefined') {
+ // In Safari typeof nodeList returns 'function', and on Firefox typeof
+ // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
+ // would like to return object for those and we can detect an invalid
+ // function by making sure that the function object has a call method.
+ return 'object';
+ }
+ return s;
+};
+
+
+/**
+ * Returns true if the specified value is null.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is null.
+ */
+goog.isNull = function(val) {
+ return val === null;
+};
+
+
+/**
+ * Returns true if the specified value is defined and not null.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is defined and not null.
+ */
+goog.isDefAndNotNull = function(val) {
+ // Note that undefined == null.
+ return val != null;
+};
+
+
+/**
+ * Returns true if the specified value is an array.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is an array.
+ */
+goog.isArray = function(val) {
+ return goog.typeOf(val) == 'array';
+};
+
+
+/**
+ * Returns true if the object looks like an array. To qualify as array like
+ * the value needs to be either a NodeList or an object with a Number length
+ * property. As a special case, a function value is not array like, because its
+ * length property is fixed to correspond to the number of expected arguments.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is an array.
+ */
+goog.isArrayLike = function(val) {
+ var type = goog.typeOf(val);
+ // We do not use goog.isObject here in order to exclude function values.
+ return type == 'array' || type == 'object' && typeof val.length == 'number';
+};
+
+
+/**
+ * Returns true if the object looks like a Date. To qualify as Date-like the
+ * value needs to be an object and have a getFullYear() function.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a like a Date.
+ */
+goog.isDateLike = function(val) {
+ return goog.isObject(val) && typeof val.getFullYear == 'function';
+};
+
+
+/**
+ * Returns true if the specified value is a string.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a string.
+ */
+goog.isString = function(val) {
+ return typeof val == 'string';
+};
+
+
+/**
+ * Returns true if the specified value is a boolean.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is boolean.
+ */
+goog.isBoolean = function(val) {
+ return typeof val == 'boolean';
+};
+
+
+/**
+ * Returns true if the specified value is a number.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a number.
+ */
+goog.isNumber = function(val) {
+ return typeof val == 'number';
+};
+
+
+/**
+ * Returns true if the specified value is a function.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a function.
+ */
+goog.isFunction = function(val) {
+ return goog.typeOf(val) == 'function';
+};
+
+
+/**
+ * Returns true if the specified value is an object. This includes arrays and
+ * functions.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is an object.
+ */
+goog.isObject = function(val) {
+ var type = typeof val;
+ return type == 'object' && val != null || type == 'function';
+ // return Object(val) === val also works, but is slower, especially if val is
+ // not an object.
+};
+
+
+/**
+ * Gets a unique ID for an object. This mutates the object so that further calls
+ * with the same object as a parameter returns the same value. The unique ID is
+ * guaranteed to be unique across the current session amongst objects that are
+ * passed into {@code getUid}. There is no guarantee that the ID is unique or
+ * consistent across sessions. It is unsafe to generate unique ID for function
+ * prototypes.
+ *
+ * @param {Object} obj The object to get the unique ID for.
+ * @return {number} The unique ID for the object.
+ */
+goog.getUid = function(obj) {
+ // TODO(arv): Make the type stricter, do not accept null.
+
+ // In Opera window.hasOwnProperty exists but always returns false so we avoid
+ // using it. As a consequence the unique ID generated for BaseClass.prototype
+ // and SubClass.prototype will be the same.
+ return obj[goog.UID_PROPERTY_] ||
+ (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
+};
+
+
+/**
+ * Whether the given object is already assigned a unique ID.
+ *
+ * This does not modify the object.
+ *
+ * @param {!Object} obj The object to check.
+ * @return {boolean} Whether there is an assigned unique id for the object.
+ */
+goog.hasUid = function(obj) {
+ return !!obj[goog.UID_PROPERTY_];
+};
+
+
+/**
+ * Removes the unique ID from an object. This is useful if the object was
+ * previously mutated using {@code goog.getUid} in which case the mutation is
+ * undone.
+ * @param {Object} obj The object to remove the unique ID field from.
+ */
+goog.removeUid = function(obj) {
+ // TODO(arv): Make the type stricter, do not accept null.
+
+ // In IE, DOM nodes are not instances of Object and throw an exception if we
+ // try to delete. Instead we try to use removeAttribute.
+ if (obj !== null && 'removeAttribute' in obj) {
+ obj.removeAttribute(goog.UID_PROPERTY_);
+ }
+ /** @preserveTry */
+ try {
+ delete obj[goog.UID_PROPERTY_];
+ } catch (ex) {
+ }
+};
+
+
+/**
+ * Name for unique ID property. Initialized in a way to help avoid collisions
+ * with other closure JavaScript on the same page.
+ * @type {string}
+ * @private
+ */
+goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
+
+
+/**
+ * Counter for UID.
+ * @type {number}
+ * @private
+ */
+goog.uidCounter_ = 0;
+
+
+/**
+ * Adds a hash code field to an object. The hash code is unique for the
+ * given object.
+ * @param {Object} obj The object to get the hash code for.
+ * @return {number} The hash code for the object.
+ * @deprecated Use goog.getUid instead.
+ */
+goog.getHashCode = goog.getUid;
+
+
+/**
+ * Removes the hash code field from an object.
+ * @param {Object} obj The object to remove the field from.
+ * @deprecated Use goog.removeUid instead.
+ */
+goog.removeHashCode = goog.removeUid;
+
+
+/**
+ * Clones a value. The input may be an Object, Array, or basic type. Objects and
+ * arrays will be cloned recursively.
+ *
+ * WARNINGS:
+ * <code>goog.cloneObject</code> does not detect reference loops. Objects that
+ * refer to themselves will cause infinite recursion.
+ *
+ * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
+ * UIDs created by <code>getUid</code> into cloned results.
+ *
+ * @param {*} obj The value to clone.
+ * @return {*} A clone of the input value.
+ * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
+ */
+goog.cloneObject = function(obj) {
+ var type = goog.typeOf(obj);
+ if (type == 'object' || type == 'array') {
+ if (obj.clone) {
+ return obj.clone();
+ }
+ var clone = type == 'array' ? [] : {};
+ for (var key in obj) {
+ clone[key] = goog.cloneObject(obj[key]);
+ }
+ return clone;
+ }
+
+ return obj;
+};
+
+
+/**
+ * A native implementation of goog.bind.
+ * @param {Function} fn A function to partially apply.
+ * @param {Object|undefined} selfObj Specifies the object which this should
+ * point to when the function is run.
+ * @param {...*} var_args Additional arguments that are partially applied to the
+ * function.
+ * @return {!Function} A partially-applied form of the function bind() was
+ * invoked as a method of.
+ * @private
+ * @suppress {deprecated} The compiler thinks that Function.prototype.bind is
+ * deprecated because some people have declared a pure-JS version.
+ * Only the pure-JS version is truly deprecated.
+ */
+goog.bindNative_ = function(fn, selfObj, var_args) {
+ return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
+};
+
+
+/**
+ * A pure-JS implementation of goog.bind.
+ * @param {Function} fn A function to partially apply.
+ * @param {Object|undefined} selfObj Specifies the object which this should
+ * point to when the function is run.
+ * @param {...*} var_args Additional arguments that are partially applied to the
+ * function.
+ * @return {!Function} A partially-applied form of the function bind() was
+ * invoked as a method of.
+ * @private
+ */
+goog.bindJs_ = function(fn, selfObj, var_args) {
+ if (!fn) {
+ throw new Error();
+ }
+
+ if (arguments.length > 2) {
+ var boundArgs = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ // Prepend the bound arguments to the current arguments.
+ var newArgs = Array.prototype.slice.call(arguments);
+ Array.prototype.unshift.apply(newArgs, boundArgs);
+ return fn.apply(selfObj, newArgs);
+ };
+
+ } else {
+ return function() { return fn.apply(selfObj, arguments); };
+ }
+};
+
+
+/**
+ * Partially applies this function to a particular 'this object' and zero or
+ * more arguments. The result is a new function with some arguments of the first
+ * function pre-filled and the value of this 'pre-specified'.
+ *
+ * Remaining arguments specified at call-time are appended to the pre-specified
+ * ones.
+ *
+ * Also see: {@link #partial}.
+ *
+ * Usage:
+ * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
+ * barMethBound('arg3', 'arg4');</pre>
+ *
+ * @param {?function(this:T, ...)} fn A function to partially apply.
+ * @param {T} selfObj Specifies the object which this should point to when the
+ * function is run.
+ * @param {...*} var_args Additional arguments that are partially applied to the
+ * function.
+ * @return {!Function} A partially-applied form of the function goog.bind() was
+ * invoked as a method of.
+ * @template T
+ * @suppress {deprecated} See above.
+ */
+goog.bind = function(fn, selfObj, var_args) {
+ // TODO(nicksantos): narrow the type signature.
+ if (Function.prototype.bind &&
+ // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
+ // extension environment. This means that for Chrome extensions, they get
+ // the implementation of Function.prototype.bind that calls goog.bind
+ // instead of the native one. Even worse, we don't want to introduce a
+ // circular dependency between goog.bind and Function.prototype.bind, so
+ // we have to hack this to make sure it works correctly.
+ Function.prototype.bind.toString().indexOf('native code') != -1) {
+ goog.bind = goog.bindNative_;
+ } else {
+ goog.bind = goog.bindJs_;
+ }
+ return goog.bind.apply(null, arguments);
+};
+
+
+/**
+ * Like goog.bind(), except that a 'this object' is not required. Useful when
+ * the target function is already bound.
+ *
+ * Usage:
+ * var g = goog.partial(f, arg1, arg2);
+ * g(arg3, arg4);
+ *
+ * @param {Function} fn A function to partially apply.
+ * @param {...*} var_args Additional arguments that are partially applied to fn.
+ * @return {!Function} A partially-applied form of the function goog.partial()
+ * was invoked as a method of.
+ */
+goog.partial = function(fn, var_args) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ // Clone the array (with slice()) and append additional arguments
+ // to the existing arguments.
+ var newArgs = args.slice();
+ newArgs.push.apply(newArgs, arguments);
+ return fn.apply(this, newArgs);
+ };
+};
+
+
+/**
+ * Copies all the members of a source object to a target object. This method
+ * does not work on all browsers for all objects that contain keys such as
+ * toString or hasOwnProperty. Use goog.object.extend for this purpose.
+ * @param {Object} target Target.
+ * @param {Object} source Source.
+ */
+goog.mixin = function(target, source) {
+ for (var x in source) {
+ target[x] = source[x];
+ }
+
+ // For IE7 or lower, the for-in-loop does not contain any properties that are
+ // not enumerable on the prototype object (for example, isPrototypeOf from
+ // Object.prototype) but also it will not include 'replace' on objects that
+ // extend String and change 'replace' (not that it is common for anyone to
+ // extend anything except Object).
+};
+
+
+/**
+ * @return {number} An integer value representing the number of milliseconds
+ * between midnight, January 1, 1970 and the current time.
+ */
+goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
+ // Unary plus operator converts its operand to a number which in
+ // the case of
+ // a date is done by calling getTime().
+ return +new Date();
+ });
+
+
+/**
+ * Evals JavaScript in the global scope. In IE this uses execScript, other
+ * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
+ * global scope (for example, in Safari), appends a script tag instead.
+ * Throws an exception if neither execScript or eval is defined.
+ * @param {string} script JavaScript string.
+ */
+goog.globalEval = function(script) {
+ if (goog.global.execScript) {
+ goog.global.execScript(script, 'JavaScript');
+ } else if (goog.global.eval) {
+ // Test to see if eval works
+ if (goog.evalWorksForGlobals_ == null) {
+ goog.global.eval('var _evalTest_ = 1;');
+ if (typeof goog.global['_evalTest_'] != 'undefined') {
+ try {
+ delete goog.global['_evalTest_'];
+ } catch (ignore) {
+ // Microsoft edge fails the deletion above in strict mode.
+ }
+ goog.evalWorksForGlobals_ = true;
+ } else {
+ goog.evalWorksForGlobals_ = false;
+ }
+ }
+
+ if (goog.evalWorksForGlobals_) {
+ goog.global.eval(script);
+ } else {
+ /** @type {Document} */
+ var doc = goog.global.document;
+ var scriptElt =
+ /** @type {!HTMLScriptElement} */ (doc.createElement('SCRIPT'));
+ scriptElt.type = 'text/javascript';
+ scriptElt.defer = false;
+ // Note(user): can't use .innerHTML since "t('<test>')" will fail and
+ // .text doesn't work in Safari 2. Therefore we append a text node.
+ scriptElt.appendChild(doc.createTextNode(script));
+ doc.body.appendChild(scriptElt);
+ doc.body.removeChild(scriptElt);
+ }
+ } else {
+ throw Error('goog.globalEval not available');
+ }
+};
+
+
+/**
+ * Indicates whether or not we can call 'eval' directly to eval code in the
+ * global scope. Set to a Boolean by the first call to goog.globalEval (which
+ * empirically tests whether eval works for globals). @see goog.globalEval
+ * @type {?boolean}
+ * @private
+ */
+goog.evalWorksForGlobals_ = null;
+
+
+/**
+ * Optional map of CSS class names to obfuscated names used with
+ * goog.getCssName().
+ * @private {!Object<string, string>|undefined}
+ * @see goog.setCssNameMapping
+ */
+goog.cssNameMapping_;
+
+
+/**
+ * Optional obfuscation style for CSS class names. Should be set to either
+ * 'BY_WHOLE' or 'BY_PART' if defined.
+ * @type {string|undefined}
+ * @private
+ * @see goog.setCssNameMapping
+ */
+goog.cssNameMappingStyle_;
+
+
+/**
+ * Handles strings that are intended to be used as CSS class names.
+ *
+ * This function works in tandem with @see goog.setCssNameMapping.
+ *
+ * Without any mapping set, the arguments are simple joined with a hyphen and
+ * passed through unaltered.
+ *
+ * When there is a mapping, there are two possible styles in which these
+ * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
+ * of the passed in css name is rewritten according to the map. In the BY_WHOLE
+ * style, the full css name is looked up in the map directly. If a rewrite is
+ * not specified by the map, the compiler will output a warning.
+ *
+ * When the mapping is passed to the compiler, it will replace calls to
+ * goog.getCssName with the strings from the mapping, e.g.
+ * var x = goog.getCssName('foo');
+ * var y = goog.getCssName(this.baseClass, 'active');
+ * becomes:
+ * var x = 'foo';
+ * var y = this.baseClass + '-active';
+ *
+ * If one argument is passed it will be processed, if two are passed only the
+ * modifier will be processed, as it is assumed the first argument was generated
+ * as a result of calling goog.getCssName.
+ *
+ * @param {string} className The class name.
+ * @param {string=} opt_modifier A modifier to be appended to the class name.
+ * @return {string} The class name or the concatenation of the class name and
+ * the modifier.
+ */
+goog.getCssName = function(className, opt_modifier) {
+ var getMapping = function(cssName) {
+ return goog.cssNameMapping_[cssName] || cssName;
+ };
+
+ var renameByParts = function(cssName) {
+ // Remap all the parts individually.
+ var parts = cssName.split('-');
+ var mapped = [];
+ for (var i = 0; i < parts.length; i++) {
+ mapped.push(getMapping(parts[i]));
+ }
+ return mapped.join('-');
+ };
+
+ var rename;
+ if (goog.cssNameMapping_) {
+ rename =
+ goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts;
+ } else {
+ rename = function(a) { return a; };
+ }
+
+ if (opt_modifier) {
+ return className + '-' + rename(opt_modifier);
+ } else {
+ return rename(className);
+ }
+};
+
+
+/**
+ * Sets the map to check when returning a value from goog.getCssName(). Example:
+ * <pre>
+ * goog.setCssNameMapping({
+ * "goog": "a",
+ * "disabled": "b",
+ * });
+ *
+ * var x = goog.getCssName('goog');
+ * // The following evaluates to: "a a-b".
+ * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
+ * </pre>
+ * When declared as a map of string literals to string literals, the JSCompiler
+ * will replace all calls to goog.getCssName() using the supplied map if the
+ * --process_closure_primitives flag is set.
+ *
+ * @param {!Object} mapping A map of strings to strings where keys are possible
+ * arguments to goog.getCssName() and values are the corresponding values
+ * that should be returned.
+ * @param {string=} opt_style The style of css name mapping. There are two valid
+ * options: 'BY_PART', and 'BY_WHOLE'.
+ * @see goog.getCssName for a description.
+ */
+goog.setCssNameMapping = function(mapping, opt_style) {
+ goog.cssNameMapping_ = mapping;
+ goog.cssNameMappingStyle_ = opt_style;
+};
+
+
+/**
+ * To use CSS renaming in compiled mode, one of the input files should have a
+ * call to goog.setCssNameMapping() with an object literal that the JSCompiler
+ * can extract and use to replace all calls to goog.getCssName(). In uncompiled
+ * mode, JavaScript code should be loaded before this base.js file that declares
+ * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
+ * to ensure that the mapping is loaded before any calls to goog.getCssName()
+ * are made in uncompiled mode.
+ *
+ * A hook for overriding the CSS name mapping.
+ * @type {!Object<string, string>|undefined}
+ */
+goog.global.CLOSURE_CSS_NAME_MAPPING;
+
+
+if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
+ // This does not call goog.setCssNameMapping() because the JSCompiler
+ // requires that goog.setCssNameMapping() be called with an object literal.
+ goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
+}
+
+
+/**
+ * Gets a localized message.
+ *
+ * This function is a compiler primitive. If you give the compiler a localized
+ * message bundle, it will replace the string at compile-time with a localized
+ * version, and expand goog.getMsg call to a concatenated string.
+ *
+ * Messages must be initialized in the form:
+ * <code>
+ * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
+ * </code>
+ *
+ * This function produces a string which should be treated as plain text. Use
+ * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to
+ * produce SafeHtml.
+ *
+ * @param {string} str Translatable string, places holders in the form {$foo}.
+ * @param {Object<string, string>=} opt_values Maps place holder name to value.
+ * @return {string} message with placeholders filled.
+ */
+goog.getMsg = function(str, opt_values) {
+ if (opt_values) {
+ str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
+ return (opt_values != null && key in opt_values) ? opt_values[key] :
+ match;
+ });
+ }
+ return str;
+};
+
+
+/**
+ * Gets a localized message. If the message does not have a translation, gives a
+ * fallback message.
+ *
+ * This is useful when introducing a new message that has not yet been
+ * translated into all languages.
+ *
+ * This function is a compiler primitive. Must be used in the form:
+ * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
+ * where MSG_A and MSG_B were initialized with goog.getMsg.
+ *
+ * @param {string} a The preferred message.
+ * @param {string} b The fallback message.
+ * @return {string} The best translated message.
+ */
+goog.getMsgWithFallback = function(a, b) {
+ return a;
+};
+
+
+/**
+ * Exposes an unobfuscated global namespace path for the given object.
+ * Note that fields of the exported object *will* be obfuscated, unless they are
+ * exported in turn via this function or goog.exportProperty.
+ *
+ * Also handy for making public items that are defined in anonymous closures.
+ *
+ * ex. goog.exportSymbol('public.path.Foo', Foo);
+ *
+ * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
+ * public.path.Foo.staticFunction();
+ *
+ * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
+ * Foo.prototype.myMethod);
+ * new public.path.Foo().myMethod();
+ *
+ * @param {string} publicPath Unobfuscated name to export.
+ * @param {*} object Object the name should point to.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ * is goog.global.
+ */
+goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
+ goog.exportPath_(publicPath, object, opt_objectToExportTo);
+};
+
+
+/**
+ * Exports a property unobfuscated into the object's namespace.
+ * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
+ * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
+ * @param {Object} object Object whose static property is being exported.
+ * @param {string} publicName Unobfuscated name to export.
+ * @param {*} symbol Object the name should point to.
+ */
+goog.exportProperty = function(object, publicName, symbol) {
+ object[publicName] = symbol;
+};
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * Usage:
+ * <pre>
+ * function ParentClass(a, b) { }
+ * ParentClass.prototype.foo = function(a) { };
+ *
+ * function ChildClass(a, b, c) {
+ * ChildClass.base(this, 'constructor', a, b);
+ * }
+ * goog.inherits(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo(); // This works.
+ * </pre>
+ *
+ * @param {!Function} childCtor Child class.
+ * @param {!Function} parentCtor Parent class.
+ */
+goog.inherits = function(childCtor, parentCtor) {
+ /** @constructor */
+ function tempCtor() {}
+ tempCtor.prototype = parentCtor.prototype;
+ childCtor.superClass_ = parentCtor.prototype;
+ childCtor.prototype = new tempCtor();
+ /** @override */
+ childCtor.prototype.constructor = childCtor;
+
+ /**
+ * Calls superclass constructor/method.
+ *
+ * This function is only available if you use goog.inherits to
+ * express inheritance relationships between classes.
+ *
+ * NOTE: This is a replacement for goog.base and for superClass_
+ * property defined in childCtor.
+ *
+ * @param {!Object} me Should always be "this".
+ * @param {string} methodName The method name to call. Calling
+ * superclass constructor can be done with the special string
+ * 'constructor'.
+ * @param {...*} var_args The arguments to pass to superclass
+ * method/constructor.
+ * @return {*} The return value of the superclass method/constructor.
+ */
+ childCtor.base = function(me, methodName, var_args) {
+ // Copying using loop to avoid deop due to passing arguments object to
+ // function. This is faster in many JS engines as of late 2014.
+ var args = new Array(arguments.length - 2);
+ for (var i = 2; i < arguments.length; i++) {
+ args[i - 2] = arguments[i];
+ }
+ return parentCtor.prototype[methodName].apply(me, args);
+ };
+};
+
+
+/**
+ * Call up to the superclass.
+ *
+ * If this is called from a constructor, then this calls the superclass
+ * constructor with arguments 1-N.
+ *
+ * If this is called from a prototype method, then you must pass the name of the
+ * method as the second argument to this function. If you do not, you will get a
+ * runtime error. This calls the superclass' method with arguments 2-N.
+ *
+ * This function only works if you use goog.inherits to express inheritance
+ * relationships between your classes.
+ *
+ * This function is a compiler primitive. At compile-time, the compiler will do
+ * macro expansion to remove a lot of the extra overhead that this function
+ * introduces. The compiler will also enforce a lot of the assumptions that this
+ * function makes, and treat it as a compiler error if you break them.
+ *
+ * @param {!Object} me Should always be "this".
+ * @param {*=} opt_methodName The method name if calling a super method.
+ * @param {...*} var_args The rest of the arguments.
+ * @return {*} The return value of the superclass method.
+ * @suppress {es5Strict} This method can not be used in strict mode, but
+ * all Closure Library consumers must depend on this file.
+ */
+goog.base = function(me, opt_methodName, var_args) {
+ var caller = arguments.callee.caller;
+
+ if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {
+ throw Error(
+ 'arguments.caller not defined. goog.base() cannot be used ' +
+ 'with strict mode code. See ' +
+ 'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
+ }
+
+ if (caller.superClass_) {
+ // Copying using loop to avoid deop due to passing arguments object to
+ // function. This is faster in many JS engines as of late 2014.
+ var ctorArgs = new Array(arguments.length - 1);
+ for (var i = 1; i < arguments.length; i++) {
+ ctorArgs[i - 1] = arguments[i];
+ }
+ // This is a constructor. Call the superclass constructor.
+ return caller.superClass_.constructor.apply(me, ctorArgs);
+ }
+
+ // Copying using loop to avoid deop due to passing arguments object to
+ // function. This is faster in many JS engines as of late 2014.
+ var args = new Array(arguments.length - 2);
+ for (var i = 2; i < arguments.length; i++) {
+ args[i - 2] = arguments[i];
+ }
+ var foundCaller = false;
+ for (var ctor = me.constructor; ctor;
+ ctor = ctor.superClass_ && ctor.superClass_.constructor) {
+ if (ctor.prototype[opt_methodName] === caller) {
+ foundCaller = true;
+ } else if (foundCaller) {
+ return ctor.prototype[opt_methodName].apply(me, args);
+ }
+ }
+
+ // If we did not find the caller in the prototype chain, then one of two
+ // things happened:
+ // 1) The caller is an instance method.
+ // 2) This method was not called by the right caller.
+ if (me[opt_methodName] === caller) {
+ return me.constructor.prototype[opt_methodName].apply(me, args);
+ } else {
+ throw Error(
+ 'goog.base called from a method of one name ' +
+ 'to a method of a different name');
+ }
+};
+
+
+/**
+ * Allow for aliasing within scope functions. This function exists for
+ * uncompiled code - in compiled code the calls will be inlined and the aliases
+ * applied. In uncompiled code the function is simply run since the aliases as
+ * written are valid JavaScript.
+ *
+ *
+ * @param {function()} fn Function to call. This function can contain aliases
+ * to namespaces (e.g. "var dom = goog.dom") or classes
+ * (e.g. "var Timer = goog.Timer").
+ */
+goog.scope = function(fn) {
+ if (goog.isInModuleLoader_()) {
+ throw Error('goog.scope is not supported within a goog.module.');
+ }
+ fn.call(goog.global);
+};
+
+
+/*
+ * To support uncompiled, strict mode bundles that use eval to divide source
+ * like so:
+ * eval('someSource;//# sourceUrl sourcefile.js');
+ * We need to export the globally defined symbols "goog" and "COMPILED".
+ * Exporting "goog" breaks the compiler optimizations, so we required that
+ * be defined externally.
+ * NOTE: We don't use goog.exportSymbol here because we don't want to trigger
+ * extern generation when that compiler option is enabled.
+ */
+if (!COMPILED) {
+ goog.global['COMPILED'] = COMPILED;
+}
+
+
+//==============================================================================
+// goog.defineClass implementation
+//==============================================================================
+
+
+/**
+ * Creates a restricted form of a Closure "class":
+ * - from the compiler's perspective, the instance returned from the
+ * constructor is sealed (no new properties may be added). This enables
+ * better checks.
+ * - the compiler will rewrite this definition to a form that is optimal
+ * for type checking and optimization (initially this will be a more
+ * traditional form).
+ *
+ * @param {Function} superClass The superclass, Object or null.
+ * @param {goog.defineClass.ClassDescriptor} def
+ * An object literal describing
+ * the class. It may have the following properties:
+ * "constructor": the constructor function
+ * "statics": an object literal containing methods to add to the constructor
+ * as "static" methods or a function that will receive the constructor
+ * function as its only parameter to which static properties can
+ * be added.
+ * all other properties are added to the prototype.
+ * @return {!Function} The class constructor.
+ */
+goog.defineClass = function(superClass, def) {
+ // TODO(johnlenz): consider making the superClass an optional parameter.
+ var constructor = def.constructor;
+ var statics = def.statics;
+ // Wrap the constructor prior to setting up the prototype and static methods.
+ if (!constructor || constructor == Object.prototype.constructor) {
+ constructor = function() {
+ throw Error('cannot instantiate an interface (no constructor defined).');
+ };
+ }
+
+ var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
+ if (superClass) {
+ goog.inherits(cls, superClass);
+ }
+
+ // Remove all the properties that should not be copied to the prototype.
+ delete def.constructor;
+ delete def.statics;
+
+ goog.defineClass.applyProperties_(cls.prototype, def);
+ if (statics != null) {
+ if (statics instanceof Function) {
+ statics(cls);
+ } else {
+ goog.defineClass.applyProperties_(cls, statics);
+ }
+ }
+
+ return cls;
+};
+
+
+/**
+ * @typedef {{
+ * constructor: (!Function|undefined),
+ * statics: (Object|undefined|function(Function):void)
+ * }}
+ * @suppress {missingProvide}
+ */
+goog.defineClass.ClassDescriptor;
+
+
+/**
+ * @define {boolean} Whether the instances returned by goog.defineClass should
+ * be sealed when possible.
+ *
+ * When sealing is disabled the constructor function will not be wrapped by
+ * goog.defineClass, making it incompatible with ES6 class methods.
+ */
+goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);
+
+
+/**
+ * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
+ * defined, this function will wrap the constructor in a function that seals the
+ * results of the provided constructor function.
+ *
+ * @param {!Function} ctr The constructor whose results maybe be sealed.
+ * @param {Function} superClass The superclass constructor.
+ * @return {!Function} The replacement constructor.
+ * @private
+ */
+goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
+ if (!goog.defineClass.SEAL_CLASS_INSTANCES) {
+ // Do now wrap the constructor when sealing is disabled. Angular code
+ // depends on this for injection to work properly.
+ return ctr;
+ }
+
+ // Compute whether the constructor is sealable at definition time, rather
+ // than when the instance is being constructed.
+ var superclassSealable = !goog.defineClass.isUnsealable_(superClass);
+
+ /**
+ * @this {Object}
+ * @return {?}
+ */
+ var wrappedCtr = function() {
+ // Don't seal an instance of a subclass when it calls the constructor of
+ // its super class as there is most likely still setup to do.
+ var instance = ctr.apply(this, arguments) || this;
+ instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];
+
+ if (this.constructor === wrappedCtr && superclassSealable &&
+ Object.seal instanceof Function) {
+ Object.seal(instance);
+ }
+ return instance;
+ };
+
+ return wrappedCtr;
+};
+
+
+/**
+ * @param {Function} ctr The constructor to test.
+ * @returns {boolean} Whether the constructor has been tagged as unsealable
+ * using goog.tagUnsealableClass.
+ * @private
+ */
+goog.defineClass.isUnsealable_ = function(ctr) {
+ return ctr && ctr.prototype &&
+ ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_];
+};
+
+
+// TODO(johnlenz): share these values with the goog.object
+/**
+ * The names of the fields that are defined on Object.prototype.
+ * @type {!Array<string>}
+ * @private
+ * @const
+ */
+goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+];
+
+
+// TODO(johnlenz): share this function with the goog.object
+/**
+ * @param {!Object} target The object to add properties to.
+ * @param {!Object} source The object to copy properties from.
+ * @private
+ */
+goog.defineClass.applyProperties_ = function(target, source) {
+ // TODO(johnlenz): update this to support ES5 getters/setters
+
+ var key;
+ for (key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+
+ // For IE the for-in-loop does not contain any properties that are not
+ // enumerable on the prototype object (for example isPrototypeOf from
+ // Object.prototype) and it will also not include 'replace' on objects that
+ // extend String and change 'replace' (not that it is common for anyone to
+ // extend anything except Object).
+ for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
+ key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+};
+
+
+/**
+ * Sealing classes breaks the older idiom of assigning properties on the
+ * prototype rather than in the constructor. As such, goog.defineClass
+ * must not seal subclasses of these old-style classes until they are fixed.
+ * Until then, this marks a class as "broken", instructing defineClass
+ * not to seal subclasses.
+ * @param {!Function} ctr The legacy constructor to tag as unsealable.
+ */
+goog.tagUnsealableClass = function(ctr) {
+ if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {
+ ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;
+ }
+};
+
+
+/**
+ * Name for unsealable tag property.
+ * @const @private {string}
+ */
+goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';
+
+goog.provide('ol');
+
+
+/**
+ * Constants defined with the define tag cannot be changed in application
+ * code, but can be set at compile time.
+ * Some reduce the size of the build in advanced compile mode.
+ */
+
+
+/**
+ * @define {boolean} Enable debug mode. Default is `true`.
+ */
+ol.DEBUG = true;
+
+
+/**
+ * @define {boolean} Assume touch. Default is `false`.
+ */
+ol.ASSUME_TOUCH = false;
+
+
+/**
+ * TODO: rename this to something having to do with tile grids
+ * see https://github.com/openlayers/ol3/issues/2076
+ * @define {number} Default maximum zoom for default tile grids.
+ */
+ol.DEFAULT_MAX_ZOOM = 42;
+
+
+/**
+ * @define {number} Default min zoom level for the map view. Default is `0`.
+ */
+ol.DEFAULT_MIN_ZOOM = 0;
+
+
+/**
+ * @define {number} Default maximum allowed threshold (in pixels) for
+ * reprojection triangulation. Default is `0.5`.
+ */
+ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD = 0.5;
+
+
+/**
+ * @define {number} Default tile size.
+ */
+ol.DEFAULT_TILE_SIZE = 256;
+
+
+/**
+ * @define {string} Default WMS version.
+ */
+ol.DEFAULT_WMS_VERSION = '1.3.0';
+
+
+/**
+ * @define {number} Hysteresis pixels.
+ */
+ol.DRAG_BOX_HYSTERESIS_PIXELS = 8;
+
+
+/**
+ * @define {boolean} Enable the Canvas renderer. Default is `true`. Setting
+ * this to false at compile time in advanced mode removes all code
+ * supporting the Canvas renderer from the build.
+ */
+ol.ENABLE_CANVAS = true;
+
+
+/**
+ * @define {boolean} Enable rendering of ol.layer.Image based layers. Default
+ * is `true`. Setting this to false at compile time in advanced mode removes
+ * all code supporting Image layers from the build.
+ */
+ol.ENABLE_IMAGE = true;
+
+
+/**
+ * @define {boolean} Enable integration with the Proj4js library. Default is
+ * `true`.
+ */
+ol.ENABLE_PROJ4JS = true;
+
+
+/**
+ * @define {boolean} Enable automatic reprojection of raster sources. Default is
+ * `true`.
+ */
+ol.ENABLE_RASTER_REPROJECTION = true;
+
+
+/**
+ * @define {boolean} Enable rendering of ol.layer.Tile based layers. Default is
+ * `true`. Setting this to false at compile time in advanced mode removes
+ * all code supporting Tile layers from the build.
+ */
+ol.ENABLE_TILE = true;
+
+
+/**
+ * @define {boolean} Enable rendering of ol.layer.Vector based layers. Default
+ * is `true`. Setting this to false at compile time in advanced mode removes
+ * all code supporting Vector layers from the build.
+ */
+ol.ENABLE_VECTOR = true;
+
+
+/**
+ * @define {boolean} Enable rendering of ol.layer.VectorTile based layers.
+ * Default is `true`. Setting this to false at compile time in advanced mode
+ * removes all code supporting VectorTile layers from the build.
+ */
+ol.ENABLE_VECTOR_TILE = true;
+
+
+/**
+ * @define {boolean} Enable the WebGL renderer. Default is `true`. Setting
+ * this to false at compile time in advanced mode removes all code
+ * supporting the WebGL renderer from the build.
+ */
+ol.ENABLE_WEBGL = true;
+
+
+/**
+ * @define {number} The size in pixels of the first atlas image. Default is
+ * `256`.
+ */
+ol.INITIAL_ATLAS_SIZE = 256;
+
+
+/**
+ * @define {number} The maximum size in pixels of atlas images. Default is
+ * `-1`, meaning it is not used (and `ol.WEBGL_MAX_TEXTURE_SIZE` is
+ * used instead).
+ */
+ol.MAX_ATLAS_SIZE = -1;
+
+
+/**
+ * @define {number} Maximum mouse wheel delta.
+ */
+ol.MOUSEWHEELZOOM_MAXDELTA = 1;
+
+
+/**
+ * @define {number} Maximum width and/or height extent ratio that determines
+ * when the overview map should be zoomed out.
+ */
+ol.OVERVIEWMAP_MAX_RATIO = 0.75;
+
+
+/**
+ * @define {number} Minimum width and/or height extent ratio that determines
+ * when the overview map should be zoomed in.
+ */
+ol.OVERVIEWMAP_MIN_RATIO = 0.1;
+
+
+/**
+ * @define {number} Maximum number of source tiles for raster reprojection of
+ * a single tile.
+ * If too many source tiles are determined to be loaded to create a single
+ * reprojected tile the browser can become unresponsive or even crash.
+ * This can happen if the developer defines projections improperly and/or
+ * with unlimited extents.
+ * If too many tiles are required, no tiles are loaded and
+ * `ol.Tile.State.ERROR` state is set. Default is `100`.
+ */
+ol.RASTER_REPROJECTION_MAX_SOURCE_TILES = 100;
+
+
+/**
+ * @define {number} Maximum number of subdivision steps during raster
+ * reprojection triangulation. Prevents high memory usage and large
+ * number of proj4 calls (for certain transformations and areas).
+ * At most `2*(2^this)` triangles are created for each triangulated
+ * extent (tile/image). Default is `10`.
+ */
+ol.RASTER_REPROJECTION_MAX_SUBDIVISION = 10;
+
+
+/**
+ * @define {number} Maximum allowed size of triangle relative to world width.
+ * When transforming corners of world extent between certain projections,
+ * the resulting triangulation seems to have zero error and no subdivision
+ * is performed.
+ * If the triangle width is more than this (relative to world width; 0-1),
+ * subdivison is forced (up to `ol.RASTER_REPROJECTION_MAX_SUBDIVISION`).
+ * Default is `0.25`.
+ */
+ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH = 0.25;
+
+
+/**
+ * @define {number} Tolerance for geometry simplification in device pixels.
+ */
+ol.SIMPLIFY_TOLERANCE = 0.5;
+
+
+/**
+ * @define {number} Texture cache high water mark.
+ */
+ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024;
+
+
+/**
+ * @define {string} OpenLayers version.
+ */
+ol.VERSION = '';
+
+
+/**
+ * The maximum supported WebGL texture size in pixels. If WebGL is not
+ * supported, the value is set to `undefined`.
+ * @const
+ * @type {number|undefined}
+ */
+ol.WEBGL_MAX_TEXTURE_SIZE; // value is set in `ol.has`
+
+
+/**
+ * List of supported WebGL extensions.
+ * @const
+ * @type {Array.<string>}
+ */
+ol.WEBGL_EXTENSIONS; // value is set in `ol.has`
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * Usage:
+ *
+ * function ParentClass(a, b) { }
+ * ParentClass.prototype.foo = function(a) { }
+ *
+ * function ChildClass(a, b, c) {
+ * // Call parent constructor
+ * ParentClass.call(this, a, b);
+ * }
+ * ol.inherits(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo(); // This works.
+ *
+ * @param {!Function} childCtor Child constructor.
+ * @param {!Function} parentCtor Parent constructor.
+ * @function
+ * @api
+ */
+ol.inherits = function(childCtor, parentCtor) {
+ childCtor.prototype = Object.create(parentCtor.prototype);
+ childCtor.prototype.constructor = childCtor;
+};
+
+
+/**
+ * A reusable function, used e.g. as a default for callbacks.
+ *
+ * @return {undefined} Nothing.
+ */
+ol.nullFunction = function() {};
+
+
+/**
+ * Gets a unique ID for an object. This mutates the object so that further calls
+ * with the same object as a parameter returns the same value. Unique IDs are generated
+ * as a strictly increasing sequence. Adapted from goog.getUid.
+ *
+ * @param {Object} obj The object to get the unique ID for.
+ * @return {number} The unique ID for the object.
+ */
+ol.getUid = function(obj) {
+ return obj.ol_uid ||
+ (obj.ol_uid = ++ol.uidCounter_);
+};
+
+
+/**
+ * Counter for getUid.
+ * @type {number}
+ * @private
+ */
+ol.uidCounter_ = 0;
+
+goog.provide('ol.AssertionError');
+
+goog.require('ol');
+
+/**
+ * Error object thrown when an assertion failed. This is an ECMA-262 Error,
+ * extended with a `code` property.
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error}
+ * @constructor
+ * @extends {Error}
+ * @implements {oli.AssertionError}
+ * @param {number} code Error code.
+ */
+ol.AssertionError = function(code) {
+
+ /**
+ * @type {string}
+ */
+ this.message = 'Assertion failed. See ' +
+ (ol.VERSION ? 'https://openlayers.org/en/' + ol.VERSION.split('-')[0] : '') +
+ '/doc/errors/#' + code + ' for details.';
+
+ /**
+ * Error code. The meaning of the code can be found on
+ * {@link https://openlayers.org/en/latest/doc/errors/} (replace `latest` with
+ * the version found in the OpenLayers script's header comment if a version
+ * other than the latest is used).
+ * @type {number}
+ * @api
+ */
+ this.code = code;
+
+ this.name = 'AssertionError';
+
+};
+ol.inherits(ol.AssertionError, Error);
+
+goog.provide('ol.asserts');
+
+goog.require('ol.AssertionError');
+
+
+/**
+ * @param {*} assertion Assertion we expected to be truthy.
+ * @param {number} errorCode Error code.
+ */
+ol.asserts.assert = function(assertion, errorCode) {
+ if (!assertion) {
+ throw new ol.AssertionError(errorCode);
+ }
+};
+
+goog.provide('ol.math');
+
+goog.require('ol');
+goog.require('ol.asserts');
+
+
+/**
+ * Takes a number and clamps it to within the provided bounds.
+ * @param {number} value The input number.
+ * @param {number} min The minimum value to return.
+ * @param {number} max The maximum value to return.
+ * @return {number} The input number if it is within bounds, or the nearest
+ * number within the bounds.
+ */
+ol.math.clamp = function(value, min, max) {
+ return Math.min(Math.max(value, min), max);
+};
+
+
+/**
+ * Return the hyperbolic cosine of a given number. The method will use the
+ * native `Math.cosh` function if it is available, otherwise the hyperbolic
+ * cosine will be calculated via the reference implementation of the Mozilla
+ * developer network.
+ *
+ * @param {number} x X.
+ * @return {number} Hyperbolic cosine of x.
+ */
+ol.math.cosh = (function() {
+ // Wrapped in a iife, to save the overhead of checking for the native
+ // implementation on every invocation.
+ var cosh;
+ if ('cosh' in Math) {
+ // The environment supports the native Math.cosh function, use it…
+ cosh = Math.cosh;
+ } else {
+ // … else, use the reference implementation of MDN:
+ cosh = function(x) {
+ var y = Math.exp(x);
+ return (y + 1 / y) / 2;
+ };
+ }
+ return cosh;
+}());
+
+
+/**
+ * @param {number} x X.
+ * @return {number} The smallest power of two greater than or equal to x.
+ */
+ol.math.roundUpToPowerOfTwo = function(x) {
+ ol.asserts.assert(0 < x, 29); // `x` must be greater than `0`
+ return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2));
+};
+
+
+/**
+ * Returns the square of the closest distance between the point (x, y) and the
+ * line segment (x1, y1) to (x2, y2).
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {number} x1 X1.
+ * @param {number} y1 Y1.
+ * @param {number} x2 X2.
+ * @param {number} y2 Y2.
+ * @return {number} Squared distance.
+ */
+ol.math.squaredSegmentDistance = function(x, y, x1, y1, x2, y2) {
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ if (dx !== 0 || dy !== 0) {
+ var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
+ if (t > 1) {
+ x1 = x2;
+ y1 = y2;
+ } else if (t > 0) {
+ x1 += dx * t;
+ y1 += dy * t;
+ }
+ }
+ return ol.math.squaredDistance(x, y, x1, y1);
+};
+
+
+/**
+ * Returns the square of the distance between the points (x1, y1) and (x2, y2).
+ * @param {number} x1 X1.
+ * @param {number} y1 Y1.
+ * @param {number} x2 X2.
+ * @param {number} y2 Y2.
+ * @return {number} Squared distance.
+ */
+ol.math.squaredDistance = function(x1, y1, x2, y2) {
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ return dx * dx + dy * dy;
+};
+
+
+/**
+ * Solves system of linear equations using Gaussian elimination method.
+ *
+ * @param {Array.<Array.<number>>} mat Augmented matrix (n x n + 1 column)
+ * in row-major order.
+ * @return {Array.<number>} The resulting vector.
+ */
+ol.math.solveLinearSystem = function(mat) {
+ var n = mat.length;
+
+ if (ol.DEBUG) {
+ for (var row = 0; row < n; row++) {
+ console.assert(mat[row].length == n + 1,
+ 'every row should have correct number of columns');
+ }
+ }
+
+ for (var i = 0; i < n; i++) {
+ // Find max in the i-th column (ignoring i - 1 first rows)
+ var maxRow = i;
+ var maxEl = Math.abs(mat[i][i]);
+ for (var r = i + 1; r < n; r++) {
+ var absValue = Math.abs(mat[r][i]);
+ if (absValue > maxEl) {
+ maxEl = absValue;
+ maxRow = r;
+ }
+ }
+
+ if (maxEl === 0) {
+ return null; // matrix is singular
+ }
+
+ // Swap max row with i-th (current) row
+ var tmp = mat[maxRow];
+ mat[maxRow] = mat[i];
+ mat[i] = tmp;
+
+ // Subtract the i-th row to make all the remaining rows 0 in the i-th column
+ for (var j = i + 1; j < n; j++) {
+ var coef = -mat[j][i] / mat[i][i];
+ for (var k = i; k < n + 1; k++) {
+ if (i == k) {
+ mat[j][k] = 0;
+ } else {
+ mat[j][k] += coef * mat[i][k];
+ }
+ }
+ }
+ }
+
+ // Solve Ax=b for upper triangular matrix A (mat)
+ var x = new Array(n);
+ for (var l = n - 1; l >= 0; l--) {
+ x[l] = mat[l][n] / mat[l][l];
+ for (var m = l - 1; m >= 0; m--) {
+ mat[m][n] -= mat[m][l] * x[l];
+ }
+ }
+ return x;
+};
+
+
+/**
+ * Converts radians to to degrees.
+ *
+ * @param {number} angleInRadians Angle in radians.
+ * @return {number} Angle in degrees.
+ */
+ol.math.toDegrees = function(angleInRadians) {
+ return angleInRadians * 180 / Math.PI;
+};
+
+
+/**
+ * Converts degrees to radians.
+ *
+ * @param {number} angleInDegrees Angle in degrees.
+ * @return {number} Angle in radians.
+ */
+ol.math.toRadians = function(angleInDegrees) {
+ return angleInDegrees * Math.PI / 180;
+};
+
+/**
+ * Returns the modulo of a / b, depending on the sign of b.
+ *
+ * @param {number} a Dividend.
+ * @param {number} b Divisor.
+ * @return {number} Modulo.
+ */
+ol.math.modulo = function(a, b) {
+ var r = a % b;
+ return r * b < 0 ? r + b : r;
+};
+
+/**
+ * Calculates the linearly interpolated value of x between a and b.
+ *
+ * @param {number} a Number
+ * @param {number} b Number
+ * @param {number} x Value to be interpolated.
+ * @return {number} Interpolated value.
+ */
+ol.math.lerp = function(a, b, x) {
+ return a + x * (b - a);
+};
+
+goog.provide('ol.CenterConstraint');
+
+goog.require('ol.math');
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @return {ol.CenterConstraintType} The constraint.
+ */
+ol.CenterConstraint.createExtent = function(extent) {
+ return (
+ /**
+ * @param {ol.Coordinate|undefined} center Center.
+ * @return {ol.Coordinate|undefined} Center.
+ */
+ function(center) {
+ if (center) {
+ return [
+ ol.math.clamp(center[0], extent[0], extent[2]),
+ ol.math.clamp(center[1], extent[1], extent[3])
+ ];
+ } else {
+ return undefined;
+ }
+ });
+};
+
+
+/**
+ * @param {ol.Coordinate|undefined} center Center.
+ * @return {ol.Coordinate|undefined} Center.
+ */
+ol.CenterConstraint.none = function(center) {
+ return center;
+};
+
+goog.provide('ol.Constraints');
+
+
+/**
+ * @constructor
+ * @param {ol.CenterConstraintType} centerConstraint Center constraint.
+ * @param {ol.ResolutionConstraintType} resolutionConstraint
+ * Resolution constraint.
+ * @param {ol.RotationConstraintType} rotationConstraint
+ * Rotation constraint.
+ */
+ol.Constraints = function(centerConstraint, resolutionConstraint, rotationConstraint) {
+
+ /**
+ * @type {ol.CenterConstraintType}
+ */
+ this.center = centerConstraint;
+
+ /**
+ * @type {ol.ResolutionConstraintType}
+ */
+ this.resolution = resolutionConstraint;
+
+ /**
+ * @type {ol.RotationConstraintType}
+ */
+ this.rotation = rotationConstraint;
+
+};
+
+goog.provide('ol.obj');
+
+
+/**
+ * Polyfill for Object.assign(). Assigns enumerable and own properties from
+ * one or more source objects to a target object.
+ *
+ * @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
+ * @param {!Object} target The target object.
+ * @param {...Object} var_sources The source object(s).
+ * @return {!Object} The modified target object.
+ */
+ol.obj.assign = (typeof Object.assign === 'function') ? Object.assign : function(target, var_sources) {
+ if (target === undefined || target === null) {
+ throw new TypeError('Cannot convert undefined or null to object');
+ }
+
+ var output = Object(target);
+ for (var i = 1, ii = arguments.length; i < ii; ++i) {
+ var source = arguments[i];
+ if (source !== undefined && source !== null) {
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ output[key] = source[key];
+ }
+ }
+ }
+ }
+ return output;
+};
+
+
+/**
+ * Removes all properties from an object.
+ * @param {Object} object The object to clear.
+ */
+ol.obj.clear = function(object) {
+ for (var property in object) {
+ delete object[property];
+ }
+};
+
+
+/**
+ * Get an array of property values from an object.
+ * @param {Object<K,V>} object The object from which to get the values.
+ * @return {!Array<V>} The property values.
+ * @template K,V
+ */
+ol.obj.getValues = function(object) {
+ var values = [];
+ for (var property in object) {
+ values.push(object[property]);
+ }
+ return values;
+};
+
+
+/**
+ * Determine if an object has any properties.
+ * @param {Object} object The object to check.
+ * @return {boolean} The object is empty.
+ */
+ol.obj.isEmpty = function(object) {
+ var property;
+ for (property in object) {
+ return false;
+ }
+ return !property;
+};
+
+goog.provide('ol.events');
+
+goog.require('ol.obj');
+
+
+/**
+ * @param {ol.EventsKey} listenerObj Listener object.
+ * @return {ol.EventsListenerFunctionType} Bound listener.
+ */
+ol.events.bindListener_ = function(listenerObj) {
+ var boundListener = function(evt) {
+ var listener = listenerObj.listener;
+ var bindTo = listenerObj.bindTo || listenerObj.target;
+ if (listenerObj.callOnce) {
+ ol.events.unlistenByKey(listenerObj);
+ }
+ return listener.call(bindTo, evt);
+ };
+ listenerObj.boundListener = boundListener;
+ return boundListener;
+};
+
+
+/**
+ * Finds the matching {@link ol.EventsKey} in the given listener
+ * array.
+ *
+ * @param {!Array<!ol.EventsKey>} listeners Array of listeners.
+ * @param {!Function} listener The listener function.
+ * @param {Object=} opt_this The `this` value inside the listener.
+ * @param {boolean=} opt_setDeleteIndex Set the deleteIndex on the matching
+ * listener, for {@link ol.events.unlistenByKey}.
+ * @return {ol.EventsKey|undefined} The matching listener object.
+ * @private
+ */
+ol.events.findListener_ = function(listeners, listener, opt_this,
+ opt_setDeleteIndex) {
+ var listenerObj;
+ for (var i = 0, ii = listeners.length; i < ii; ++i) {
+ listenerObj = listeners[i];
+ if (listenerObj.listener === listener &&
+ listenerObj.bindTo === opt_this) {
+ if (opt_setDeleteIndex) {
+ listenerObj.deleteIndex = i;
+ }
+ return listenerObj;
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @param {ol.EventTargetLike} target Target.
+ * @param {string} type Type.
+ * @return {Array.<ol.EventsKey>|undefined} Listeners.
+ */
+ol.events.getListeners = function(target, type) {
+ var listenerMap = target.ol_lm;
+ return listenerMap ? listenerMap[type] : undefined;
+};
+
+
+/**
+ * Get the lookup of listeners. If one does not exist on the target, it is
+ * created.
+ * @param {ol.EventTargetLike} target Target.
+ * @return {!Object.<string, Array.<ol.EventsKey>>} Map of
+ * listeners by event type.
+ * @private
+ */
+ol.events.getListenerMap_ = function(target) {
+ var listenerMap = target.ol_lm;
+ if (!listenerMap) {
+ listenerMap = target.ol_lm = {};
+ }
+ return listenerMap;
+};
+
+
+/**
+ * Clean up all listener objects of the given type. All properties on the
+ * listener objects will be removed, and if no listeners remain in the listener
+ * map, it will be removed from the target.
+ * @param {ol.EventTargetLike} target Target.
+ * @param {string} type Type.
+ * @private
+ */
+ol.events.removeListeners_ = function(target, type) {
+ var listeners = ol.events.getListeners(target, type);
+ if (listeners) {
+ for (var i = 0, ii = listeners.length; i < ii; ++i) {
+ target.removeEventListener(type, listeners[i].boundListener);
+ ol.obj.clear(listeners[i]);
+ }
+ listeners.length = 0;
+ var listenerMap = target.ol_lm;
+ if (listenerMap) {
+ delete listenerMap[type];
+ if (Object.keys(listenerMap).length === 0) {
+ delete target.ol_lm;
+ }
+ }
+ }
+};
+
+
+/**
+ * Registers an event listener on an event target. Inspired by
+ * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html}
+ *
+ * This function efficiently binds a `listener` to a `this` object, and returns
+ * a key for use with {@link ol.events.unlistenByKey}.
+ *
+ * @param {ol.EventTargetLike} target Event target.
+ * @param {string} type Event type.
+ * @param {ol.EventsListenerFunctionType} listener Listener.
+ * @param {Object=} opt_this Object referenced by the `this` keyword in the
+ * listener. Default is the `target`.
+ * @param {boolean=} opt_once If true, add the listener as one-off listener.
+ * @return {ol.EventsKey} Unique key for the listener.
+ */
+ol.events.listen = function(target, type, listener, opt_this, opt_once) {
+ var listenerMap = ol.events.getListenerMap_(target);
+ var listeners = listenerMap[type];
+ if (!listeners) {
+ listeners = listenerMap[type] = [];
+ }
+ var listenerObj = ol.events.findListener_(listeners, listener, opt_this,
+ false);
+ if (listenerObj) {
+ if (!opt_once) {
+ // Turn one-off listener into a permanent one.
+ listenerObj.callOnce = false;
+ }
+ } else {
+ listenerObj = /** @type {ol.EventsKey} */ ({
+ bindTo: opt_this,
+ callOnce: !!opt_once,
+ listener: listener,
+ target: target,
+ type: type
+ });
+ target.addEventListener(type, ol.events.bindListener_(listenerObj));
+ listeners.push(listenerObj);
+ }
+
+ return listenerObj;
+};
+
+
+/**
+ * Registers a one-off event listener on an event target. Inspired by
+ * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html}
+ *
+ * This function efficiently binds a `listener` as self-unregistering listener
+ * to a `this` object, and returns a key for use with
+ * {@link ol.events.unlistenByKey} in case the listener needs to be unregistered
+ * before it is called.
+ *
+ * When {@link ol.events.listen} is called with the same arguments after this
+ * function, the self-unregistering listener will be turned into a permanent
+ * listener.
+ *
+ * @param {ol.EventTargetLike} target Event target.
+ * @param {string} type Event type.
+ * @param {ol.EventsListenerFunctionType} listener Listener.
+ * @param {Object=} opt_this Object referenced by the `this` keyword in the
+ * listener. Default is the `target`.
+ * @return {ol.EventsKey} Key for unlistenByKey.
+ */
+ol.events.listenOnce = function(target, type, listener, opt_this) {
+ return ol.events.listen(target, type, listener, opt_this, true);
+};
+
+
+/**
+ * Unregisters an event listener on an event target. Inspired by
+ * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html}
+ *
+ * To return a listener, this function needs to be called with the exact same
+ * arguments that were used for a previous {@link ol.events.listen} call.
+ *
+ * @param {ol.EventTargetLike} target Event target.
+ * @param {string} type Event type.
+ * @param {ol.EventsListenerFunctionType} listener Listener.
+ * @param {Object=} opt_this Object referenced by the `this` keyword in the
+ * listener. Default is the `target`.
+ */
+ol.events.unlisten = function(target, type, listener, opt_this) {
+ var listeners = ol.events.getListeners(target, type);
+ if (listeners) {
+ var listenerObj = ol.events.findListener_(listeners, listener, opt_this,
+ true);
+ if (listenerObj) {
+ ol.events.unlistenByKey(listenerObj);
+ }
+ }
+};
+
+
+/**
+ * Unregisters event listeners on an event target. Inspired by
+ * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html}
+ *
+ * The argument passed to this function is the key returned from
+ * {@link ol.events.listen} or {@link ol.events.listenOnce}.
+ *
+ * @param {ol.EventsKey} key The key.
+ */
+ol.events.unlistenByKey = function(key) {
+ if (key && key.target) {
+ key.target.removeEventListener(key.type, key.boundListener);
+ var listeners = ol.events.getListeners(key.target, key.type);
+ if (listeners) {
+ var i = 'deleteIndex' in key ? key.deleteIndex : listeners.indexOf(key);
+ if (i !== -1) {
+ listeners.splice(i, 1);
+ }
+ if (listeners.length === 0) {
+ ol.events.removeListeners_(key.target, key.type);
+ }
+ }
+ ol.obj.clear(key);
+ }
+};
+
+
+/**
+ * Unregisters all event listeners on an event target. Inspired by
+ * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html}
+ *
+ * @param {ol.EventTargetLike} target Target.
+ */
+ol.events.unlistenAll = function(target) {
+ var listenerMap = ol.events.getListenerMap_(target);
+ for (var type in listenerMap) {
+ ol.events.removeListeners_(target, type);
+ }
+};
+
+goog.provide('ol.Disposable');
+
+goog.require('ol');
+
+/**
+ * Objects that need to clean up after themselves.
+ * @constructor
+ */
+ol.Disposable = function() {};
+
+/**
+ * The object has already been disposed.
+ * @type {boolean}
+ * @private
+ */
+ol.Disposable.prototype.disposed_ = false;
+
+/**
+ * Clean up.
+ */
+ol.Disposable.prototype.dispose = function() {
+ if (!this.disposed_) {
+ this.disposed_ = true;
+ this.disposeInternal();
+ }
+};
+
+/**
+ * Extension point for disposable objects.
+ * @protected
+ */
+ol.Disposable.prototype.disposeInternal = ol.nullFunction;
+
+goog.provide('ol.events.Event');
+
+
+/**
+ * @classdesc
+ * Stripped down implementation of the W3C DOM Level 2 Event interface.
+ * @see {@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface}
+ *
+ * This implementation only provides `type` and `target` properties, and
+ * `stopPropagation` and `preventDefault` methods. It is meant as base class
+ * for higher level events defined in the library, and works with
+ * {@link ol.events.EventTarget}.
+ *
+ * @constructor
+ * @implements {oli.events.Event}
+ * @param {string} type Type.
+ */
+ol.events.Event = function(type) {
+
+ /**
+ * @type {boolean}
+ */
+ this.propagationStopped;
+
+ /**
+ * The event type.
+ * @type {string}
+ * @api stable
+ */
+ this.type = type;
+
+ /**
+ * The event target.
+ * @type {Object}
+ * @api stable
+ */
+ this.target = null;
+
+};
+
+
+/**
+ * Stop event propagation.
+ * @function
+ * @api stable
+ */
+ol.events.Event.prototype.preventDefault =
+
+/**
+ * Stop event propagation.
+ * @function
+ * @api stable
+ */
+ol.events.Event.prototype.stopPropagation = function() {
+ this.propagationStopped = true;
+};
+
+
+/**
+ * @param {Event|ol.events.Event} evt Event
+ */
+ol.events.Event.stopPropagation = function(evt) {
+ evt.stopPropagation();
+};
+
+
+/**
+ * @param {Event|ol.events.Event} evt Event
+ */
+ol.events.Event.preventDefault = function(evt) {
+ evt.preventDefault();
+};
+
+goog.provide('ol.events.EventTarget');
+
+goog.require('ol');
+goog.require('ol.Disposable');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+
+
+/**
+ * @classdesc
+ * A simplified implementation of the W3C DOM Level 2 EventTarget interface.
+ * @see {@link https://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget}
+ *
+ * There are two important simplifications compared to the specification:
+ *
+ * 1. The handling of `useCapture` in `addEventListener` and
+ * `removeEventListener`. There is no real capture model.
+ * 2. The handling of `stopPropagation` and `preventDefault` on `dispatchEvent`.
+ * There is no event target hierarchy. When a listener calls
+ * `stopPropagation` or `preventDefault` on an event object, it means that no
+ * more listeners after this one will be called. Same as when the listener
+ * returns false.
+ *
+ * @constructor
+ * @extends {ol.Disposable}
+ */
+ol.events.EventTarget = function() {
+
+ ol.Disposable.call(this);
+
+ /**
+ * @private
+ * @type {!Object.<string, number>}
+ */
+ this.pendingRemovals_ = {};
+
+ /**
+ * @private
+ * @type {!Object.<string, number>}
+ */
+ this.dispatching_ = {};
+
+ /**
+ * @private
+ * @type {!Object.<string, Array.<ol.EventsListenerFunctionType>>}
+ */
+ this.listeners_ = {};
+
+};
+ol.inherits(ol.events.EventTarget, ol.Disposable);
+
+
+/**
+ * @param {string} type Type.
+ * @param {ol.EventsListenerFunctionType} listener Listener.
+ */
+ol.events.EventTarget.prototype.addEventListener = function(type, listener) {
+ var listeners = this.listeners_[type];
+ if (!listeners) {
+ listeners = this.listeners_[type] = [];
+ }
+ if (listeners.indexOf(listener) === -1) {
+ listeners.push(listener);
+ }
+};
+
+
+/**
+ * @param {{type: string,
+ * target: (EventTarget|ol.events.EventTarget|undefined)}|ol.events.Event|
+ * string} event Event or event type.
+ * @return {boolean|undefined} `false` if anyone called preventDefault on the
+ * event object or if any of the listeners returned false.
+ */
+ol.events.EventTarget.prototype.dispatchEvent = function(event) {
+ var evt = typeof event === 'string' ? new ol.events.Event(event) : event;
+ var type = evt.type;
+ evt.target = this;
+ var listeners = this.listeners_[type];
+ var propagate;
+ if (listeners) {
+ if (!(type in this.dispatching_)) {
+ this.dispatching_[type] = 0;
+ this.pendingRemovals_[type] = 0;
+ }
+ ++this.dispatching_[type];
+ for (var i = 0, ii = listeners.length; i < ii; ++i) {
+ if (listeners[i].call(this, evt) === false || evt.propagationStopped) {
+ propagate = false;
+ break;
+ }
+ }
+ --this.dispatching_[type];
+ if (this.dispatching_[type] === 0) {
+ var pendingRemovals = this.pendingRemovals_[type];
+ delete this.pendingRemovals_[type];
+ while (pendingRemovals--) {
+ this.removeEventListener(type, ol.nullFunction);
+ }
+ delete this.dispatching_[type];
+ }
+ return propagate;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.events.EventTarget.prototype.disposeInternal = function() {
+ ol.events.unlistenAll(this);
+};
+
+
+/**
+ * Get the listeners for a specified event type. Listeners are returned in the
+ * order that they will be called in.
+ *
+ * @param {string} type Type.
+ * @return {Array.<ol.EventsListenerFunctionType>} Listeners.
+ */
+ol.events.EventTarget.prototype.getListeners = function(type) {
+ return this.listeners_[type];
+};
+
+
+/**
+ * @param {string=} opt_type Type. If not provided,
+ * `true` will be returned if this EventTarget has any listeners.
+ * @return {boolean} Has listeners.
+ */
+ol.events.EventTarget.prototype.hasListener = function(opt_type) {
+ return opt_type ?
+ opt_type in this.listeners_ :
+ Object.keys(this.listeners_).length > 0;
+};
+
+
+/**
+ * @param {string} type Type.
+ * @param {ol.EventsListenerFunctionType} listener Listener.
+ */
+ol.events.EventTarget.prototype.removeEventListener = function(type, listener) {
+ var listeners = this.listeners_[type];
+ if (listeners) {
+ var index = listeners.indexOf(listener);
+ ol.DEBUG && console.assert(index != -1, 'listener not found');
+ if (type in this.pendingRemovals_) {
+ // make listener a no-op, and remove later in #dispatchEvent()
+ listeners[index] = ol.nullFunction;
+ ++this.pendingRemovals_[type];
+ } else {
+ listeners.splice(index, 1);
+ if (listeners.length === 0) {
+ delete this.listeners_[type];
+ }
+ }
+ }
+};
+
+goog.provide('ol.events.EventType');
+
+/**
+ * @enum {string}
+ * @const
+ */
+ol.events.EventType = {
+ /**
+ * Generic change event. Triggered when the revision counter is increased.
+ * @event ol.events.Event#change
+ * @api
+ */
+ CHANGE: 'change',
+
+ CLICK: 'click',
+ DBLCLICK: 'dblclick',
+ DRAGENTER: 'dragenter',
+ DRAGOVER: 'dragover',
+ DROP: 'drop',
+ ERROR: 'error',
+ KEYDOWN: 'keydown',
+ KEYPRESS: 'keypress',
+ LOAD: 'load',
+ MOUSEDOWN: 'mousedown',
+ MOUSEMOVE: 'mousemove',
+ MOUSEOUT: 'mouseout',
+ MOUSEUP: 'mouseup',
+ MOUSEWHEEL: 'mousewheel',
+ MSPOINTERDOWN: 'mspointerdown',
+ RESIZE: 'resize',
+ TOUCHSTART: 'touchstart',
+ TOUCHMOVE: 'touchmove',
+ TOUCHEND: 'touchend',
+ WHEEL: 'wheel'
+};
+
+goog.provide('ol.Observable');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.events.EventTarget');
+goog.require('ol.events.EventType');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * An event target providing convenient methods for listener registration
+ * and unregistration. A generic `change` event is always available through
+ * {@link ol.Observable#changed}.
+ *
+ * @constructor
+ * @extends {ol.events.EventTarget}
+ * @fires ol.events.Event
+ * @struct
+ * @api stable
+ */
+ol.Observable = function() {
+
+ ol.events.EventTarget.call(this);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.revision_ = 0;
+
+};
+ol.inherits(ol.Observable, ol.events.EventTarget);
+
+
+/**
+ * Removes an event listener using the key returned by `on()` or `once()`.
+ * @param {ol.EventsKey|Array.<ol.EventsKey>} key The key returned by `on()`
+ * or `once()` (or an array of keys).
+ * @api stable
+ */
+ol.Observable.unByKey = function(key) {
+ if (Array.isArray(key)) {
+ for (var i = 0, ii = key.length; i < ii; ++i) {
+ ol.events.unlistenByKey(key[i]);
+ }
+ } else {
+ ol.events.unlistenByKey(/** @type {ol.EventsKey} */ (key));
+ }
+};
+
+
+/**
+ * Increases the revision counter and dispatches a 'change' event.
+ * @api
+ */
+ol.Observable.prototype.changed = function() {
+ ++this.revision_;
+ this.dispatchEvent(ol.events.EventType.CHANGE);
+};
+
+
+/**
+ * Dispatches an event and calls all listeners listening for events
+ * of this type. The event parameter can either be a string or an
+ * Object with a `type` property.
+ *
+ * @param {{type: string,
+ * target: (EventTarget|ol.events.EventTarget|undefined)}|ol.events.Event|
+ * string} event Event object.
+ * @function
+ * @api
+ */
+ol.Observable.prototype.dispatchEvent;
+
+
+/**
+ * Get the version number for this object. Each time the object is modified,
+ * its version number will be incremented.
+ * @return {number} Revision.
+ * @api
+ */
+ol.Observable.prototype.getRevision = function() {
+ return this.revision_;
+};
+
+
+/**
+ * Listen for a certain type of event.
+ * @param {string|Array.<string>} type The event type or array of event types.
+ * @param {function(?): ?} listener The listener function.
+ * @param {Object=} opt_this The object to use as `this` in `listener`.
+ * @return {ol.EventsKey|Array.<ol.EventsKey>} Unique key for the listener. If
+ * called with an array of event types as the first argument, the return
+ * will be an array of keys.
+ * @api stable
+ */
+ol.Observable.prototype.on = function(type, listener, opt_this) {
+ if (Array.isArray(type)) {
+ var len = type.length;
+ var keys = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ keys[i] = ol.events.listen(this, type[i], listener, opt_this);
+ }
+ return keys;
+ } else {
+ return ol.events.listen(
+ this, /** @type {string} */ (type), listener, opt_this);
+ }
+};
+
+
+/**
+ * Listen once for a certain type of event.
+ * @param {string|Array.<string>} type The event type or array of event types.
+ * @param {function(?): ?} listener The listener function.
+ * @param {Object=} opt_this The object to use as `this` in `listener`.
+ * @return {ol.EventsKey|Array.<ol.EventsKey>} Unique key for the listener. If
+ * called with an array of event types as the first argument, the return
+ * will be an array of keys.
+ * @api stable
+ */
+ol.Observable.prototype.once = function(type, listener, opt_this) {
+ if (Array.isArray(type)) {
+ var len = type.length;
+ var keys = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ keys[i] = ol.events.listenOnce(this, type[i], listener, opt_this);
+ }
+ return keys;
+ } else {
+ return ol.events.listenOnce(
+ this, /** @type {string} */ (type), listener, opt_this);
+ }
+};
+
+
+/**
+ * Unlisten for a certain type of event.
+ * @param {string|Array.<string>} type The event type or array of event types.
+ * @param {function(?): ?} listener The listener function.
+ * @param {Object=} opt_this The object which was used as `this` by the
+ * `listener`.
+ * @api stable
+ */
+ol.Observable.prototype.un = function(type, listener, opt_this) {
+ if (Array.isArray(type)) {
+ for (var i = 0, ii = type.length; i < ii; ++i) {
+ ol.events.unlisten(this, type[i], listener, opt_this);
+ }
+ return;
+ } else {
+ ol.events.unlisten(this, /** @type {string} */ (type), listener, opt_this);
+ }
+};
+
+
+/**
+ * Removes an event listener using the key returned by `on()` or `once()`.
+ * Note that using the {@link ol.Observable.unByKey} static function is to
+ * be preferred.
+ * @param {ol.EventsKey|Array.<ol.EventsKey>} key The key returned by `on()`
+ * or `once()` (or an array of keys).
+ * @function
+ * @api stable
+ */
+ol.Observable.prototype.unByKey = ol.Observable.unByKey;
+
+goog.provide('ol.Object');
+
+goog.require('ol');
+goog.require('ol.Observable');
+goog.require('ol.events.Event');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Most non-trivial classes inherit from this.
+ *
+ * This extends {@link ol.Observable} with observable properties, where each
+ * property is observable as well as the object as a whole.
+ *
+ * Classes that inherit from this have pre-defined properties, to which you can
+ * add your owns. The pre-defined properties are listed in this documentation as
+ * 'Observable Properties', and have their own accessors; for example,
+ * {@link ol.Map} has a `target` property, accessed with `getTarget()` and
+ * changed with `setTarget()`. Not all properties are however settable. There
+ * are also general-purpose accessors `get()` and `set()`. For example,
+ * `get('target')` is equivalent to `getTarget()`.
+ *
+ * The `set` accessors trigger a change event, and you can monitor this by
+ * registering a listener. For example, {@link ol.View} has a `center`
+ * property, so `view.on('change:center', function(evt) {...});` would call the
+ * function whenever the value of the center property changes. Within the
+ * function, `evt.target` would be the view, so `evt.target.getCenter()` would
+ * return the new center.
+ *
+ * You can add your own observable properties with
+ * `object.set('prop', 'value')`, and retrieve that with `object.get('prop')`.
+ * You can listen for changes on that property value with
+ * `object.on('change:prop', listener)`. You can get a list of all
+ * properties with {@link ol.Object#getProperties object.getProperties()}.
+ *
+ * Note that the observable properties are separate from standard JS properties.
+ * You can, for example, give your map object a title with
+ * `map.title='New title'` and with `map.set('title', 'Another title')`. The
+ * first will be a `hasOwnProperty`; the second will appear in
+ * `getProperties()`. Only the second is observable.
+ *
+ * Properties can be deleted by using the unset method. E.g.
+ * object.unset('foo').
+ *
+ * @constructor
+ * @extends {ol.Observable}
+ * @param {Object.<string, *>=} opt_values An object with key-value pairs.
+ * @fires ol.Object.Event
+ * @api
+ */
+ol.Object = function(opt_values) {
+ ol.Observable.call(this);
+
+ // Call ol.getUid to ensure that the order of objects' ids is the same as
+ // the order in which they were created. This also helps to ensure that
+ // object properties are always added in the same order, which helps many
+ // JavaScript engines generate faster code.
+ ol.getUid(this);
+
+ /**
+ * @private
+ * @type {!Object.<string, *>}
+ */
+ this.values_ = {};
+
+ if (opt_values !== undefined) {
+ this.setProperties(opt_values);
+ }
+};
+ol.inherits(ol.Object, ol.Observable);
+
+
+/**
+ * @private
+ * @type {Object.<string, string>}
+ */
+ol.Object.changeEventTypeCache_ = {};
+
+
+/**
+ * @param {string} key Key name.
+ * @return {string} Change name.
+ */
+ol.Object.getChangeEventType = function(key) {
+ return ol.Object.changeEventTypeCache_.hasOwnProperty(key) ?
+ ol.Object.changeEventTypeCache_[key] :
+ (ol.Object.changeEventTypeCache_[key] = 'change:' + key);
+};
+
+
+/**
+ * Gets a value.
+ * @param {string} key Key name.
+ * @return {*} Value.
+ * @api stable
+ */
+ol.Object.prototype.get = function(key) {
+ var value;
+ if (this.values_.hasOwnProperty(key)) {
+ value = this.values_[key];
+ }
+ return value;
+};
+
+
+/**
+ * Get a list of object property names.
+ * @return {Array.<string>} List of property names.
+ * @api stable
+ */
+ol.Object.prototype.getKeys = function() {
+ return Object.keys(this.values_);
+};
+
+
+/**
+ * Get an object of all property names and values.
+ * @return {Object.<string, *>} Object.
+ * @api stable
+ */
+ol.Object.prototype.getProperties = function() {
+ return ol.obj.assign({}, this.values_);
+};
+
+
+/**
+ * @param {string} key Key name.
+ * @param {*} oldValue Old value.
+ */
+ol.Object.prototype.notify = function(key, oldValue) {
+ var eventType;
+ eventType = ol.Object.getChangeEventType(key);
+ this.dispatchEvent(new ol.Object.Event(eventType, key, oldValue));
+ eventType = ol.Object.EventType.PROPERTYCHANGE;
+ this.dispatchEvent(new ol.Object.Event(eventType, key, oldValue));
+};
+
+
+/**
+ * Sets a value.
+ * @param {string} key Key name.
+ * @param {*} value Value.
+ * @param {boolean=} opt_silent Update without triggering an event.
+ * @api stable
+ */
+ol.Object.prototype.set = function(key, value, opt_silent) {
+ if (opt_silent) {
+ this.values_[key] = value;
+ } else {
+ var oldValue = this.values_[key];
+ this.values_[key] = value;
+ if (oldValue !== value) {
+ this.notify(key, oldValue);
+ }
+ }
+};
+
+
+/**
+ * Sets a collection of key-value pairs. Note that this changes any existing
+ * properties and adds new ones (it does not remove any existing properties).
+ * @param {Object.<string, *>} values Values.
+ * @param {boolean=} opt_silent Update without triggering an event.
+ * @api stable
+ */
+ol.Object.prototype.setProperties = function(values, opt_silent) {
+ var key;
+ for (key in values) {
+ this.set(key, values[key], opt_silent);
+ }
+};
+
+
+/**
+ * Unsets a property.
+ * @param {string} key Key name.
+ * @param {boolean=} opt_silent Unset without triggering an event.
+ * @api stable
+ */
+ol.Object.prototype.unset = function(key, opt_silent) {
+ if (key in this.values_) {
+ var oldValue = this.values_[key];
+ delete this.values_[key];
+ if (!opt_silent) {
+ this.notify(key, oldValue);
+ }
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.Object.EventType = {
+ /**
+ * Triggered when a property is changed.
+ * @event ol.Object.Event#propertychange
+ * @api stable
+ */
+ PROPERTYCHANGE: 'propertychange'
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.Object} instances are instances of this type.
+ *
+ * @param {string} type The event type.
+ * @param {string} key The property name.
+ * @param {*} oldValue The old value for `key`.
+ * @extends {ol.events.Event}
+ * @implements {oli.Object.Event}
+ * @constructor
+ */
+ol.Object.Event = function(type, key, oldValue) {
+ ol.events.Event.call(this, type);
+
+ /**
+ * The name of the property whose value is changing.
+ * @type {string}
+ * @api stable
+ */
+ this.key = key;
+
+ /**
+ * The old value. To get the new value use `e.target.get(e.key)` where
+ * `e` is the event object.
+ * @type {*}
+ * @api stable
+ */
+ this.oldValue = oldValue;
+
+};
+ol.inherits(ol.Object.Event, ol.events.Event);
+
+goog.provide('ol.array');
+
+goog.require('ol');
+
+
+/**
+ * Performs a binary search on the provided sorted list and returns the index of the item if found. If it can't be found it'll return -1.
+ * https://github.com/darkskyapp/binary-search
+ *
+ * @param {Array.<*>} haystack Items to search through.
+ * @param {*} needle The item to look for.
+ * @param {Function=} opt_comparator Comparator function.
+ * @return {number} The index of the item if found, -1 if not.
+ */
+ol.array.binarySearch = function(haystack, needle, opt_comparator) {
+ var mid, cmp;
+ var comparator = opt_comparator || ol.array.numberSafeCompareFunction;
+ var low = 0;
+ var high = haystack.length;
+ var found = false;
+
+ while (low < high) {
+ /* Note that "(low + high) >>> 1" may overflow, and results in a typecast
+ * to double (which gives the wrong results). */
+ mid = low + (high - low >> 1);
+ cmp = +comparator(haystack[mid], needle);
+
+ if (cmp < 0.0) { /* Too low. */
+ low = mid + 1;
+
+ } else { /* Key found or too high */
+ high = mid;
+ found = !cmp;
+ }
+ }
+
+ /* Key not found. */
+ return found ? low : ~low;
+};
+
+
+/**
+ * Compare function for array sort that is safe for numbers.
+ * @param {*} a The first object to be compared.
+ * @param {*} b The second object to be compared.
+ * @return {number} A negative number, zero, or a positive number as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ol.array.numberSafeCompareFunction = function(a, b) {
+ return a > b ? 1 : a < b ? -1 : 0;
+};
+
+
+/**
+ * Whether the array contains the given object.
+ * @param {Array.<*>} arr The array to test for the presence of the element.
+ * @param {*} obj The object for which to test.
+ * @return {boolean} The object is in the array.
+ */
+ol.array.includes = function(arr, obj) {
+ return arr.indexOf(obj) >= 0;
+};
+
+
+/**
+ * @param {Array.<number>} arr Array.
+ * @param {number} target Target.
+ * @param {number} direction 0 means return the nearest, > 0
+ * means return the largest nearest, < 0 means return the
+ * smallest nearest.
+ * @return {number} Index.
+ */
+ol.array.linearFindNearest = function(arr, target, direction) {
+ var n = arr.length;
+ if (arr[0] <= target) {
+ return 0;
+ } else if (target <= arr[n - 1]) {
+ return n - 1;
+ } else {
+ var i;
+ if (direction > 0) {
+ for (i = 1; i < n; ++i) {
+ if (arr[i] < target) {
+ return i - 1;
+ }
+ }
+ } else if (direction < 0) {
+ for (i = 1; i < n; ++i) {
+ if (arr[i] <= target) {
+ return i;
+ }
+ }
+ } else {
+ for (i = 1; i < n; ++i) {
+ if (arr[i] == target) {
+ return i;
+ } else if (arr[i] < target) {
+ if (arr[i - 1] - target < target - arr[i]) {
+ return i - 1;
+ } else {
+ return i;
+ }
+ }
+ }
+ }
+ return n - 1;
+ }
+};
+
+
+/**
+ * @param {Array.<*>} arr Array.
+ * @param {number} begin Begin index.
+ * @param {number} end End index.
+ */
+ol.array.reverseSubArray = function(arr, begin, end) {
+ ol.DEBUG && console.assert(begin >= 0,
+ 'Array begin index should be equal to or greater than 0');
+ ol.DEBUG && console.assert(end < arr.length,
+ 'Array end index should be less than the array length');
+ while (begin < end) {
+ var tmp = arr[begin];
+ arr[begin] = arr[end];
+ arr[end] = tmp;
+ ++begin;
+ --end;
+ }
+};
+
+
+/**
+ * @param {Array.<*>} arr Array.
+ * @return {!Array.<?>} Flattened Array.
+ */
+ol.array.flatten = function(arr) {
+ var data = arr.reduce(function(flattened, value) {
+ if (Array.isArray(value)) {
+ return flattened.concat(ol.array.flatten(value));
+ } else {
+ return flattened.concat(value);
+ }
+ }, []);
+ return data;
+};
+
+
+/**
+ * @param {Array.<VALUE>} arr The array to modify.
+ * @param {Array.<VALUE>|VALUE} data The elements or arrays of elements
+ * to add to arr.
+ * @template VALUE
+ */
+ol.array.extend = function(arr, data) {
+ var i;
+ var extension = Array.isArray(data) ? data : [data];
+ var length = extension.length;
+ for (i = 0; i < length; i++) {
+ arr[arr.length] = extension[i];
+ }
+};
+
+
+/**
+ * @param {Array.<VALUE>} arr The array to modify.
+ * @param {VALUE} obj The element to remove.
+ * @template VALUE
+ * @return {boolean} If the element was removed.
+ */
+ol.array.remove = function(arr, obj) {
+ var i = arr.indexOf(obj);
+ var found = i > -1;
+ if (found) {
+ arr.splice(i, 1);
+ }
+ return found;
+};
+
+
+/**
+ * @param {Array.<VALUE>} arr The array to search in.
+ * @param {function(VALUE, number, ?) : boolean} func The function to compare.
+ * @template VALUE
+ * @return {VALUE} The element found.
+ */
+ol.array.find = function(arr, func) {
+ var length = arr.length >>> 0;
+ var value;
+
+ for (var i = 0; i < length; i++) {
+ value = arr[i];
+ if (func(value, i, arr)) {
+ return value;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @param {Array|Uint8ClampedArray} arr1 The first array to compare.
+ * @param {Array|Uint8ClampedArray} arr2 The second array to compare.
+ * @return {boolean} Whether the two arrays are equal.
+ */
+ol.array.equals = function(arr1, arr2) {
+ var len1 = arr1.length;
+ if (len1 !== arr2.length) {
+ return false;
+ }
+ for (var i = 0; i < len1; i++) {
+ if (arr1[i] !== arr2[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * @param {Array.<*>} arr The array to sort (modifies original).
+ * @param {Function} compareFnc Comparison function.
+ */
+ol.array.stableSort = function(arr, compareFnc) {
+ var length = arr.length;
+ var tmp = Array(arr.length);
+ var i;
+ for (i = 0; i < length; i++) {
+ tmp[i] = {index: i, value: arr[i]};
+ }
+ tmp.sort(function(a, b) {
+ return compareFnc(a.value, b.value) || a.index - b.index;
+ });
+ for (i = 0; i < arr.length; i++) {
+ arr[i] = tmp[i].value;
+ }
+};
+
+
+/**
+ * @param {Array.<*>} arr The array to search in.
+ * @param {Function} func Comparison function.
+ * @return {number} Return index.
+ */
+ol.array.findIndex = function(arr, func) {
+ var index;
+ var found = !arr.every(function(el, idx) {
+ index = idx;
+ return !func(el, idx, arr);
+ });
+ return found ? index : -1;
+};
+
+
+/**
+ * @param {Array.<*>} arr The array to test.
+ * @param {Function=} opt_func Comparison function.
+ * @param {boolean=} opt_strict Strictly sorted (default false).
+ * @return {boolean} Return index.
+ */
+ol.array.isSorted = function(arr, opt_func, opt_strict) {
+ var compare = opt_func || ol.array.numberSafeCompareFunction;
+ return arr.every(function(currentVal, index) {
+ if (index === 0) {
+ return true;
+ }
+ var res = compare(arr[index - 1], currentVal);
+ return !(res > 0 || opt_strict && res === 0);
+ });
+};
+
+goog.provide('ol.ResolutionConstraint');
+
+goog.require('ol.array');
+goog.require('ol.math');
+
+
+/**
+ * @param {Array.<number>} resolutions Resolutions.
+ * @return {ol.ResolutionConstraintType} Zoom function.
+ */
+ol.ResolutionConstraint.createSnapToResolutions = function(resolutions) {
+ return (
+ /**
+ * @param {number|undefined} resolution Resolution.
+ * @param {number} delta Delta.
+ * @param {number} direction Direction.
+ * @return {number|undefined} Resolution.
+ */
+ function(resolution, delta, direction) {
+ if (resolution !== undefined) {
+ var z =
+ ol.array.linearFindNearest(resolutions, resolution, direction);
+ z = ol.math.clamp(z + delta, 0, resolutions.length - 1);
+ var index = Math.floor(z);
+ if (z != index && index < resolutions.length - 1) {
+ var power = resolutions[index] / resolutions[index + 1];
+ return resolutions[index] / Math.pow(power, z - index);
+ } else {
+ return resolutions[index];
+ }
+ } else {
+ return undefined;
+ }
+ });
+};
+
+
+/**
+ * @param {number} power Power.
+ * @param {number} maxResolution Maximum resolution.
+ * @param {number=} opt_maxLevel Maximum level.
+ * @return {ol.ResolutionConstraintType} Zoom function.
+ */
+ol.ResolutionConstraint.createSnapToPower = function(power, maxResolution, opt_maxLevel) {
+ return (
+ /**
+ * @param {number|undefined} resolution Resolution.
+ * @param {number} delta Delta.
+ * @param {number} direction Direction.
+ * @return {number|undefined} Resolution.
+ */
+ function(resolution, delta, direction) {
+ if (resolution !== undefined) {
+ var offset = -direction / 2 + 0.5;
+ var oldLevel = Math.floor(
+ Math.log(maxResolution / resolution) / Math.log(power) + offset);
+ var newLevel = Math.max(oldLevel + delta, 0);
+ if (opt_maxLevel !== undefined) {
+ newLevel = Math.min(newLevel, opt_maxLevel);
+ }
+ return maxResolution / Math.pow(power, newLevel);
+ } else {
+ return undefined;
+ }
+ });
+};
+
+goog.provide('ol.RotationConstraint');
+
+goog.require('ol.math');
+
+
+/**
+ * @param {number|undefined} rotation Rotation.
+ * @param {number} delta Delta.
+ * @return {number|undefined} Rotation.
+ */
+ol.RotationConstraint.disable = function(rotation, delta) {
+ if (rotation !== undefined) {
+ return 0;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {number|undefined} rotation Rotation.
+ * @param {number} delta Delta.
+ * @return {number|undefined} Rotation.
+ */
+ol.RotationConstraint.none = function(rotation, delta) {
+ if (rotation !== undefined) {
+ return rotation + delta;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {number} n N.
+ * @return {ol.RotationConstraintType} Rotation constraint.
+ */
+ol.RotationConstraint.createSnapToN = function(n) {
+ var theta = 2 * Math.PI / n;
+ return (
+ /**
+ * @param {number|undefined} rotation Rotation.
+ * @param {number} delta Delta.
+ * @return {number|undefined} Rotation.
+ */
+ function(rotation, delta) {
+ if (rotation !== undefined) {
+ rotation = Math.floor((rotation + delta) / theta + 0.5) * theta;
+ return rotation;
+ } else {
+ return undefined;
+ }
+ });
+};
+
+
+/**
+ * @param {number=} opt_tolerance Tolerance.
+ * @return {ol.RotationConstraintType} Rotation constraint.
+ */
+ol.RotationConstraint.createSnapToZero = function(opt_tolerance) {
+ var tolerance = opt_tolerance || ol.math.toRadians(5);
+ return (
+ /**
+ * @param {number|undefined} rotation Rotation.
+ * @param {number} delta Delta.
+ * @return {number|undefined} Rotation.
+ */
+ function(rotation, delta) {
+ if (rotation !== undefined) {
+ if (Math.abs(rotation + delta) <= tolerance) {
+ return 0;
+ } else {
+ return rotation + delta;
+ }
+ } else {
+ return undefined;
+ }
+ });
+};
+
+goog.provide('ol.string');
+
+/**
+ * @param {number} number Number to be formatted
+ * @param {number} width The desired width
+ * @param {number=} opt_precision Precision of the output string (i.e. number of decimal places)
+ * @returns {string} Formatted string
+*/
+ol.string.padNumber = function(number, width, opt_precision) {
+ var numberString = opt_precision !== undefined ? number.toFixed(opt_precision) : '' + number;
+ var decimal = numberString.indexOf('.');
+ decimal = decimal === -1 ? numberString.length : decimal;
+ return decimal > width ? numberString : new Array(1 + width - decimal).join('0') + numberString;
+};
+
+/**
+ * Adapted from https://github.com/omichelsen/compare-versions/blob/master/index.js
+ * @param {string|number} v1 First version
+ * @param {string|number} v2 Second version
+ * @returns {number} Value
+ */
+ol.string.compareVersions = function(v1, v2) {
+ var s1 = ('' + v1).split('.');
+ var s2 = ('' + v2).split('.');
+
+ for (var i = 0; i < Math.max(s1.length, s2.length); i++) {
+ var n1 = parseInt(s1[i] || '0', 10);
+ var n2 = parseInt(s2[i] || '0', 10);
+
+ if (n1 > n2) {
+ return 1;
+ }
+ if (n2 > n1) {
+ return -1;
+ }
+ }
+
+ return 0;
+};
+
+goog.provide('ol.coordinate');
+
+goog.require('ol.math');
+goog.require('ol.string');
+
+
+/**
+ * Add `delta` to `coordinate`. `coordinate` is modified in place and returned
+ * by the function.
+ *
+ * Example:
+ *
+ * var coord = [7.85, 47.983333];
+ * ol.coordinate.add(coord, [-2, 4]);
+ * // coord is now [5.85, 51.983333]
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.Coordinate} delta Delta.
+ * @return {ol.Coordinate} The input coordinate adjusted by the given delta.
+ * @api stable
+ */
+ol.coordinate.add = function(coordinate, delta) {
+ coordinate[0] += delta[0];
+ coordinate[1] += delta[1];
+ return coordinate;
+};
+
+
+/**
+ * Calculates the point closest to the passed coordinate on the passed segment.
+ * This is the foot of the perpendicular of the coordinate to the segment when
+ * the foot is on the segment, or the closest segment coordinate when the foot
+ * is outside the segment.
+ *
+ * @param {ol.Coordinate} coordinate The coordinate.
+ * @param {Array.<ol.Coordinate>} segment The two coordinates of the segment.
+ * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to
+ * the segment.
+ */
+ol.coordinate.closestOnSegment = function(coordinate, segment) {
+ var x0 = coordinate[0];
+ var y0 = coordinate[1];
+ var start = segment[0];
+ var end = segment[1];
+ var x1 = start[0];
+ var y1 = start[1];
+ var x2 = end[0];
+ var y2 = end[1];
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ var along = (dx === 0 && dy === 0) ? 0 :
+ ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0);
+ var x, y;
+ if (along <= 0) {
+ x = x1;
+ y = y1;
+ } else if (along >= 1) {
+ x = x2;
+ y = y2;
+ } else {
+ x = x1 + along * dx;
+ y = y1 + along * dy;
+ }
+ return [x, y];
+};
+
+
+/**
+ * Returns a {@link ol.CoordinateFormatType} function that can be used to format
+ * a {ol.Coordinate} to a string.
+ *
+ * Example without specifying the fractional digits:
+ *
+ * var coord = [7.85, 47.983333];
+ * var stringifyFunc = ol.coordinate.createStringXY();
+ * var out = stringifyFunc(coord);
+ * // out is now '8, 48'
+ *
+ * Example with explicitly specifying 2 fractional digits:
+ *
+ * var coord = [7.85, 47.983333];
+ * var stringifyFunc = ol.coordinate.createStringXY(2);
+ * var out = stringifyFunc(coord);
+ * // out is now '7.85, 47.98'
+ *
+ * @param {number=} opt_fractionDigits The number of digits to include
+ * after the decimal point. Default is `0`.
+ * @return {ol.CoordinateFormatType} Coordinate format.
+ * @api stable
+ */
+ol.coordinate.createStringXY = function(opt_fractionDigits) {
+ return (
+ /**
+ * @param {ol.Coordinate|undefined} coordinate Coordinate.
+ * @return {string} String XY.
+ */
+ function(coordinate) {
+ return ol.coordinate.toStringXY(coordinate, opt_fractionDigits);
+ });
+};
+
+
+/**
+ * @private
+ * @param {number} degrees Degrees.
+ * @param {string} hemispheres Hemispheres.
+ * @param {number=} opt_fractionDigits The number of digits to include
+ * after the decimal point. Default is `0`.
+ * @return {string} String.
+ */
+ol.coordinate.degreesToStringHDMS_ = function(degrees, hemispheres, opt_fractionDigits) {
+ var normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180;
+ var x = Math.abs(3600 * normalizedDegrees);
+ var dflPrecision = opt_fractionDigits || 0;
+ return Math.floor(x / 3600) + '\u00b0 ' +
+ ol.string.padNumber(Math.floor((x / 60) % 60), 2) + '\u2032 ' +
+ ol.string.padNumber((x % 60), 2, dflPrecision) + '\u2033 ' +
+ hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0);
+};
+
+
+/**
+ * Transforms the given {@link ol.Coordinate} to a string using the given string
+ * template. The strings `{x}` and `{y}` in the template will be replaced with
+ * the first and second coordinate values respectively.
+ *
+ * Example without specifying the fractional digits:
+ *
+ * var coord = [7.85, 47.983333];
+ * var template = 'Coordinate is ({x}|{y}).';
+ * var out = ol.coordinate.format(coord, template);
+ * // out is now 'Coordinate is (8|48).'
+ *
+ * Example explicitly specifying the fractional digits:
+ *
+ * var coord = [7.85, 47.983333];
+ * var template = 'Coordinate is ({x}|{y}).';
+ * var out = ol.coordinate.format(coord, template, 2);
+ * // out is now 'Coordinate is (7.85|47.98).'
+ *
+ * @param {ol.Coordinate|undefined} coordinate Coordinate.
+ * @param {string} template A template string with `{x}` and `{y}` placeholders
+ * that will be replaced by first and second coordinate values.
+ * @param {number=} opt_fractionDigits The number of digits to include
+ * after the decimal point. Default is `0`.
+ * @return {string} Formatted coordinate.
+ * @api stable
+ */
+ol.coordinate.format = function(coordinate, template, opt_fractionDigits) {
+ if (coordinate) {
+ return template
+ .replace('{x}', coordinate[0].toFixed(opt_fractionDigits))
+ .replace('{y}', coordinate[1].toFixed(opt_fractionDigits));
+ } else {
+ return '';
+ }
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate1 First coordinate.
+ * @param {ol.Coordinate} coordinate2 Second coordinate.
+ * @return {boolean} Whether the passed coordinates are equal.
+ */
+ol.coordinate.equals = function(coordinate1, coordinate2) {
+ var equals = true;
+ for (var i = coordinate1.length - 1; i >= 0; --i) {
+ if (coordinate1[i] != coordinate2[i]) {
+ equals = false;
+ break;
+ }
+ }
+ return equals;
+};
+
+
+/**
+ * Rotate `coordinate` by `angle`. `coordinate` is modified in place and
+ * returned by the function.
+ *
+ * Example:
+ *
+ * var coord = [7.85, 47.983333];
+ * var rotateRadians = Math.PI / 2; // 90 degrees
+ * ol.coordinate.rotate(coord, rotateRadians);
+ * // coord is now [-47.983333, 7.85]
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} angle Angle in radian.
+ * @return {ol.Coordinate} Coordinate.
+ * @api stable
+ */
+ol.coordinate.rotate = function(coordinate, angle) {
+ var cosAngle = Math.cos(angle);
+ var sinAngle = Math.sin(angle);
+ var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
+ var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
+ coordinate[0] = x;
+ coordinate[1] = y;
+ return coordinate;
+};
+
+
+/**
+ * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned
+ * by the function.
+ *
+ * Example:
+ *
+ * var coord = [7.85, 47.983333];
+ * var scale = 1.2;
+ * ol.coordinate.scale(coord, scale);
+ * // coord is now [9.42, 57.5799996]
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} scale Scale factor.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ol.coordinate.scale = function(coordinate, scale) {
+ coordinate[0] *= scale;
+ coordinate[1] *= scale;
+ return coordinate;
+};
+
+
+/**
+ * Subtract `delta` to `coordinate`. `coordinate` is modified in place and
+ * returned by the function.
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.Coordinate} delta Delta.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ol.coordinate.sub = function(coordinate, delta) {
+ coordinate[0] -= delta[0];
+ coordinate[1] -= delta[1];
+ return coordinate;
+};
+
+
+/**
+ * @param {ol.Coordinate} coord1 First coordinate.
+ * @param {ol.Coordinate} coord2 Second coordinate.
+ * @return {number} Squared distance between coord1 and coord2.
+ */
+ol.coordinate.squaredDistance = function(coord1, coord2) {
+ var dx = coord1[0] - coord2[0];
+ var dy = coord1[1] - coord2[1];
+ return dx * dx + dy * dy;
+};
+
+
+/**
+ * Calculate the squared distance from a coordinate to a line segment.
+ *
+ * @param {ol.Coordinate} coordinate Coordinate of the point.
+ * @param {Array.<ol.Coordinate>} segment Line segment (2 coordinates).
+ * @return {number} Squared distance from the point to the line segment.
+ */
+ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) {
+ return ol.coordinate.squaredDistance(coordinate,
+ ol.coordinate.closestOnSegment(coordinate, segment));
+};
+
+
+/**
+ * Format a geographic coordinate with the hemisphere, degrees, minutes, and
+ * seconds.
+ *
+ * Example without specifying fractional digits:
+ *
+ * var coord = [7.85, 47.983333];
+ * var out = ol.coordinate.toStringHDMS(coord);
+ * // out is now '47° 58′ 60″ N 7° 50′ 60″ E'
+ *
+ * Example explicitly specifying 1 fractional digit:
+ *
+ * var coord = [7.85, 47.983333];
+ * var out = ol.coordinate.toStringHDMS(coord, 1);
+ * // out is now '47° 58′ 60.0″ N 7° 50′ 60.0″ E'
+ *
+ * @param {ol.Coordinate|undefined} coordinate Coordinate.
+ * @param {number=} opt_fractionDigits The number of digits to include
+ * after the decimal point. Default is `0`.
+ * @return {string} Hemisphere, degrees, minutes and seconds.
+ * @api stable
+ */
+ol.coordinate.toStringHDMS = function(coordinate, opt_fractionDigits) {
+ if (coordinate) {
+ return ol.coordinate.degreesToStringHDMS_(coordinate[1], 'NS', opt_fractionDigits) + ' ' +
+ ol.coordinate.degreesToStringHDMS_(coordinate[0], 'EW', opt_fractionDigits);
+ } else {
+ return '';
+ }
+};
+
+
+/**
+ * Format a coordinate as a comma delimited string.
+ *
+ * Example without specifying fractional digits:
+ *
+ * var coord = [7.85, 47.983333];
+ * var out = ol.coordinate.toStringXY(coord);
+ * // out is now '8, 48'
+ *
+ * Example explicitly specifying 1 fractional digit:
+ *
+ * var coord = [7.85, 47.983333];
+ * var out = ol.coordinate.toStringXY(coord, 1);
+ * // out is now '7.8, 48.0'
+ *
+ * @param {ol.Coordinate|undefined} coordinate Coordinate.
+ * @param {number=} opt_fractionDigits The number of digits to include
+ * after the decimal point. Default is `0`.
+ * @return {string} XY.
+ * @api stable
+ */
+ol.coordinate.toStringXY = function(coordinate, opt_fractionDigits) {
+ return ol.coordinate.format(coordinate, '{x}, {y}', opt_fractionDigits);
+};
+
+goog.provide('ol.easing');
+
+
+/**
+ * Start slow and speed up.
+ * @param {number} t Input between 0 and 1.
+ * @return {number} Output between 0 and 1.
+ * @api
+ */
+ol.easing.easeIn = function(t) {
+ return Math.pow(t, 3);
+};
+
+
+/**
+ * Start fast and slow down.
+ * @param {number} t Input between 0 and 1.
+ * @return {number} Output between 0 and 1.
+ * @api
+ */
+ol.easing.easeOut = function(t) {
+ return 1 - ol.easing.easeIn(1 - t);
+};
+
+
+/**
+ * Start slow, speed up, and then slow down again.
+ * @param {number} t Input between 0 and 1.
+ * @return {number} Output between 0 and 1.
+ * @api
+ */
+ol.easing.inAndOut = function(t) {
+ return 3 * t * t - 2 * t * t * t;
+};
+
+
+/**
+ * Maintain a constant speed over time.
+ * @param {number} t Input between 0 and 1.
+ * @return {number} Output between 0 and 1.
+ * @api
+ */
+ol.easing.linear = function(t) {
+ return t;
+};
+
+
+/**
+ * Start slow, speed up, and at the very end slow down again. This has the
+ * same general behavior as {@link ol.easing.inAndOut}, but the final slowdown
+ * is delayed.
+ * @param {number} t Input between 0 and 1.
+ * @return {number} Output between 0 and 1.
+ * @api
+ */
+ol.easing.upAndDown = function(t) {
+ if (t < 0.5) {
+ return ol.easing.inAndOut(2 * t);
+ } else {
+ return 1 - ol.easing.inAndOut(2 * (t - 0.5));
+ }
+};
+
+goog.provide('ol.extent.Corner');
+
+/**
+ * Extent corner.
+ * @enum {string}
+ */
+ol.extent.Corner = {
+ BOTTOM_LEFT: 'bottom-left',
+ BOTTOM_RIGHT: 'bottom-right',
+ TOP_LEFT: 'top-left',
+ TOP_RIGHT: 'top-right'
+};
+
+goog.provide('ol.extent.Relationship');
+
+
+/**
+ * Relationship to an extent.
+ * @enum {number}
+ */
+ol.extent.Relationship = {
+ UNKNOWN: 0,
+ INTERSECTING: 1,
+ ABOVE: 2,
+ RIGHT: 4,
+ BELOW: 8,
+ LEFT: 16
+};
+
+goog.provide('ol.extent');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.extent.Corner');
+goog.require('ol.extent.Relationship');
+
+
+/**
+ * Build an extent that includes all given coordinates.
+ *
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @return {ol.Extent} Bounding extent.
+ * @api stable
+ */
+ol.extent.boundingExtent = function(coordinates) {
+ var extent = ol.extent.createEmpty();
+ for (var i = 0, ii = coordinates.length; i < ii; ++i) {
+ ol.extent.extendCoordinate(extent, coordinates[i]);
+ }
+ return extent;
+};
+
+
+/**
+ * @param {Array.<number>} xs Xs.
+ * @param {Array.<number>} ys Ys.
+ * @param {ol.Extent=} opt_extent Destination extent.
+ * @private
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.boundingExtentXYs_ = function(xs, ys, opt_extent) {
+ ol.DEBUG && console.assert(xs.length > 0, 'xs length should be larger than 0');
+ ol.DEBUG && console.assert(ys.length > 0, 'ys length should be larger than 0');
+ var minX = Math.min.apply(null, xs);
+ var minY = Math.min.apply(null, ys);
+ var maxX = Math.max.apply(null, xs);
+ var maxY = Math.max.apply(null, ys);
+ return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
+};
+
+
+/**
+ * Return extent increased by the provided value.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} value The amount by which the extent should be buffered.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ * @api stable
+ */
+ol.extent.buffer = function(extent, value, opt_extent) {
+ if (opt_extent) {
+ opt_extent[0] = extent[0] - value;
+ opt_extent[1] = extent[1] - value;
+ opt_extent[2] = extent[2] + value;
+ opt_extent[3] = extent[3] + value;
+ return opt_extent;
+ } else {
+ return [
+ extent[0] - value,
+ extent[1] - value,
+ extent[2] + value,
+ extent[3] + value
+ ];
+ }
+};
+
+
+/**
+ * Creates a clone of an extent.
+ *
+ * @param {ol.Extent} extent Extent to clone.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} The clone.
+ */
+ol.extent.clone = function(extent, opt_extent) {
+ if (opt_extent) {
+ opt_extent[0] = extent[0];
+ opt_extent[1] = extent[1];
+ opt_extent[2] = extent[2];
+ opt_extent[3] = extent[3];
+ return opt_extent;
+ } else {
+ return extent.slice();
+ }
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {number} Closest squared distance.
+ */
+ol.extent.closestSquaredDistanceXY = function(extent, x, y) {
+ var dx, dy;
+ if (x < extent[0]) {
+ dx = extent[0] - x;
+ } else if (extent[2] < x) {
+ dx = x - extent[2];
+ } else {
+ dx = 0;
+ }
+ if (y < extent[1]) {
+ dy = extent[1] - y;
+ } else if (extent[3] < y) {
+ dy = y - extent[3];
+ } else {
+ dy = 0;
+ }
+ return dx * dx + dy * dy;
+};
+
+
+/**
+ * Check if the passed coordinate is contained or on the edge of the extent.
+ *
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {boolean} The coordinate is contained in the extent.
+ * @api stable
+ */
+ol.extent.containsCoordinate = function(extent, coordinate) {
+ return ol.extent.containsXY(extent, coordinate[0], coordinate[1]);
+};
+
+
+/**
+ * Check if one extent contains another.
+ *
+ * An extent is deemed contained if it lies completely within the other extent,
+ * including if they share one or more edges.
+ *
+ * @param {ol.Extent} extent1 Extent 1.
+ * @param {ol.Extent} extent2 Extent 2.
+ * @return {boolean} The second extent is contained by or on the edge of the
+ * first.
+ * @api stable
+ */
+ol.extent.containsExtent = function(extent1, extent2) {
+ return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] &&
+ extent1[1] <= extent2[1] && extent2[3] <= extent1[3];
+};
+
+
+/**
+ * Check if the passed coordinate is contained or on the edge of the extent.
+ *
+ * @param {ol.Extent} extent Extent.
+ * @param {number} x X coordinate.
+ * @param {number} y Y coordinate.
+ * @return {boolean} The x, y values are contained in the extent.
+ * @api stable
+ */
+ol.extent.containsXY = function(extent, x, y) {
+ return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3];
+};
+
+
+/**
+ * Get the relationship between a coordinate and extent.
+ * @param {ol.Extent} extent The extent.
+ * @param {ol.Coordinate} coordinate The coordinate.
+ * @return {number} The relationship (bitwise compare with
+ * ol.extent.Relationship).
+ */
+ol.extent.coordinateRelationship = function(extent, coordinate) {
+ var minX = extent[0];
+ var minY = extent[1];
+ var maxX = extent[2];
+ var maxY = extent[3];
+ var x = coordinate[0];
+ var y = coordinate[1];
+ var relationship = ol.extent.Relationship.UNKNOWN;
+ if (x < minX) {
+ relationship = relationship | ol.extent.Relationship.LEFT;
+ } else if (x > maxX) {
+ relationship = relationship | ol.extent.Relationship.RIGHT;
+ }
+ if (y < minY) {
+ relationship = relationship | ol.extent.Relationship.BELOW;
+ } else if (y > maxY) {
+ relationship = relationship | ol.extent.Relationship.ABOVE;
+ }
+ if (relationship === ol.extent.Relationship.UNKNOWN) {
+ relationship = ol.extent.Relationship.INTERSECTING;
+ }
+ return relationship;
+};
+
+
+/**
+ * Create an empty extent.
+ * @return {ol.Extent} Empty extent.
+ * @api stable
+ */
+ol.extent.createEmpty = function() {
+ return [Infinity, Infinity, -Infinity, -Infinity];
+};
+
+
+/**
+ * Create a new extent or update the provided extent.
+ * @param {number} minX Minimum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxX Maximum X.
+ * @param {number} maxY Maximum Y.
+ * @param {ol.Extent=} opt_extent Destination extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.createOrUpdate = function(minX, minY, maxX, maxY, opt_extent) {
+ if (opt_extent) {
+ opt_extent[0] = minX;
+ opt_extent[1] = minY;
+ opt_extent[2] = maxX;
+ opt_extent[3] = maxY;
+ return opt_extent;
+ } else {
+ return [minX, minY, maxX, maxY];
+ }
+};
+
+
+/**
+ * Create a new empty extent or make the provided one empty.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.createOrUpdateEmpty = function(opt_extent) {
+ return ol.extent.createOrUpdate(
+ Infinity, Infinity, -Infinity, -Infinity, opt_extent);
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.createOrUpdateFromCoordinate = function(coordinate, opt_extent) {
+ var x = coordinate[0];
+ var y = coordinate[1];
+ return ol.extent.createOrUpdate(x, y, x, y, opt_extent);
+};
+
+
+/**
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.createOrUpdateFromCoordinates = function(coordinates, opt_extent) {
+ var extent = ol.extent.createOrUpdateEmpty(opt_extent);
+ return ol.extent.extendCoordinates(extent, coordinates);
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.createOrUpdateFromFlatCoordinates = function(flatCoordinates, offset, end, stride, opt_extent) {
+ var extent = ol.extent.createOrUpdateEmpty(opt_extent);
+ return ol.extent.extendFlatCoordinates(
+ extent, flatCoordinates, offset, end, stride);
+};
+
+
+/**
+ * @param {Array.<Array.<ol.Coordinate>>} rings Rings.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.createOrUpdateFromRings = function(rings, opt_extent) {
+ var extent = ol.extent.createOrUpdateEmpty(opt_extent);
+ return ol.extent.extendRings(extent, rings);
+};
+
+
+/**
+ * Determine if two extents are equivalent.
+ * @param {ol.Extent} extent1 Extent 1.
+ * @param {ol.Extent} extent2 Extent 2.
+ * @return {boolean} The two extents are equivalent.
+ * @api stable
+ */
+ol.extent.equals = function(extent1, extent2) {
+ return extent1[0] == extent2[0] && extent1[2] == extent2[2] &&
+ extent1[1] == extent2[1] && extent1[3] == extent2[3];
+};
+
+
+/**
+ * Modify an extent to include another extent.
+ * @param {ol.Extent} extent1 The extent to be modified.
+ * @param {ol.Extent} extent2 The extent that will be included in the first.
+ * @return {ol.Extent} A reference to the first (extended) extent.
+ * @api stable
+ */
+ol.extent.extend = function(extent1, extent2) {
+ if (extent2[0] < extent1[0]) {
+ extent1[0] = extent2[0];
+ }
+ if (extent2[2] > extent1[2]) {
+ extent1[2] = extent2[2];
+ }
+ if (extent2[1] < extent1[1]) {
+ extent1[1] = extent2[1];
+ }
+ if (extent2[3] > extent1[3]) {
+ extent1[3] = extent2[3];
+ }
+ return extent1;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ */
+ol.extent.extendCoordinate = function(extent, coordinate) {
+ if (coordinate[0] < extent[0]) {
+ extent[0] = coordinate[0];
+ }
+ if (coordinate[0] > extent[2]) {
+ extent[2] = coordinate[0];
+ }
+ if (coordinate[1] < extent[1]) {
+ extent[1] = coordinate[1];
+ }
+ if (coordinate[1] > extent[3]) {
+ extent[3] = coordinate[1];
+ }
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.extendCoordinates = function(extent, coordinates) {
+ var i, ii;
+ for (i = 0, ii = coordinates.length; i < ii; ++i) {
+ ol.extent.extendCoordinate(extent, coordinates[i]);
+ }
+ return extent;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.extendFlatCoordinates = function(extent, flatCoordinates, offset, end, stride) {
+ for (; offset < end; offset += stride) {
+ ol.extent.extendXY(
+ extent, flatCoordinates[offset], flatCoordinates[offset + 1]);
+ }
+ return extent;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {Array.<Array.<ol.Coordinate>>} rings Rings.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.extendRings = function(extent, rings) {
+ var i, ii;
+ for (i = 0, ii = rings.length; i < ii; ++i) {
+ ol.extent.extendCoordinates(extent, rings[i]);
+ }
+ return extent;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} x X.
+ * @param {number} y Y.
+ */
+ol.extent.extendXY = function(extent, x, y) {
+ extent[0] = Math.min(extent[0], x);
+ extent[1] = Math.min(extent[1], y);
+ extent[2] = Math.max(extent[2], x);
+ extent[3] = Math.max(extent[3], y);
+};
+
+
+/**
+ * This function calls `callback` for each corner of the extent. If the
+ * callback returns a truthy value the function returns that value
+ * immediately. Otherwise the function returns `false`.
+ * @param {ol.Extent} extent Extent.
+ * @param {function(this:T, ol.Coordinate): S} callback Callback.
+ * @param {T=} opt_this Value to use as `this` when executing `callback`.
+ * @return {S|boolean} Value.
+ * @template S, T
+ */
+ol.extent.forEachCorner = function(extent, callback, opt_this) {
+ var val;
+ val = callback.call(opt_this, ol.extent.getBottomLeft(extent));
+ if (val) {
+ return val;
+ }
+ val = callback.call(opt_this, ol.extent.getBottomRight(extent));
+ if (val) {
+ return val;
+ }
+ val = callback.call(opt_this, ol.extent.getTopRight(extent));
+ if (val) {
+ return val;
+ }
+ val = callback.call(opt_this, ol.extent.getTopLeft(extent));
+ if (val) {
+ return val;
+ }
+ return false;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @return {number} Area.
+ */
+ol.extent.getArea = function(extent) {
+ var area = 0;
+ if (!ol.extent.isEmpty(extent)) {
+ area = ol.extent.getWidth(extent) * ol.extent.getHeight(extent);
+ }
+ return area;
+};
+
+
+/**
+ * Get the bottom left coordinate of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {ol.Coordinate} Bottom left coordinate.
+ * @api stable
+ */
+ol.extent.getBottomLeft = function(extent) {
+ return [extent[0], extent[1]];
+};
+
+
+/**
+ * Get the bottom right coordinate of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {ol.Coordinate} Bottom right coordinate.
+ * @api stable
+ */
+ol.extent.getBottomRight = function(extent) {
+ return [extent[2], extent[1]];
+};
+
+
+/**
+ * Get the center coordinate of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {ol.Coordinate} Center.
+ * @api stable
+ */
+ol.extent.getCenter = function(extent) {
+ return [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2];
+};
+
+
+/**
+ * Get a corner coordinate of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.extent.Corner} corner Corner.
+ * @return {ol.Coordinate} Corner coordinate.
+ */
+ol.extent.getCorner = function(extent, corner) {
+ var coordinate;
+ if (corner === ol.extent.Corner.BOTTOM_LEFT) {
+ coordinate = ol.extent.getBottomLeft(extent);
+ } else if (corner === ol.extent.Corner.BOTTOM_RIGHT) {
+ coordinate = ol.extent.getBottomRight(extent);
+ } else if (corner === ol.extent.Corner.TOP_LEFT) {
+ coordinate = ol.extent.getTopLeft(extent);
+ } else if (corner === ol.extent.Corner.TOP_RIGHT) {
+ coordinate = ol.extent.getTopRight(extent);
+ } else {
+ ol.asserts.assert(false, 13); // Invalid corner
+ }
+ return /** @type {!ol.Coordinate} */ (coordinate);
+};
+
+
+/**
+ * @param {ol.Extent} extent1 Extent 1.
+ * @param {ol.Extent} extent2 Extent 2.
+ * @return {number} Enlarged area.
+ */
+ol.extent.getEnlargedArea = function(extent1, extent2) {
+ var minX = Math.min(extent1[0], extent2[0]);
+ var minY = Math.min(extent1[1], extent2[1]);
+ var maxX = Math.max(extent1[2], extent2[2]);
+ var maxY = Math.max(extent1[3], extent2[3]);
+ return (maxX - minX) * (maxY - minY);
+};
+
+
+/**
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {ol.Extent=} opt_extent Destination extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.getForViewAndSize = function(center, resolution, rotation, size, opt_extent) {
+ var dx = resolution * size[0] / 2;
+ var dy = resolution * size[1] / 2;
+ var cosRotation = Math.cos(rotation);
+ var sinRotation = Math.sin(rotation);
+ var xCos = dx * cosRotation;
+ var xSin = dx * sinRotation;
+ var yCos = dy * cosRotation;
+ var ySin = dy * sinRotation;
+ var x = center[0];
+ var y = center[1];
+ var x0 = x - xCos + ySin;
+ var x1 = x - xCos - ySin;
+ var x2 = x + xCos - ySin;
+ var x3 = x + xCos + ySin;
+ var y0 = y - xSin - yCos;
+ var y1 = y - xSin + yCos;
+ var y2 = y + xSin + yCos;
+ var y3 = y + xSin - yCos;
+ return ol.extent.createOrUpdate(
+ Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3),
+ Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3),
+ opt_extent);
+};
+
+
+/**
+ * Get the height of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {number} Height.
+ * @api stable
+ */
+ol.extent.getHeight = function(extent) {
+ return extent[3] - extent[1];
+};
+
+
+/**
+ * @param {ol.Extent} extent1 Extent 1.
+ * @param {ol.Extent} extent2 Extent 2.
+ * @return {number} Intersection area.
+ */
+ol.extent.getIntersectionArea = function(extent1, extent2) {
+ var intersection = ol.extent.getIntersection(extent1, extent2);
+ return ol.extent.getArea(intersection);
+};
+
+
+/**
+ * Get the intersection of two extents.
+ * @param {ol.Extent} extent1 Extent 1.
+ * @param {ol.Extent} extent2 Extent 2.
+ * @param {ol.Extent=} opt_extent Optional extent to populate with intersection.
+ * @return {ol.Extent} Intersecting extent.
+ * @api stable
+ */
+ol.extent.getIntersection = function(extent1, extent2, opt_extent) {
+ var intersection = opt_extent ? opt_extent : ol.extent.createEmpty();
+ if (ol.extent.intersects(extent1, extent2)) {
+ if (extent1[0] > extent2[0]) {
+ intersection[0] = extent1[0];
+ } else {
+ intersection[0] = extent2[0];
+ }
+ if (extent1[1] > extent2[1]) {
+ intersection[1] = extent1[1];
+ } else {
+ intersection[1] = extent2[1];
+ }
+ if (extent1[2] < extent2[2]) {
+ intersection[2] = extent1[2];
+ } else {
+ intersection[2] = extent2[2];
+ }
+ if (extent1[3] < extent2[3]) {
+ intersection[3] = extent1[3];
+ } else {
+ intersection[3] = extent2[3];
+ }
+ }
+ return intersection;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @return {number} Margin.
+ */
+ol.extent.getMargin = function(extent) {
+ return ol.extent.getWidth(extent) + ol.extent.getHeight(extent);
+};
+
+
+/**
+ * Get the size (width, height) of an extent.
+ * @param {ol.Extent} extent The extent.
+ * @return {ol.Size} The extent size.
+ * @api stable
+ */
+ol.extent.getSize = function(extent) {
+ return [extent[2] - extent[0], extent[3] - extent[1]];
+};
+
+
+/**
+ * Get the top left coordinate of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {ol.Coordinate} Top left coordinate.
+ * @api stable
+ */
+ol.extent.getTopLeft = function(extent) {
+ return [extent[0], extent[3]];
+};
+
+
+/**
+ * Get the top right coordinate of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {ol.Coordinate} Top right coordinate.
+ * @api stable
+ */
+ol.extent.getTopRight = function(extent) {
+ return [extent[2], extent[3]];
+};
+
+
+/**
+ * Get the width of an extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {number} Width.
+ * @api stable
+ */
+ol.extent.getWidth = function(extent) {
+ return extent[2] - extent[0];
+};
+
+
+/**
+ * Determine if one extent intersects another.
+ * @param {ol.Extent} extent1 Extent 1.
+ * @param {ol.Extent} extent2 Extent.
+ * @return {boolean} The two extents intersect.
+ * @api stable
+ */
+ol.extent.intersects = function(extent1, extent2) {
+ return extent1[0] <= extent2[2] &&
+ extent1[2] >= extent2[0] &&
+ extent1[1] <= extent2[3] &&
+ extent1[3] >= extent2[1];
+};
+
+
+/**
+ * Determine if an extent is empty.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} Is empty.
+ * @api stable
+ */
+ol.extent.isEmpty = function(extent) {
+ return extent[2] < extent[0] || extent[3] < extent[1];
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} Extent.
+ */
+ol.extent.returnOrUpdate = function(extent, opt_extent) {
+ if (opt_extent) {
+ opt_extent[0] = extent[0];
+ opt_extent[1] = extent[1];
+ opt_extent[2] = extent[2];
+ opt_extent[3] = extent[3];
+ return opt_extent;
+ } else {
+ return extent;
+ }
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} value Value.
+ */
+ol.extent.scaleFromCenter = function(extent, value) {
+ var deltaX = ((extent[2] - extent[0]) / 2) * (value - 1);
+ var deltaY = ((extent[3] - extent[1]) / 2) * (value - 1);
+ extent[0] -= deltaX;
+ extent[2] += deltaX;
+ extent[1] -= deltaY;
+ extent[3] += deltaY;
+};
+
+
+/**
+ * Determine if the segment between two coordinates intersects (crosses,
+ * touches, or is contained by) the provided extent.
+ * @param {ol.Extent} extent The extent.
+ * @param {ol.Coordinate} start Segment start coordinate.
+ * @param {ol.Coordinate} end Segment end coordinate.
+ * @return {boolean} The segment intersects the extent.
+ */
+ol.extent.intersectsSegment = function(extent, start, end) {
+ var intersects = false;
+ var startRel = ol.extent.coordinateRelationship(extent, start);
+ var endRel = ol.extent.coordinateRelationship(extent, end);
+ if (startRel === ol.extent.Relationship.INTERSECTING ||
+ endRel === ol.extent.Relationship.INTERSECTING) {
+ intersects = true;
+ } else {
+ var minX = extent[0];
+ var minY = extent[1];
+ var maxX = extent[2];
+ var maxY = extent[3];
+ var startX = start[0];
+ var startY = start[1];
+ var endX = end[0];
+ var endY = end[1];
+ var slope = (endY - startY) / (endX - startX);
+ var x, y;
+ if (!!(endRel & ol.extent.Relationship.ABOVE) &&
+ !(startRel & ol.extent.Relationship.ABOVE)) {
+ // potentially intersects top
+ x = endX - ((endY - maxY) / slope);
+ intersects = x >= minX && x <= maxX;
+ }
+ if (!intersects && !!(endRel & ol.extent.Relationship.RIGHT) &&
+ !(startRel & ol.extent.Relationship.RIGHT)) {
+ // potentially intersects right
+ y = endY - ((endX - maxX) * slope);
+ intersects = y >= minY && y <= maxY;
+ }
+ if (!intersects && !!(endRel & ol.extent.Relationship.BELOW) &&
+ !(startRel & ol.extent.Relationship.BELOW)) {
+ // potentially intersects bottom
+ x = endX - ((endY - minY) / slope);
+ intersects = x >= minX && x <= maxX;
+ }
+ if (!intersects && !!(endRel & ol.extent.Relationship.LEFT) &&
+ !(startRel & ol.extent.Relationship.LEFT)) {
+ // potentially intersects left
+ y = endY - ((endX - minX) * slope);
+ intersects = y >= minY && y <= maxY;
+ }
+
+ }
+ return intersects;
+};
+
+
+/**
+ * Apply a transform function to the extent.
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.TransformFunction} transformFn Transform function. Called with
+ * [minX, minY, maxX, maxY] extent coordinates.
+ * @param {ol.Extent=} opt_extent Destination extent.
+ * @return {ol.Extent} Extent.
+ * @api stable
+ */
+ol.extent.applyTransform = function(extent, transformFn, opt_extent) {
+ var coordinates = [
+ extent[0], extent[1],
+ extent[0], extent[3],
+ extent[2], extent[1],
+ extent[2], extent[3]
+ ];
+ transformFn(coordinates, coordinates, 2);
+ var xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]];
+ var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];
+ return ol.extent.boundingExtentXYs_(xs, ys, opt_extent);
+};
+
+goog.provide('ol.geom.GeometryLayout');
+
+
+/**
+ * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z')
+ * or measure ('M') coordinate is available. Supported values are `'XY'`,
+ * `'XYZ'`, `'XYM'`, `'XYZM'`.
+ * @enum {string}
+ */
+ol.geom.GeometryLayout = {
+ XY: 'XY',
+ XYZ: 'XYZ',
+ XYM: 'XYM',
+ XYZM: 'XYZM'
+};
+
+goog.provide('ol.geom.GeometryType');
+
+
+/**
+ * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`,
+ * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`,
+ * `'GeometryCollection'`, `'Circle'`.
+ * @enum {string}
+ */
+ol.geom.GeometryType = {
+ POINT: 'Point',
+ LINE_STRING: 'LineString',
+ LINEAR_RING: 'LinearRing',
+ POLYGON: 'Polygon',
+ MULTI_POINT: 'MultiPoint',
+ MULTI_LINE_STRING: 'MultiLineString',
+ MULTI_POLYGON: 'MultiPolygon',
+ GEOMETRY_COLLECTION: 'GeometryCollection',
+ CIRCLE: 'Circle'
+};
+
+goog.provide('ol.functions');
+
+/**
+ * Always returns true.
+ * @returns {boolean} true.
+ */
+ol.functions.TRUE = function() {
+ return true;
+};
+
+/**
+ * Always returns false.
+ * @returns {boolean} false.
+ */
+ol.functions.FALSE = function() {
+ return false;
+};
+
+/**
+ * @license
+ * Latitude/longitude spherical geodesy formulae taken from
+ * http://www.movable-type.co.uk/scripts/latlong.html
+ * Licensed under CC-BY-3.0.
+ */
+
+goog.provide('ol.Sphere');
+
+goog.require('ol.math');
+
+
+/**
+ * @classdesc
+ * Class to create objects that can be used with {@link
+ * ol.geom.Polygon.circular}.
+ *
+ * For example to create a sphere whose radius is equal to the semi-major
+ * axis of the WGS84 ellipsoid:
+ *
+ * ```js
+ * var wgs84Sphere= new ol.Sphere(6378137);
+ * ```
+ *
+ * @constructor
+ * @param {number} radius Radius.
+ * @api
+ */
+ol.Sphere = function(radius) {
+
+ /**
+ * @type {number}
+ */
+ this.radius = radius;
+
+};
+
+
+/**
+ * Returns the geodesic area for a list of coordinates.
+ *
+ * [Reference](http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409)
+ * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+ * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+ * Laboratory, Pasadena, CA, June 2007
+ *
+ * @param {Array.<ol.Coordinate>} coordinates List of coordinates of a linear
+ * ring. If the ring is oriented clockwise, the area will be positive,
+ * otherwise it will be negative.
+ * @return {number} Area.
+ * @api
+ */
+ol.Sphere.prototype.geodesicArea = function(coordinates) {
+ var area = 0, len = coordinates.length;
+ var x1 = coordinates[len - 1][0];
+ var y1 = coordinates[len - 1][1];
+ for (var i = 0; i < len; i++) {
+ var x2 = coordinates[i][0], y2 = coordinates[i][1];
+ area += ol.math.toRadians(x2 - x1) *
+ (2 + Math.sin(ol.math.toRadians(y1)) +
+ Math.sin(ol.math.toRadians(y2)));
+ x1 = x2;
+ y1 = y2;
+ }
+ return area * this.radius * this.radius / 2.0;
+};
+
+
+/**
+ * Returns the distance from c1 to c2 using the haversine formula.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {number} Haversine distance.
+ * @api
+ */
+ol.Sphere.prototype.haversineDistance = function(c1, c2) {
+ var lat1 = ol.math.toRadians(c1[1]);
+ var lat2 = ol.math.toRadians(c2[1]);
+ var deltaLatBy2 = (lat2 - lat1) / 2;
+ var deltaLonBy2 = ol.math.toRadians(c2[0] - c1[0]) / 2;
+ var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +
+ Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *
+ Math.cos(lat1) * Math.cos(lat2);
+ return 2 * this.radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+};
+
+
+/**
+ * Returns the coordinate at the given distance and bearing from `c1`.
+ *
+ * @param {ol.Coordinate} c1 The origin point (`[lon, lat]` in degrees).
+ * @param {number} distance The great-circle distance between the origin
+ * point and the target point.
+ * @param {number} bearing The bearing (in radians).
+ * @return {ol.Coordinate} The target point.
+ */
+ol.Sphere.prototype.offset = function(c1, distance, bearing) {
+ var lat1 = ol.math.toRadians(c1[1]);
+ var lon1 = ol.math.toRadians(c1[0]);
+ var dByR = distance / this.radius;
+ var lat = Math.asin(
+ Math.sin(lat1) * Math.cos(dByR) +
+ Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
+ var lon = lon1 + Math.atan2(
+ Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
+ Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
+ return [ol.math.toDegrees(lon), ol.math.toDegrees(lat)];
+};
+
+goog.provide('ol.sphere.NORMAL');
+
+goog.require('ol.Sphere');
+
+
+/**
+ * The normal sphere.
+ * @const
+ * @type {ol.Sphere}
+ */
+ol.sphere.NORMAL = new ol.Sphere(6370997);
+
+goog.provide('ol.proj.Units');
+
+goog.require('ol.sphere.NORMAL');
+
+
+/**
+ * Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or
+ * `'us-ft'`.
+ * @enum {string}
+ */
+ol.proj.Units = {
+ DEGREES: 'degrees',
+ FEET: 'ft',
+ METERS: 'm',
+ PIXELS: 'pixels',
+ TILE_PIXELS: 'tile-pixels',
+ USFEET: 'us-ft'
+};
+
+
+/**
+ * Meters per unit lookup table.
+ * @const
+ * @type {Object.<ol.proj.Units, number>}
+ * @api stable
+ */
+ol.proj.Units.METERS_PER_UNIT = {};
+ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.DEGREES] =
+ 2 * Math.PI * ol.sphere.NORMAL.radius / 360;
+ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.FEET] = 0.3048;
+ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.METERS] = 1;
+ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.USFEET] = 1200 / 3937;
+
+goog.provide('ol.proj.proj4');
+
+
+/**
+ * @private
+ * @type {proj4}
+ */
+ol.proj.proj4.cache_ = null;
+
+
+/**
+ * Store the proj4 function.
+ * @param {proj4} proj4 The proj4 function.
+ */
+ol.proj.proj4.set = function(proj4) {
+ ol.proj.proj4.cache_ = proj4;
+};
+
+
+/**
+ * Get proj4.
+ * @return {proj4} The proj4 function set above or available globally.
+ */
+ol.proj.proj4.get = function() {
+ return ol.proj.proj4.cache_ || window['proj4'];
+};
+
+goog.provide('ol.proj.Projection');
+
+goog.require('ol');
+goog.require('ol.proj.Units');
+goog.require('ol.proj.proj4');
+
+
+/**
+ * @classdesc
+ * Projection definition class. One of these is created for each projection
+ * supported in the application and stored in the {@link ol.proj} namespace.
+ * You can use these in applications, but this is not required, as API params
+ * and options use {@link ol.ProjectionLike} which means the simple string
+ * code will suffice.
+ *
+ * You can use {@link ol.proj.get} to retrieve the object for a particular
+ * projection.
+ *
+ * The library includes definitions for `EPSG:4326` and `EPSG:3857`, together
+ * with the following aliases:
+ * * `EPSG:4326`: CRS:84, urn:ogc:def:crs:EPSG:6.6:4326,
+ * urn:ogc:def:crs:OGC:1.3:CRS84, urn:ogc:def:crs:OGC:2:84,
+ * http://www.opengis.net/gml/srs/epsg.xml#4326,
+ * urn:x-ogc:def:crs:EPSG:4326
+ * * `EPSG:3857`: EPSG:102100, EPSG:102113, EPSG:900913,
+ * urn:ogc:def:crs:EPSG:6.18:3:3857,
+ * http://www.opengis.net/gml/srs/epsg.xml#3857
+ *
+ * If you use proj4js, aliases can be added using `proj4.defs()`; see
+ * [documentation](https://github.com/proj4js/proj4js). To set an alternative
+ * namespace for proj4, use {@link ol.proj.setProj4}.
+ *
+ * @constructor
+ * @param {olx.ProjectionOptions} options Projection options.
+ * @struct
+ * @api stable
+ */
+ol.proj.Projection = function(options) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.code_ = options.code;
+
+ /**
+ * @private
+ * @type {ol.proj.Units}
+ */
+ this.units_ = /** @type {ol.proj.Units} */ (options.units);
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.extent_ = options.extent !== undefined ? options.extent : null;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.worldExtent_ = options.worldExtent !== undefined ?
+ options.worldExtent : null;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.axisOrientation_ = options.axisOrientation !== undefined ?
+ options.axisOrientation : 'enu';
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.global_ = options.global !== undefined ? options.global : false;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.canWrapX_ = !!(this.global_ && this.extent_);
+
+ /**
+ * @private
+ * @type {function(number, ol.Coordinate):number|undefined}
+ */
+ this.getPointResolutionFunc_ = options.getPointResolution;
+
+ /**
+ * @private
+ * @type {ol.tilegrid.TileGrid}
+ */
+ this.defaultTileGrid_ = null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.metersPerUnit_ = options.metersPerUnit;
+
+ var code = options.code;
+ ol.DEBUG && console.assert(code !== undefined,
+ 'Option "code" is required for constructing instance');
+ if (ol.ENABLE_PROJ4JS) {
+ var proj4js = ol.proj.proj4.get();
+ if (typeof proj4js == 'function') {
+ var def = proj4js.defs(code);
+ if (def !== undefined) {
+ if (def.axis !== undefined && options.axisOrientation === undefined) {
+ this.axisOrientation_ = def.axis;
+ }
+ if (options.metersPerUnit === undefined) {
+ this.metersPerUnit_ = def.to_meter;
+ }
+ if (options.units === undefined) {
+ this.units_ = def.units;
+ }
+ }
+ }
+ }
+
+};
+
+
+/**
+ * @return {boolean} The projection is suitable for wrapping the x-axis
+ */
+ol.proj.Projection.prototype.canWrapX = function() {
+ return this.canWrapX_;
+};
+
+
+/**
+ * Get the code for this projection, e.g. 'EPSG:4326'.
+ * @return {string} Code.
+ * @api stable
+ */
+ol.proj.Projection.prototype.getCode = function() {
+ return this.code_;
+};
+
+
+/**
+ * Get the validity extent for this projection.
+ * @return {ol.Extent} Extent.
+ * @api stable
+ */
+ol.proj.Projection.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+
+/**
+ * Get the units of this projection.
+ * @return {ol.proj.Units} Units.
+ * @api stable
+ */
+ol.proj.Projection.prototype.getUnits = function() {
+ return this.units_;
+};
+
+
+/**
+ * Get the amount of meters per unit of this projection. If the projection is
+ * not configured with `metersPerUnit` or a units identifier, the return is
+ * `undefined`.
+ * @return {number|undefined} Meters.
+ * @api stable
+ */
+ol.proj.Projection.prototype.getMetersPerUnit = function() {
+ return this.metersPerUnit_ || ol.proj.Units.METERS_PER_UNIT[this.units_];
+};
+
+
+/**
+ * Get the world extent for this projection.
+ * @return {ol.Extent} Extent.
+ * @api
+ */
+ol.proj.Projection.prototype.getWorldExtent = function() {
+ return this.worldExtent_;
+};
+
+
+/**
+ * Get the axis orientation of this projection.
+ * Example values are:
+ * enu - the default easting, northing, elevation.
+ * neu - northing, easting, up - useful for "lat/long" geographic coordinates,
+ * or south orientated transverse mercator.
+ * wnu - westing, northing, up - some planetary coordinate systems have
+ * "west positive" coordinate systems
+ * @return {string} Axis orientation.
+ */
+ol.proj.Projection.prototype.getAxisOrientation = function() {
+ return this.axisOrientation_;
+};
+
+
+/**
+ * Is this projection a global projection which spans the whole world?
+ * @return {boolean} Whether the projection is global.
+ * @api stable
+ */
+ol.proj.Projection.prototype.isGlobal = function() {
+ return this.global_;
+};
+
+
+/**
+* Set if the projection is a global projection which spans the whole world
+* @param {boolean} global Whether the projection is global.
+* @api stable
+*/
+ol.proj.Projection.prototype.setGlobal = function(global) {
+ this.global_ = global;
+ this.canWrapX_ = !!(global && this.extent_);
+};
+
+
+/**
+ * @return {ol.tilegrid.TileGrid} The default tile grid.
+ */
+ol.proj.Projection.prototype.getDefaultTileGrid = function() {
+ return this.defaultTileGrid_;
+};
+
+
+/**
+ * @param {ol.tilegrid.TileGrid} tileGrid The default tile grid.
+ */
+ol.proj.Projection.prototype.setDefaultTileGrid = function(tileGrid) {
+ this.defaultTileGrid_ = tileGrid;
+};
+
+
+/**
+ * Set the validity extent for this projection.
+ * @param {ol.Extent} extent Extent.
+ * @api stable
+ */
+ol.proj.Projection.prototype.setExtent = function(extent) {
+ this.extent_ = extent;
+ this.canWrapX_ = !!(this.global_ && extent);
+};
+
+
+/**
+ * Set the world extent for this projection.
+ * @param {ol.Extent} worldExtent World extent
+ * [minlon, minlat, maxlon, maxlat].
+ * @api
+ */
+ol.proj.Projection.prototype.setWorldExtent = function(worldExtent) {
+ this.worldExtent_ = worldExtent;
+};
+
+
+/**
+ * Set the getPointResolution function for this projection.
+ * @param {function(number, ol.Coordinate):number} func Function
+ * @api
+ */
+ol.proj.Projection.prototype.setGetPointResolution = function(func) {
+ this.getPointResolutionFunc_ = func;
+};
+
+
+/**
+ * Get the custom point resolution function for this projection (if set).
+ * @return {function(number, ol.Coordinate):number|undefined} The custom point
+ * resolution function (if set).
+ */
+ol.proj.Projection.prototype.getPointResolutionFunc = function() {
+ return this.getPointResolutionFunc_;
+};
+
+goog.provide('ol.proj.projections');
+
+
+/**
+ * @private
+ * @type {Object.<string, ol.proj.Projection>}
+ */
+ol.proj.projections.cache_ = {};
+
+
+/**
+ * Clear the projections cache.
+ */
+ol.proj.projections.clear = function() {
+ ol.proj.projections.cache_ = {};
+};
+
+
+/**
+ * Get a cached projection by code.
+ * @param {string} code The code for the projection.
+ * @return {ol.proj.Projection} The projection (if cached).
+ */
+ol.proj.projections.get = function(code) {
+ var projections = ol.proj.projections.cache_;
+ return projections[code] || null;
+};
+
+
+/**
+ * Add a projection to the cache.
+ * @param {string} code The projection code.
+ * @param {ol.proj.Projection} projection The projection to cache.
+ */
+ol.proj.projections.add = function(code, projection) {
+ var projections = ol.proj.projections.cache_;
+ projections[code] = projection;
+};
+
+goog.provide('ol.proj.transforms');
+
+goog.require('ol');
+goog.require('ol.obj');
+
+
+/**
+ * @private
+ * @type {Object.<string, Object.<string, ol.TransformFunction>>}
+ */
+ol.proj.transforms.cache_ = {};
+
+
+/**
+ * Clear the transform cache.
+ */
+ol.proj.transforms.clear = function() {
+ ol.proj.transforms.cache_ = {};
+};
+
+
+/**
+ * Registers a conversion function to convert coordinates from the source
+ * projection to the destination projection.
+ *
+ * @param {ol.proj.Projection} source Source.
+ * @param {ol.proj.Projection} destination Destination.
+ * @param {ol.TransformFunction} transformFn Transform.
+ */
+ol.proj.transforms.add = function(source, destination, transformFn) {
+ var sourceCode = source.getCode();
+ var destinationCode = destination.getCode();
+ var transforms = ol.proj.transforms.cache_;
+ if (!(sourceCode in transforms)) {
+ transforms[sourceCode] = {};
+ }
+ transforms[sourceCode][destinationCode] = transformFn;
+};
+
+
+/**
+ * Unregisters the conversion function to convert coordinates from the source
+ * projection to the destination projection. This method is used to clean up
+ * cached transforms during testing.
+ *
+ * @param {ol.proj.Projection} source Source projection.
+ * @param {ol.proj.Projection} destination Destination projection.
+ * @return {ol.TransformFunction} transformFn The unregistered transform.
+ */
+ol.proj.transforms.remove = function(source, destination) {
+ var sourceCode = source.getCode();
+ var destinationCode = destination.getCode();
+ var transforms = ol.proj.transforms.cache_;
+ ol.DEBUG && console.assert(sourceCode in transforms,
+ 'sourceCode should be in transforms');
+ ol.DEBUG && console.assert(destinationCode in transforms[sourceCode],
+ 'destinationCode should be in transforms of sourceCode');
+ var transform = transforms[sourceCode][destinationCode];
+ delete transforms[sourceCode][destinationCode];
+ if (ol.obj.isEmpty(transforms[sourceCode])) {
+ delete transforms[sourceCode];
+ }
+ return transform;
+};
+
+
+/**
+ * Get a transform given a source code and a destination code.
+ * @param {string} sourceCode The code for the source projection.
+ * @param {string} destinationCode The code for the destination projection.
+ * @return {ol.TransformFunction|undefined} The transform function (if found).
+ */
+ol.proj.transforms.get = function(sourceCode, destinationCode) {
+ var transform;
+ var transforms = ol.proj.transforms.cache_;
+ if (sourceCode in transforms && destinationCode in transforms[sourceCode]) {
+ transform = transforms[sourceCode][destinationCode];
+ }
+ return transform;
+};
+
+goog.provide('ol.proj');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.proj.Projection');
+goog.require('ol.proj.Units');
+goog.require('ol.proj.proj4');
+goog.require('ol.proj.projections');
+goog.require('ol.proj.transforms');
+goog.require('ol.sphere.NORMAL');
+
+
+/**
+ * Meters per unit lookup table.
+ * @const
+ * @type {Object.<ol.proj.Units, number>}
+ * @api stable
+ */
+ol.proj.METERS_PER_UNIT = ol.proj.Units.METERS_PER_UNIT;
+
+
+if (ol.ENABLE_PROJ4JS) {
+ /**
+ * Register proj4. If not explicitly registered, it will be assumed that
+ * proj4js will be loaded in the global namespace. For example in a
+ * browserify ES6 environment you could use:
+ *
+ * import ol from 'openlayers';
+ * import proj4 from 'proj4';
+ * ol.proj.setProj4(proj4);
+ *
+ * @param {proj4} proj4 Proj4.
+ * @api
+ */
+ ol.proj.setProj4 = function(proj4) {
+ ol.DEBUG && console.assert(typeof proj4 == 'function',
+ 'proj4 argument should be a function');
+ ol.proj.proj4.set(proj4);
+ };
+}
+
+
+/**
+ * Get the resolution of the point in degrees or distance units.
+ * For projections with degrees as the unit this will simply return the
+ * provided resolution. For other projections the point resolution is
+ * estimated by transforming the 'point' pixel to EPSG:4326,
+ * measuring its width and height on the normal sphere,
+ * and taking the average of the width and height.
+ * @param {ol.proj.Projection} projection The projection.
+ * @param {number} resolution Nominal resolution in projection units.
+ * @param {ol.Coordinate} point Point to find adjusted resolution at.
+ * @return {number} Point resolution at point in projection units.
+ * @api
+ */
+ol.proj.getPointResolution = function(projection, resolution, point) {
+ var pointResolution;
+ var getter = projection.getPointResolutionFunc();
+ if (getter) {
+ pointResolution = getter(resolution, point);
+ } else {
+ var units = projection.getUnits();
+ if (units == ol.proj.Units.DEGREES) {
+ pointResolution = resolution;
+ } else {
+ // Estimate point resolution by transforming the center pixel to EPSG:4326,
+ // measuring its width and height on the normal sphere, and taking the
+ // average of the width and height.
+ var toEPSG4326 = ol.proj.getTransformFromProjections(projection, ol.proj.get('EPSG:4326'));
+ var vertices = [
+ point[0] - resolution / 2, point[1],
+ point[0] + resolution / 2, point[1],
+ point[0], point[1] - resolution / 2,
+ point[0], point[1] + resolution / 2
+ ];
+ vertices = toEPSG4326(vertices, vertices, 2);
+ var width = ol.sphere.NORMAL.haversineDistance(
+ vertices.slice(0, 2), vertices.slice(2, 4));
+ var height = ol.sphere.NORMAL.haversineDistance(
+ vertices.slice(4, 6), vertices.slice(6, 8));
+ pointResolution = (width + height) / 2;
+ var metersPerUnit = projection.getMetersPerUnit();
+ if (metersPerUnit !== undefined) {
+ pointResolution /= metersPerUnit;
+ }
+ }
+ }
+ return pointResolution;
+};
+
+
+/**
+ * Registers transformation functions that don't alter coordinates. Those allow
+ * to transform between projections with equal meaning.
+ *
+ * @param {Array.<ol.proj.Projection>} projections Projections.
+ * @api
+ */
+ol.proj.addEquivalentProjections = function(projections) {
+ ol.proj.addProjections(projections);
+ projections.forEach(function(source) {
+ projections.forEach(function(destination) {
+ if (source !== destination) {
+ ol.proj.transforms.add(source, destination, ol.proj.cloneTransform);
+ }
+ });
+ });
+};
+
+
+/**
+ * Registers transformation functions to convert coordinates in any projection
+ * in projection1 to any projection in projection2.
+ *
+ * @param {Array.<ol.proj.Projection>} projections1 Projections with equal
+ * meaning.
+ * @param {Array.<ol.proj.Projection>} projections2 Projections with equal
+ * meaning.
+ * @param {ol.TransformFunction} forwardTransform Transformation from any
+ * projection in projection1 to any projection in projection2.
+ * @param {ol.TransformFunction} inverseTransform Transform from any projection
+ * in projection2 to any projection in projection1..
+ */
+ol.proj.addEquivalentTransforms = function(projections1, projections2, forwardTransform, inverseTransform) {
+ projections1.forEach(function(projection1) {
+ projections2.forEach(function(projection2) {
+ ol.proj.transforms.add(projection1, projection2, forwardTransform);
+ ol.proj.transforms.add(projection2, projection1, inverseTransform);
+ });
+ });
+};
+
+
+/**
+ * Add a Projection object to the list of supported projections that can be
+ * looked up by their code.
+ *
+ * @param {ol.proj.Projection} projection Projection instance.
+ * @api stable
+ */
+ol.proj.addProjection = function(projection) {
+ ol.proj.projections.add(projection.getCode(), projection);
+ ol.proj.transforms.add(projection, projection, ol.proj.cloneTransform);
+};
+
+
+/**
+ * @param {Array.<ol.proj.Projection>} projections Projections.
+ */
+ol.proj.addProjections = function(projections) {
+ var addedProjections = [];
+ projections.forEach(function(projection) {
+ addedProjections.push(ol.proj.addProjection(projection));
+ });
+};
+
+
+/**
+ * Clear all cached projections and transforms.
+ */
+ol.proj.clearAllProjections = function() {
+ ol.proj.projections.clear();
+ ol.proj.transforms.clear();
+};
+
+
+/**
+ * @param {ol.proj.Projection|string|undefined} projection Projection.
+ * @param {string} defaultCode Default code.
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.proj.createProjection = function(projection, defaultCode) {
+ if (!projection) {
+ return ol.proj.get(defaultCode);
+ } else if (typeof projection === 'string') {
+ return ol.proj.get(projection);
+ } else {
+ return /** @type {ol.proj.Projection} */ (projection);
+ }
+};
+
+
+/**
+ * Registers coordinate transform functions to convert coordinates between the
+ * source projection and the destination projection.
+ * The forward and inverse functions convert coordinate pairs; this function
+ * converts these into the functions used internally which also handle
+ * extents and coordinate arrays.
+ *
+ * @param {ol.ProjectionLike} source Source projection.
+ * @param {ol.ProjectionLike} destination Destination projection.
+ * @param {function(ol.Coordinate): ol.Coordinate} forward The forward transform
+ * function (that is, from the source projection to the destination
+ * projection) that takes a {@link ol.Coordinate} as argument and returns
+ * the transformed {@link ol.Coordinate}.
+ * @param {function(ol.Coordinate): ol.Coordinate} inverse The inverse transform
+ * function (that is, from the destination projection to the source
+ * projection) that takes a {@link ol.Coordinate} as argument and returns
+ * the transformed {@link ol.Coordinate}.
+ * @api stable
+ */
+ol.proj.addCoordinateTransforms = function(source, destination, forward, inverse) {
+ var sourceProj = ol.proj.get(source);
+ var destProj = ol.proj.get(destination);
+ ol.proj.transforms.add(sourceProj, destProj,
+ ol.proj.createTransformFromCoordinateTransform(forward));
+ ol.proj.transforms.add(destProj, sourceProj,
+ ol.proj.createTransformFromCoordinateTransform(inverse));
+};
+
+
+/**
+ * Creates a {@link ol.TransformFunction} from a simple 2D coordinate transform
+ * function.
+ * @param {function(ol.Coordinate): ol.Coordinate} transform Coordinate
+ * transform.
+ * @return {ol.TransformFunction} Transform function.
+ */
+ol.proj.createTransformFromCoordinateTransform = function(transform) {
+ return (
+ /**
+ * @param {Array.<number>} input Input.
+ * @param {Array.<number>=} opt_output Output.
+ * @param {number=} opt_dimension Dimension.
+ * @return {Array.<number>} Output.
+ */
+ function(input, opt_output, opt_dimension) {
+ var length = input.length;
+ var dimension = opt_dimension !== undefined ? opt_dimension : 2;
+ var output = opt_output !== undefined ? opt_output : new Array(length);
+ var point, i, j;
+ for (i = 0; i < length; i += dimension) {
+ point = transform([input[i], input[i + 1]]);
+ output[i] = point[0];
+ output[i + 1] = point[1];
+ for (j = dimension - 1; j >= 2; --j) {
+ output[i + j] = input[i + j];
+ }
+ }
+ return output;
+ });
+};
+
+
+/**
+ * Transforms a coordinate from longitude/latitude to a different projection.
+ * @param {ol.Coordinate} coordinate Coordinate as longitude and latitude, i.e.
+ * an array with longitude as 1st and latitude as 2nd element.
+ * @param {ol.ProjectionLike=} opt_projection Target projection. The
+ * default is Web Mercator, i.e. 'EPSG:3857'.
+ * @return {ol.Coordinate} Coordinate projected to the target projection.
+ * @api stable
+ */
+ol.proj.fromLonLat = function(coordinate, opt_projection) {
+ return ol.proj.transform(coordinate, 'EPSG:4326',
+ opt_projection !== undefined ? opt_projection : 'EPSG:3857');
+};
+
+
+/**
+ * Transforms a coordinate to longitude/latitude.
+ * @param {ol.Coordinate} coordinate Projected coordinate.
+ * @param {ol.ProjectionLike=} opt_projection Projection of the coordinate.
+ * The default is Web Mercator, i.e. 'EPSG:3857'.
+ * @return {ol.Coordinate} Coordinate as longitude and latitude, i.e. an array
+ * with longitude as 1st and latitude as 2nd element.
+ * @api stable
+ */
+ol.proj.toLonLat = function(coordinate, opt_projection) {
+ return ol.proj.transform(coordinate,
+ opt_projection !== undefined ? opt_projection : 'EPSG:3857', 'EPSG:4326');
+};
+
+
+/**
+ * Fetches a Projection object for the code specified.
+ *
+ * @param {ol.ProjectionLike} projectionLike Either a code string which is
+ * a combination of authority and identifier such as "EPSG:4326", or an
+ * existing projection object, or undefined.
+ * @return {ol.proj.Projection} Projection object, or null if not in list.
+ * @api stable
+ */
+ol.proj.get = function(projectionLike) {
+ var projection = null;
+ if (projectionLike instanceof ol.proj.Projection) {
+ projection = projectionLike;
+ } else if (typeof projectionLike === 'string') {
+ var code = projectionLike;
+ projection = ol.proj.projections.get(code);
+ if (ol.ENABLE_PROJ4JS) {
+ var proj4js = ol.proj.proj4.get();
+ if (!projection && typeof proj4js == 'function' &&
+ proj4js.defs(code) !== undefined) {
+ projection = new ol.proj.Projection({code: code});
+ ol.proj.addProjection(projection);
+ }
+ }
+ }
+ return projection;
+};
+
+
+/**
+ * Checks if two projections are the same, that is every coordinate in one
+ * projection does represent the same geographic point as the same coordinate in
+ * the other projection.
+ *
+ * @param {ol.proj.Projection} projection1 Projection 1.
+ * @param {ol.proj.Projection} projection2 Projection 2.
+ * @return {boolean} Equivalent.
+ * @api
+ */
+ol.proj.equivalent = function(projection1, projection2) {
+ if (projection1 === projection2) {
+ return true;
+ }
+ var equalUnits = projection1.getUnits() === projection2.getUnits();
+ if (projection1.getCode() === projection2.getCode()) {
+ return equalUnits;
+ } else {
+ var transformFn = ol.proj.getTransformFromProjections(
+ projection1, projection2);
+ return transformFn === ol.proj.cloneTransform && equalUnits;
+ }
+};
+
+
+/**
+ * Given the projection-like objects, searches for a transformation
+ * function to convert a coordinates array from the source projection to the
+ * destination projection.
+ *
+ * @param {ol.ProjectionLike} source Source.
+ * @param {ol.ProjectionLike} destination Destination.
+ * @return {ol.TransformFunction} Transform function.
+ * @api stable
+ */
+ol.proj.getTransform = function(source, destination) {
+ var sourceProjection = ol.proj.get(source);
+ var destinationProjection = ol.proj.get(destination);
+ return ol.proj.getTransformFromProjections(
+ sourceProjection, destinationProjection);
+};
+
+
+/**
+ * Searches in the list of transform functions for the function for converting
+ * coordinates from the source projection to the destination projection.
+ *
+ * @param {ol.proj.Projection} sourceProjection Source Projection object.
+ * @param {ol.proj.Projection} destinationProjection Destination Projection
+ * object.
+ * @return {ol.TransformFunction} Transform function.
+ */
+ol.proj.getTransformFromProjections = function(sourceProjection, destinationProjection) {
+ var sourceCode = sourceProjection.getCode();
+ var destinationCode = destinationProjection.getCode();
+ var transform = ol.proj.transforms.get(sourceCode, destinationCode);
+ if (ol.ENABLE_PROJ4JS && !transform) {
+ var proj4js = ol.proj.proj4.get();
+ if (typeof proj4js == 'function') {
+ var sourceDef = proj4js.defs(sourceCode);
+ var destinationDef = proj4js.defs(destinationCode);
+
+ if (sourceDef !== undefined && destinationDef !== undefined) {
+ if (sourceDef === destinationDef) {
+ ol.proj.addEquivalentProjections([destinationProjection, sourceProjection]);
+ } else {
+ var proj4Transform = proj4js(destinationCode, sourceCode);
+ ol.proj.addCoordinateTransforms(destinationProjection, sourceProjection,
+ proj4Transform.forward, proj4Transform.inverse);
+ }
+ transform = ol.proj.transforms.get(sourceCode, destinationCode);
+ }
+ }
+ }
+ if (!transform) {
+ ol.DEBUG && console.assert(transform, 'transform should be defined');
+ transform = ol.proj.identityTransform;
+ }
+ return transform;
+};
+
+
+/**
+ * @param {Array.<number>} input Input coordinate array.
+ * @param {Array.<number>=} opt_output Output array of coordinate values.
+ * @param {number=} opt_dimension Dimension.
+ * @return {Array.<number>} Input coordinate array (same array as input).
+ */
+ol.proj.identityTransform = function(input, opt_output, opt_dimension) {
+ if (opt_output !== undefined && input !== opt_output) {
+ // TODO: consider making this a warning instead
+ ol.DEBUG && console.assert(false, 'This should not be used internally.');
+ for (var i = 0, ii = input.length; i < ii; ++i) {
+ opt_output[i] = input[i];
+ }
+ input = opt_output;
+ }
+ return input;
+};
+
+
+/**
+ * @param {Array.<number>} input Input coordinate array.
+ * @param {Array.<number>=} opt_output Output array of coordinate values.
+ * @param {number=} opt_dimension Dimension.
+ * @return {Array.<number>} Output coordinate array (new array, same coordinate
+ * values).
+ */
+ol.proj.cloneTransform = function(input, opt_output, opt_dimension) {
+ var output;
+ if (opt_output !== undefined) {
+ for (var i = 0, ii = input.length; i < ii; ++i) {
+ opt_output[i] = input[i];
+ }
+ output = opt_output;
+ } else {
+ output = input.slice();
+ }
+ return output;
+};
+
+
+/**
+ * Transforms a coordinate from source projection to destination projection.
+ * This returns a new coordinate (and does not modify the original).
+ *
+ * See {@link ol.proj.transformExtent} for extent transformation.
+ * See the transform method of {@link ol.geom.Geometry} and its subclasses for
+ * geometry transforms.
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.ProjectionLike} source Source projection-like.
+ * @param {ol.ProjectionLike} destination Destination projection-like.
+ * @return {ol.Coordinate} Coordinate.
+ * @api stable
+ */
+ol.proj.transform = function(coordinate, source, destination) {
+ var transformFn = ol.proj.getTransform(source, destination);
+ return transformFn(coordinate, undefined, coordinate.length);
+};
+
+
+/**
+ * Transforms an extent from source projection to destination projection. This
+ * returns a new extent (and does not modify the original).
+ *
+ * @param {ol.Extent} extent The extent to transform.
+ * @param {ol.ProjectionLike} source Source projection-like.
+ * @param {ol.ProjectionLike} destination Destination projection-like.
+ * @return {ol.Extent} The transformed extent.
+ * @api stable
+ */
+ol.proj.transformExtent = function(extent, source, destination) {
+ var transformFn = ol.proj.getTransform(source, destination);
+ return ol.extent.applyTransform(extent, transformFn);
+};
+
+
+/**
+ * Transforms the given point to the destination projection.
+ *
+ * @param {ol.Coordinate} point Point.
+ * @param {ol.proj.Projection} sourceProjection Source projection.
+ * @param {ol.proj.Projection} destinationProjection Destination projection.
+ * @return {ol.Coordinate} Point.
+ */
+ol.proj.transformWithProjections = function(point, sourceProjection, destinationProjection) {
+ var transformFn = ol.proj.getTransformFromProjections(
+ sourceProjection, destinationProjection);
+ return transformFn(point);
+};
+
+goog.provide('ol.geom.Geometry');
+
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.extent');
+goog.require('ol.functions');
+goog.require('ol.proj');
+goog.require('ol.proj.Units');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for vector geometries.
+ *
+ * To get notified of changes to the geometry, register a listener for the
+ * generic `change` event on your geometry instance.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @api stable
+ */
+ol.geom.Geometry = function() {
+
+ ol.Object.call(this);
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.extent_ = ol.extent.createEmpty();
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.extentRevision_ = -1;
+
+ /**
+ * @protected
+ * @type {Object.<string, ol.geom.Geometry>}
+ */
+ this.simplifiedGeometryCache = {};
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.simplifiedGeometryMaxMinSquaredTolerance = 0;
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.simplifiedGeometryRevision = 0;
+
+};
+ol.inherits(ol.geom.Geometry, ol.Object);
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @abstract
+ * @return {!ol.geom.Geometry} Clone.
+ */
+ol.geom.Geometry.prototype.clone = function() {};
+
+
+/**
+ * @abstract
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {ol.Coordinate} closestPoint Closest point.
+ * @param {number} minSquaredDistance Minimum squared distance.
+ * @return {number} Minimum squared distance.
+ */
+ol.geom.Geometry.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {};
+
+
+/**
+ * Return the closest point of the geometry to the passed point as
+ * {@link ol.Coordinate coordinate}.
+ * @param {ol.Coordinate} point Point.
+ * @param {ol.Coordinate=} opt_closestPoint Closest point.
+ * @return {ol.Coordinate} Closest point.
+ * @api stable
+ */
+ol.geom.Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) {
+ var closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN];
+ this.closestPointXY(point[0], point[1], closestPoint, Infinity);
+ return closestPoint;
+};
+
+
+/**
+ * Returns true if this geometry includes the specified coordinate. If the
+ * coordinate is on the boundary of the geometry, returns false.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {boolean} Contains coordinate.
+ * @api
+ */
+ol.geom.Geometry.prototype.intersectsCoordinate = function(coordinate) {
+ return this.containsXY(coordinate[0], coordinate[1]);
+};
+
+
+/**
+ * @abstract
+ * @param {ol.Extent} extent Extent.
+ * @protected
+ * @return {ol.Extent} extent Extent.
+ */
+ol.geom.Geometry.prototype.computeExtent = function(extent) {};
+
+
+/**
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {boolean} Contains (x, y).
+ */
+ol.geom.Geometry.prototype.containsXY = ol.functions.FALSE;
+
+
+/**
+ * Get the extent of the geometry.
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {ol.Extent} extent Extent.
+ * @api stable
+ */
+ol.geom.Geometry.prototype.getExtent = function(opt_extent) {
+ if (this.extentRevision_ != this.getRevision()) {
+ this.extent_ = this.computeExtent(this.extent_);
+ this.extentRevision_ = this.getRevision();
+ }
+ return ol.extent.returnOrUpdate(this.extent_, opt_extent);
+};
+
+
+/**
+ * Rotate the geometry around a given coordinate. This modifies the geometry
+ * coordinates in place.
+ * @abstract
+ * @param {number} angle Rotation angle in radians.
+ * @param {ol.Coordinate} anchor The rotation center.
+ * @api
+ */
+ol.geom.Geometry.prototype.rotate = function(angle, anchor) {};
+
+
+/**
+ * Scale the geometry (with an optional origin). This modifies the geometry
+ * coordinates in place.
+ * @abstract
+ * @param {number} sx The scaling factor in the x-direction.
+ * @param {number=} opt_sy The scaling factor in the y-direction (defaults to
+ * sx).
+ * @param {ol.Coordinate=} opt_anchor The scale origin (defaults to the center
+ * of the geometry extent).
+ * @api
+ */
+ol.geom.Geometry.prototype.scale = function(sx, opt_sy, opt_anchor) {};
+
+
+/**
+ * Create a simplified version of this geometry. For linestrings, this uses
+ * the the {@link
+ * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
+ * Douglas Peucker} algorithm. For polygons, a quantization-based
+ * simplification is used to preserve topology.
+ * @function
+ * @param {number} tolerance The tolerance distance for simplification.
+ * @return {ol.geom.Geometry} A new, simplified version of the original
+ * geometry.
+ * @api
+ */
+ol.geom.Geometry.prototype.simplify = function(tolerance) {
+ return this.getSimplifiedGeometry(tolerance * tolerance);
+};
+
+
+/**
+ * Create a simplified version of this geometry using the Douglas Peucker
+ * algorithm.
+ * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
+ * @abstract
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {ol.geom.Geometry} Simplified geometry.
+ */
+ol.geom.Geometry.prototype.getSimplifiedGeometry = function(squaredTolerance) {};
+
+
+/**
+ * Get the type of this geometry.
+ * @abstract
+ * @return {ol.geom.GeometryType} Geometry type.
+ */
+ol.geom.Geometry.prototype.getType = function() {};
+
+
+/**
+ * Apply a transform function to each coordinate of the geometry.
+ * The geometry is modified in place.
+ * If you do not want the geometry modified in place, first `clone()` it and
+ * then use this function on the clone.
+ * @abstract
+ * @param {ol.TransformFunction} transformFn Transform.
+ */
+ol.geom.Geometry.prototype.applyTransform = function(transformFn) {};
+
+
+/**
+ * Test if the geometry and the passed extent intersect.
+ * @abstract
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} `true` if the geometry and the extent intersect.
+ */
+ol.geom.Geometry.prototype.intersectsExtent = function(extent) {};
+
+
+/**
+ * Translate the geometry. This modifies the geometry coordinates in place. If
+ * instead you want a new geometry, first `clone()` this geometry.
+ * @abstract
+ * @param {number} deltaX Delta X.
+ * @param {number} deltaY Delta Y.
+ */
+ol.geom.Geometry.prototype.translate = function(deltaX, deltaY) {};
+
+
+/**
+ * Transform each coordinate of the geometry from one coordinate reference
+ * system to another. The geometry is modified in place.
+ * For example, a line will be transformed to a line and a circle to a circle.
+ * If you do not want the geometry modified in place, first `clone()` it and
+ * then use this function on the clone.
+ *
+ * @param {ol.ProjectionLike} source The current projection. Can be a
+ * string identifier or a {@link ol.proj.Projection} object.
+ * @param {ol.ProjectionLike} destination The desired projection. Can be a
+ * string identifier or a {@link ol.proj.Projection} object.
+ * @return {ol.geom.Geometry} This geometry. Note that original geometry is
+ * modified in place.
+ * @api stable
+ */
+ol.geom.Geometry.prototype.transform = function(source, destination) {
+ ol.DEBUG && console.assert(
+ ol.proj.get(source).getUnits() !== ol.proj.Units.TILE_PIXELS &&
+ ol.proj.get(destination).getUnits() !== ol.proj.Units.TILE_PIXELS,
+ 'cannot transform geometries with TILE_PIXELS units');
+ this.applyTransform(ol.proj.getTransform(source, destination));
+ return this;
+};
+
+goog.provide('ol.geom.flat.transform');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.Transform} transform Transform.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Transformed coordinates.
+ */
+ol.geom.flat.transform.transform2D = function(flatCoordinates, offset, end, stride, transform, opt_dest) {
+ var dest = opt_dest ? opt_dest : [];
+ var i = 0;
+ var j;
+ for (j = offset; j < end; j += stride) {
+ var x = flatCoordinates[j];
+ var y = flatCoordinates[j + 1];
+ dest[i++] = transform[0] * x + transform[2] * y + transform[4];
+ dest[i++] = transform[1] * x + transform[3] * y + transform[5];
+ }
+ if (opt_dest && dest.length != i) {
+ dest.length = i;
+ }
+ return dest;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} angle Angle.
+ * @param {Array.<number>} anchor Rotation anchor point.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Transformed coordinates.
+ */
+ol.geom.flat.transform.rotate = function(flatCoordinates, offset, end, stride, angle, anchor, opt_dest) {
+ var dest = opt_dest ? opt_dest : [];
+ var cos = Math.cos(angle);
+ var sin = Math.sin(angle);
+ var anchorX = anchor[0];
+ var anchorY = anchor[1];
+ var i = 0;
+ for (var j = offset; j < end; j += stride) {
+ var deltaX = flatCoordinates[j] - anchorX;
+ var deltaY = flatCoordinates[j + 1] - anchorY;
+ dest[i++] = anchorX + deltaX * cos - deltaY * sin;
+ dest[i++] = anchorY + deltaX * sin + deltaY * cos;
+ for (var k = j + 2; k < j + stride; ++k) {
+ dest[i++] = flatCoordinates[k];
+ }
+ }
+ if (opt_dest && dest.length != i) {
+ dest.length = i;
+ }
+ return dest;
+};
+
+
+/**
+ * Scale the coordinates.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} sx Scale factor in the x-direction.
+ * @param {number} sy Scale factor in the y-direction.
+ * @param {Array.<number>} anchor Scale anchor point.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Transformed coordinates.
+ */
+ol.geom.flat.transform.scale = function(flatCoordinates, offset, end, stride, sx, sy, anchor, opt_dest) {
+ var dest = opt_dest ? opt_dest : [];
+ var anchorX = anchor[0];
+ var anchorY = anchor[1];
+ var i = 0;
+ for (var j = offset; j < end; j += stride) {
+ var deltaX = flatCoordinates[j] - anchorX;
+ var deltaY = flatCoordinates[j + 1] - anchorY;
+ dest[i++] = anchorX + sx * deltaX;
+ dest[i++] = anchorY + sy * deltaY;
+ for (var k = j + 2; k < j + stride; ++k) {
+ dest[i++] = flatCoordinates[k];
+ }
+ }
+ if (opt_dest && dest.length != i) {
+ dest.length = i;
+ }
+ return dest;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} deltaX Delta X.
+ * @param {number} deltaY Delta Y.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Transformed coordinates.
+ */
+ol.geom.flat.transform.translate = function(flatCoordinates, offset, end, stride, deltaX, deltaY, opt_dest) {
+ var dest = opt_dest ? opt_dest : [];
+ var i = 0;
+ var j, k;
+ for (j = offset; j < end; j += stride) {
+ dest[i++] = flatCoordinates[j] + deltaX;
+ dest[i++] = flatCoordinates[j + 1] + deltaY;
+ for (k = j + 2; k < j + stride; ++k) {
+ dest[i++] = flatCoordinates[k];
+ }
+ }
+ if (opt_dest && dest.length != i) {
+ dest.length = i;
+ }
+ return dest;
+};
+
+goog.provide('ol.geom.SimpleGeometry');
+
+goog.require('ol');
+goog.require('ol.functions');
+goog.require('ol.extent');
+goog.require('ol.geom.Geometry');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * Abstract base class; only used for creating subclasses; do not instantiate
+ * in apps, as cannot be rendered.
+ *
+ * @constructor
+ * @extends {ol.geom.Geometry}
+ * @api stable
+ */
+ol.geom.SimpleGeometry = function() {
+
+ ol.geom.Geometry.call(this);
+
+ /**
+ * @protected
+ * @type {ol.geom.GeometryLayout}
+ */
+ this.layout = ol.geom.GeometryLayout.XY;
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.stride = 2;
+
+ /**
+ * @protected
+ * @type {Array.<number>}
+ */
+ this.flatCoordinates = null;
+
+};
+ol.inherits(ol.geom.SimpleGeometry, ol.geom.Geometry);
+
+
+/**
+ * @param {number} stride Stride.
+ * @private
+ * @return {ol.geom.GeometryLayout} layout Layout.
+ */
+ol.geom.SimpleGeometry.getLayoutForStride_ = function(stride) {
+ var layout;
+ if (stride == 2) {
+ layout = ol.geom.GeometryLayout.XY;
+ } else if (stride == 3) {
+ layout = ol.geom.GeometryLayout.XYZ;
+ } else if (stride == 4) {
+ layout = ol.geom.GeometryLayout.XYZM;
+ }
+ ol.DEBUG && console.assert(layout, 'unsupported stride: ' + stride);
+ return /** @type {ol.geom.GeometryLayout} */ (layout);
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @return {number} Stride.
+ */
+ol.geom.SimpleGeometry.getStrideForLayout = function(layout) {
+ var stride;
+ if (layout == ol.geom.GeometryLayout.XY) {
+ stride = 2;
+ } else if (layout == ol.geom.GeometryLayout.XYZ || layout == ol.geom.GeometryLayout.XYM) {
+ stride = 3;
+ } else if (layout == ol.geom.GeometryLayout.XYZM) {
+ stride = 4;
+ }
+ ol.DEBUG && console.assert(stride, 'unsupported layout: ' + layout);
+ return /** @type {number} */ (stride);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.SimpleGeometry.prototype.containsXY = ol.functions.FALSE;
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.SimpleGeometry.prototype.computeExtent = function(extent) {
+ return ol.extent.createOrUpdateFromFlatCoordinates(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ extent);
+};
+
+
+/**
+ * @abstract
+ * @return {Array} Coordinates.
+ */
+ol.geom.SimpleGeometry.prototype.getCoordinates = function() {};
+
+
+/**
+ * Return the first coordinate of the geometry.
+ * @return {ol.Coordinate} First coordinate.
+ * @api stable
+ */
+ol.geom.SimpleGeometry.prototype.getFirstCoordinate = function() {
+ return this.flatCoordinates.slice(0, this.stride);
+};
+
+
+/**
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.geom.SimpleGeometry.prototype.getFlatCoordinates = function() {
+ return this.flatCoordinates;
+};
+
+
+/**
+ * Return the last coordinate of the geometry.
+ * @return {ol.Coordinate} Last point.
+ * @api stable
+ */
+ol.geom.SimpleGeometry.prototype.getLastCoordinate = function() {
+ return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride);
+};
+
+
+/**
+ * Return the {@link ol.geom.GeometryLayout layout} of the geometry.
+ * @return {ol.geom.GeometryLayout} Layout.
+ * @api stable
+ */
+ol.geom.SimpleGeometry.prototype.getLayout = function() {
+ return this.layout;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.SimpleGeometry.prototype.getSimplifiedGeometry = function(squaredTolerance) {
+ if (this.simplifiedGeometryRevision != this.getRevision()) {
+ ol.obj.clear(this.simplifiedGeometryCache);
+ this.simplifiedGeometryMaxMinSquaredTolerance = 0;
+ this.simplifiedGeometryRevision = this.getRevision();
+ }
+ // If squaredTolerance is negative or if we know that simplification will not
+ // have any effect then just return this.
+ if (squaredTolerance < 0 ||
+ (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
+ squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) {
+ return this;
+ }
+ var key = squaredTolerance.toString();
+ if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
+ return this.simplifiedGeometryCache[key];
+ } else {
+ var simplifiedGeometry =
+ this.getSimplifiedGeometryInternal(squaredTolerance);
+ var simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates();
+ if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) {
+ this.simplifiedGeometryCache[key] = simplifiedGeometry;
+ return simplifiedGeometry;
+ } else {
+ // Simplification did not actually remove any coordinates. We now know
+ // that any calls to getSimplifiedGeometry with a squaredTolerance less
+ // than or equal to the current squaredTolerance will also not have any
+ // effect. This allows us to short circuit simplification (saving CPU
+ // cycles) and prevents the cache of simplified geometries from filling
+ // up with useless identical copies of this geometry (saving memory).
+ this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
+ return this;
+ }
+ }
+};
+
+
+/**
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {ol.geom.SimpleGeometry} Simplified geometry.
+ * @protected
+ */
+ol.geom.SimpleGeometry.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
+ return this;
+};
+
+
+/**
+ * @return {number} Stride.
+ */
+ol.geom.SimpleGeometry.prototype.getStride = function() {
+ return this.stride;
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @protected
+ */
+ol.geom.SimpleGeometry.prototype.setFlatCoordinatesInternal = function(layout, flatCoordinates) {
+ this.stride = ol.geom.SimpleGeometry.getStrideForLayout(layout);
+ this.layout = layout;
+ this.flatCoordinates = flatCoordinates;
+};
+
+
+/**
+ * @abstract
+ * @param {Array} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ */
+ol.geom.SimpleGeometry.prototype.setCoordinates = function(coordinates, opt_layout) {};
+
+
+/**
+ * @param {ol.geom.GeometryLayout|undefined} layout Layout.
+ * @param {Array} coordinates Coordinates.
+ * @param {number} nesting Nesting.
+ * @protected
+ */
+ol.geom.SimpleGeometry.prototype.setLayout = function(layout, coordinates, nesting) {
+ /** @type {number} */
+ var stride;
+ if (layout) {
+ stride = ol.geom.SimpleGeometry.getStrideForLayout(layout);
+ } else {
+ var i;
+ for (i = 0; i < nesting; ++i) {
+ if (coordinates.length === 0) {
+ this.layout = ol.geom.GeometryLayout.XY;
+ this.stride = 2;
+ return;
+ } else {
+ coordinates = /** @type {Array} */ (coordinates[0]);
+ }
+ }
+ stride = coordinates.length;
+ layout = ol.geom.SimpleGeometry.getLayoutForStride_(stride);
+ }
+ this.layout = layout;
+ this.stride = stride;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.SimpleGeometry.prototype.applyTransform = function(transformFn) {
+ if (this.flatCoordinates) {
+ transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
+ this.changed();
+ }
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.geom.SimpleGeometry.prototype.rotate = function(angle, anchor) {
+ var flatCoordinates = this.getFlatCoordinates();
+ if (flatCoordinates) {
+ var stride = this.getStride();
+ ol.geom.flat.transform.rotate(
+ flatCoordinates, 0, flatCoordinates.length,
+ stride, angle, anchor, flatCoordinates);
+ this.changed();
+ }
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.geom.SimpleGeometry.prototype.scale = function(sx, opt_sy, opt_anchor) {
+ var sy = opt_sy;
+ if (sy === undefined) {
+ sy = sx;
+ }
+ var anchor = opt_anchor;
+ if (!anchor) {
+ anchor = ol.extent.getCenter(this.getExtent());
+ }
+ var flatCoordinates = this.getFlatCoordinates();
+ if (flatCoordinates) {
+ var stride = this.getStride();
+ ol.geom.flat.transform.scale(
+ flatCoordinates, 0, flatCoordinates.length,
+ stride, sx, sy, anchor, flatCoordinates);
+ this.changed();
+ }
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.SimpleGeometry.prototype.translate = function(deltaX, deltaY) {
+ var flatCoordinates = this.getFlatCoordinates();
+ if (flatCoordinates) {
+ var stride = this.getStride();
+ ol.geom.flat.transform.translate(
+ flatCoordinates, 0, flatCoordinates.length, stride,
+ deltaX, deltaY, flatCoordinates);
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.SimpleGeometry} simpleGeometry Simple geometry.
+ * @param {ol.Transform} transform Transform.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Transformed flat coordinates.
+ */
+ol.geom.SimpleGeometry.transform2D = function(simpleGeometry, transform, opt_dest) {
+ var flatCoordinates = simpleGeometry.getFlatCoordinates();
+ if (!flatCoordinates) {
+ return null;
+ } else {
+ var stride = simpleGeometry.getStride();
+ return ol.geom.flat.transform.transform2D(
+ flatCoordinates, 0, flatCoordinates.length, stride,
+ transform, opt_dest);
+ }
+};
+
+goog.provide('ol.geom.flat.area');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {number} Area.
+ */
+ol.geom.flat.area.linearRing = function(flatCoordinates, offset, end, stride) {
+ var twiceArea = 0;
+ var x1 = flatCoordinates[end - stride];
+ var y1 = flatCoordinates[end - stride + 1];
+ for (; offset < end; offset += stride) {
+ var x2 = flatCoordinates[offset];
+ var y2 = flatCoordinates[offset + 1];
+ twiceArea += y1 * x2 - x1 * y2;
+ x1 = x2;
+ y1 = y2;
+ }
+ return twiceArea / 2;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @return {number} Area.
+ */
+ol.geom.flat.area.linearRings = function(flatCoordinates, offset, ends, stride) {
+ var area = 0;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ area += ol.geom.flat.area.linearRing(flatCoordinates, offset, end, stride);
+ offset = end;
+ }
+ return area;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @return {number} Area.
+ */
+ol.geom.flat.area.linearRingss = function(flatCoordinates, offset, endss, stride) {
+ var area = 0;
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ area +=
+ ol.geom.flat.area.linearRings(flatCoordinates, offset, ends, stride);
+ offset = ends[ends.length - 1];
+ }
+ return area;
+};
+
+goog.provide('ol.geom.flat.closest');
+
+goog.require('ol');
+goog.require('ol.math');
+
+
+/**
+ * Returns the point on the 2D line segment flatCoordinates[offset1] to
+ * flatCoordinates[offset2] that is closest to the point (x, y). Extra
+ * dimensions are linearly interpolated.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset1 Offset 1.
+ * @param {number} offset2 Offset 2.
+ * @param {number} stride Stride.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {Array.<number>} closestPoint Closest point.
+ */
+ol.geom.flat.closest.point = function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) {
+ var x1 = flatCoordinates[offset1];
+ var y1 = flatCoordinates[offset1 + 1];
+ var dx = flatCoordinates[offset2] - x1;
+ var dy = flatCoordinates[offset2 + 1] - y1;
+ var i, offset;
+ if (dx === 0 && dy === 0) {
+ offset = offset1;
+ } else {
+ var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
+ if (t > 1) {
+ offset = offset2;
+ } else if (t > 0) {
+ for (i = 0; i < stride; ++i) {
+ closestPoint[i] = ol.math.lerp(flatCoordinates[offset1 + i],
+ flatCoordinates[offset2 + i], t);
+ }
+ closestPoint.length = stride;
+ return;
+ } else {
+ offset = offset1;
+ }
+ }
+ for (i = 0; i < stride; ++i) {
+ closestPoint[i] = flatCoordinates[offset + i];
+ }
+ closestPoint.length = stride;
+};
+
+
+/**
+ * Return the squared of the largest distance between any pair of consecutive
+ * coordinates.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} maxSquaredDelta Max squared delta.
+ * @return {number} Max squared delta.
+ */
+ol.geom.flat.closest.getMaxSquaredDelta = function(flatCoordinates, offset, end, stride, maxSquaredDelta) {
+ var x1 = flatCoordinates[offset];
+ var y1 = flatCoordinates[offset + 1];
+ for (offset += stride; offset < end; offset += stride) {
+ var x2 = flatCoordinates[offset];
+ var y2 = flatCoordinates[offset + 1];
+ var squaredDelta = ol.math.squaredDistance(x1, y1, x2, y2);
+ if (squaredDelta > maxSquaredDelta) {
+ maxSquaredDelta = squaredDelta;
+ }
+ x1 = x2;
+ y1 = y2;
+ }
+ return maxSquaredDelta;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {number} maxSquaredDelta Max squared delta.
+ * @return {number} Max squared delta.
+ */
+ol.geom.flat.closest.getsMaxSquaredDelta = function(flatCoordinates, offset, ends, stride, maxSquaredDelta) {
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ maxSquaredDelta = ol.geom.flat.closest.getMaxSquaredDelta(
+ flatCoordinates, offset, end, stride, maxSquaredDelta);
+ offset = end;
+ }
+ return maxSquaredDelta;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {number} maxSquaredDelta Max squared delta.
+ * @return {number} Max squared delta.
+ */
+ol.geom.flat.closest.getssMaxSquaredDelta = function(flatCoordinates, offset, endss, stride, maxSquaredDelta) {
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ maxSquaredDelta = ol.geom.flat.closest.getsMaxSquaredDelta(
+ flatCoordinates, offset, ends, stride, maxSquaredDelta);
+ offset = ends[ends.length - 1];
+ }
+ return maxSquaredDelta;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} maxDelta Max delta.
+ * @param {boolean} isRing Is ring.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {Array.<number>} closestPoint Closest point.
+ * @param {number} minSquaredDistance Minimum squared distance.
+ * @param {Array.<number>=} opt_tmpPoint Temporary point object.
+ * @return {number} Minimum squared distance.
+ */
+ol.geom.flat.closest.getClosestPoint = function(flatCoordinates, offset, end,
+ stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance,
+ opt_tmpPoint) {
+ if (offset == end) {
+ return minSquaredDistance;
+ }
+ var i, squaredDistance;
+ if (maxDelta === 0) {
+ // All points are identical, so just test the first point.
+ squaredDistance = ol.math.squaredDistance(
+ x, y, flatCoordinates[offset], flatCoordinates[offset + 1]);
+ if (squaredDistance < minSquaredDistance) {
+ for (i = 0; i < stride; ++i) {
+ closestPoint[i] = flatCoordinates[offset + i];
+ }
+ closestPoint.length = stride;
+ return squaredDistance;
+ } else {
+ return minSquaredDistance;
+ }
+ }
+ ol.DEBUG && console.assert(maxDelta > 0, 'maxDelta should be larger than 0');
+ var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN];
+ var index = offset + stride;
+ while (index < end) {
+ ol.geom.flat.closest.point(
+ flatCoordinates, index - stride, index, stride, x, y, tmpPoint);
+ squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]);
+ if (squaredDistance < minSquaredDistance) {
+ minSquaredDistance = squaredDistance;
+ for (i = 0; i < stride; ++i) {
+ closestPoint[i] = tmpPoint[i];
+ }
+ closestPoint.length = stride;
+ index += stride;
+ } else {
+ // Skip ahead multiple points, because we know that all the skipped
+ // points cannot be any closer than the closest point we have found so
+ // far. We know this because we know how close the current point is, how
+ // close the closest point we have found so far is, and the maximum
+ // distance between consecutive points. For example, if we're currently
+ // at distance 10, the best we've found so far is 3, and that the maximum
+ // distance between consecutive points is 2, then we'll need to skip at
+ // least (10 - 3) / 2 == 3 (rounded down) points to have any chance of
+ // finding a closer point. We use Math.max(..., 1) to ensure that we
+ // always advance at least one point, to avoid an infinite loop.
+ index += stride * Math.max(
+ ((Math.sqrt(squaredDistance) -
+ Math.sqrt(minSquaredDistance)) / maxDelta) | 0, 1);
+ }
+ }
+ if (isRing) {
+ // Check the closing segment.
+ ol.geom.flat.closest.point(
+ flatCoordinates, end - stride, offset, stride, x, y, tmpPoint);
+ squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]);
+ if (squaredDistance < minSquaredDistance) {
+ minSquaredDistance = squaredDistance;
+ for (i = 0; i < stride; ++i) {
+ closestPoint[i] = tmpPoint[i];
+ }
+ closestPoint.length = stride;
+ }
+ }
+ return minSquaredDistance;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {number} maxDelta Max delta.
+ * @param {boolean} isRing Is ring.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {Array.<number>} closestPoint Closest point.
+ * @param {number} minSquaredDistance Minimum squared distance.
+ * @param {Array.<number>=} opt_tmpPoint Temporary point object.
+ * @return {number} Minimum squared distance.
+ */
+ol.geom.flat.closest.getsClosestPoint = function(flatCoordinates, offset, ends,
+ stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance,
+ opt_tmpPoint) {
+ var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN];
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ minSquaredDistance = ol.geom.flat.closest.getClosestPoint(
+ flatCoordinates, offset, end, stride,
+ maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);
+ offset = end;
+ }
+ return minSquaredDistance;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {number} maxDelta Max delta.
+ * @param {boolean} isRing Is ring.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {Array.<number>} closestPoint Closest point.
+ * @param {number} minSquaredDistance Minimum squared distance.
+ * @param {Array.<number>=} opt_tmpPoint Temporary point object.
+ * @return {number} Minimum squared distance.
+ */
+ol.geom.flat.closest.getssClosestPoint = function(flatCoordinates, offset,
+ endss, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance,
+ opt_tmpPoint) {
+ var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN];
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ minSquaredDistance = ol.geom.flat.closest.getsClosestPoint(
+ flatCoordinates, offset, ends, stride,
+ maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);
+ offset = ends[ends.length - 1];
+ }
+ return minSquaredDistance;
+};
+
+goog.provide('ol.geom.flat.deflate');
+
+goog.require('ol');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} stride Stride.
+ * @return {number} offset Offset.
+ */
+ol.geom.flat.deflate.coordinate = function(flatCoordinates, offset, coordinate, stride) {
+ ol.DEBUG && console.assert(coordinate.length == stride,
+ 'length of the coordinate array should match stride');
+ var i, ii;
+ for (i = 0, ii = coordinate.length; i < ii; ++i) {
+ flatCoordinates[offset++] = coordinate[i];
+ }
+ return offset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {number} stride Stride.
+ * @return {number} offset Offset.
+ */
+ol.geom.flat.deflate.coordinates = function(flatCoordinates, offset, coordinates, stride) {
+ var i, ii;
+ for (i = 0, ii = coordinates.length; i < ii; ++i) {
+ var coordinate = coordinates[i];
+ ol.DEBUG && console.assert(coordinate.length == stride,
+ 'length of coordinate array should match stride');
+ var j;
+ for (j = 0; j < stride; ++j) {
+ flatCoordinates[offset++] = coordinate[j];
+ }
+ }
+ return offset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<ol.Coordinate>>} coordinatess Coordinatess.
+ * @param {number} stride Stride.
+ * @param {Array.<number>=} opt_ends Ends.
+ * @return {Array.<number>} Ends.
+ */
+ol.geom.flat.deflate.coordinatess = function(flatCoordinates, offset, coordinatess, stride, opt_ends) {
+ var ends = opt_ends ? opt_ends : [];
+ var i = 0;
+ var j, jj;
+ for (j = 0, jj = coordinatess.length; j < jj; ++j) {
+ var end = ol.geom.flat.deflate.coordinates(
+ flatCoordinates, offset, coordinatess[j], stride);
+ ends[i++] = end;
+ offset = end;
+ }
+ ends.length = i;
+ return ends;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinatesss Coordinatesss.
+ * @param {number} stride Stride.
+ * @param {Array.<Array.<number>>=} opt_endss Endss.
+ * @return {Array.<Array.<number>>} Endss.
+ */
+ol.geom.flat.deflate.coordinatesss = function(flatCoordinates, offset, coordinatesss, stride, opt_endss) {
+ var endss = opt_endss ? opt_endss : [];
+ var i = 0;
+ var j, jj;
+ for (j = 0, jj = coordinatesss.length; j < jj; ++j) {
+ var ends = ol.geom.flat.deflate.coordinatess(
+ flatCoordinates, offset, coordinatesss[j], stride, endss[i]);
+ endss[i++] = ends;
+ offset = ends[ends.length - 1];
+ }
+ endss.length = i;
+ return endss;
+};
+
+goog.provide('ol.geom.flat.inflate');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {Array.<ol.Coordinate>=} opt_coordinates Coordinates.
+ * @return {Array.<ol.Coordinate>} Coordinates.
+ */
+ol.geom.flat.inflate.coordinates = function(flatCoordinates, offset, end, stride, opt_coordinates) {
+ var coordinates = opt_coordinates !== undefined ? opt_coordinates : [];
+ var i = 0;
+ var j;
+ for (j = offset; j < end; j += stride) {
+ coordinates[i++] = flatCoordinates.slice(j, j + stride);
+ }
+ coordinates.length = i;
+ return coordinates;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {Array.<Array.<ol.Coordinate>>=} opt_coordinatess Coordinatess.
+ * @return {Array.<Array.<ol.Coordinate>>} Coordinatess.
+ */
+ol.geom.flat.inflate.coordinatess = function(flatCoordinates, offset, ends, stride, opt_coordinatess) {
+ var coordinatess = opt_coordinatess !== undefined ? opt_coordinatess : [];
+ var i = 0;
+ var j, jj;
+ for (j = 0, jj = ends.length; j < jj; ++j) {
+ var end = ends[j];
+ coordinatess[i++] = ol.geom.flat.inflate.coordinates(
+ flatCoordinates, offset, end, stride, coordinatess[i]);
+ offset = end;
+ }
+ coordinatess.length = i;
+ return coordinatess;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {Array.<Array.<Array.<ol.Coordinate>>>=} opt_coordinatesss
+ * Coordinatesss.
+ * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinatesss.
+ */
+ol.geom.flat.inflate.coordinatesss = function(flatCoordinates, offset, endss, stride, opt_coordinatesss) {
+ var coordinatesss = opt_coordinatesss !== undefined ? opt_coordinatesss : [];
+ var i = 0;
+ var j, jj;
+ for (j = 0, jj = endss.length; j < jj; ++j) {
+ var ends = endss[j];
+ coordinatesss[i++] = ol.geom.flat.inflate.coordinatess(
+ flatCoordinates, offset, ends, stride, coordinatesss[i]);
+ offset = ends[ends.length - 1];
+ }
+ coordinatesss.length = i;
+ return coordinatesss;
+};
+
+// Based on simplify-js https://github.com/mourner/simplify-js
+// Copyright (c) 2012, Vladimir Agafonkin
+// 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.
+
+goog.provide('ol.geom.flat.simplify');
+
+goog.require('ol.math');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {boolean} highQuality Highest quality.
+ * @param {Array.<number>=} opt_simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @return {Array.<number>} Simplified line string.
+ */
+ol.geom.flat.simplify.lineString = function(flatCoordinates, offset, end,
+ stride, squaredTolerance, highQuality, opt_simplifiedFlatCoordinates) {
+ var simplifiedFlatCoordinates = opt_simplifiedFlatCoordinates !== undefined ?
+ opt_simplifiedFlatCoordinates : [];
+ if (!highQuality) {
+ end = ol.geom.flat.simplify.radialDistance(flatCoordinates, offset, end,
+ stride, squaredTolerance,
+ simplifiedFlatCoordinates, 0);
+ flatCoordinates = simplifiedFlatCoordinates;
+ offset = 0;
+ stride = 2;
+ }
+ simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker(
+ flatCoordinates, offset, end, stride, squaredTolerance,
+ simplifiedFlatCoordinates, 0);
+ return simplifiedFlatCoordinates;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.douglasPeucker = function(flatCoordinates, offset, end,
+ stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {
+ var n = (end - offset) / stride;
+ if (n < 3) {
+ for (; offset < end; offset += stride) {
+ simplifiedFlatCoordinates[simplifiedOffset++] =
+ flatCoordinates[offset];
+ simplifiedFlatCoordinates[simplifiedOffset++] =
+ flatCoordinates[offset + 1];
+ }
+ return simplifiedOffset;
+ }
+ /** @type {Array.<number>} */
+ var markers = new Array(n);
+ markers[0] = 1;
+ markers[n - 1] = 1;
+ /** @type {Array.<number>} */
+ var stack = [offset, end - stride];
+ var index = 0;
+ var i;
+ while (stack.length > 0) {
+ var last = stack.pop();
+ var first = stack.pop();
+ var maxSquaredDistance = 0;
+ var x1 = flatCoordinates[first];
+ var y1 = flatCoordinates[first + 1];
+ var x2 = flatCoordinates[last];
+ var y2 = flatCoordinates[last + 1];
+ for (i = first + stride; i < last; i += stride) {
+ var x = flatCoordinates[i];
+ var y = flatCoordinates[i + 1];
+ var squaredDistance = ol.math.squaredSegmentDistance(
+ x, y, x1, y1, x2, y2);
+ if (squaredDistance > maxSquaredDistance) {
+ index = i;
+ maxSquaredDistance = squaredDistance;
+ }
+ }
+ if (maxSquaredDistance > squaredTolerance) {
+ markers[(index - offset) / stride] = 1;
+ if (first + stride < index) {
+ stack.push(first, index);
+ }
+ if (index + stride < last) {
+ stack.push(index, last);
+ }
+ }
+ }
+ for (i = 0; i < n; ++i) {
+ if (markers[i]) {
+ simplifiedFlatCoordinates[simplifiedOffset++] =
+ flatCoordinates[offset + i * stride];
+ simplifiedFlatCoordinates[simplifiedOffset++] =
+ flatCoordinates[offset + i * stride + 1];
+ }
+ }
+ return simplifiedOffset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @param {Array.<number>} simplifiedEnds Simplified ends.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.douglasPeuckers = function(flatCoordinates, offset,
+ ends, stride, squaredTolerance, simplifiedFlatCoordinates,
+ simplifiedOffset, simplifiedEnds) {
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ simplifiedOffset = ol.geom.flat.simplify.douglasPeucker(
+ flatCoordinates, offset, end, stride, squaredTolerance,
+ simplifiedFlatCoordinates, simplifiedOffset);
+ simplifiedEnds.push(simplifiedOffset);
+ offset = end;
+ }
+ return simplifiedOffset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.douglasPeuckerss = function(
+ flatCoordinates, offset, endss, stride, squaredTolerance,
+ simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) {
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ var simplifiedEnds = [];
+ simplifiedOffset = ol.geom.flat.simplify.douglasPeuckers(
+ flatCoordinates, offset, ends, stride, squaredTolerance,
+ simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds);
+ simplifiedEndss.push(simplifiedEnds);
+ offset = ends[ends.length - 1];
+ }
+ return simplifiedOffset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.radialDistance = function(flatCoordinates, offset, end,
+ stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {
+ if (end <= offset + stride) {
+ // zero or one point, no simplification possible, so copy and return
+ for (; offset < end; offset += stride) {
+ simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset];
+ simplifiedFlatCoordinates[simplifiedOffset++] =
+ flatCoordinates[offset + 1];
+ }
+ return simplifiedOffset;
+ }
+ var x1 = flatCoordinates[offset];
+ var y1 = flatCoordinates[offset + 1];
+ // copy first point
+ simplifiedFlatCoordinates[simplifiedOffset++] = x1;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y1;
+ var x2 = x1;
+ var y2 = y1;
+ for (offset += stride; offset < end; offset += stride) {
+ x2 = flatCoordinates[offset];
+ y2 = flatCoordinates[offset + 1];
+ if (ol.math.squaredDistance(x1, y1, x2, y2) > squaredTolerance) {
+ // copy point at offset
+ simplifiedFlatCoordinates[simplifiedOffset++] = x2;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y2;
+ x1 = x2;
+ y1 = y2;
+ }
+ }
+ if (x2 != x1 || y2 != y1) {
+ // copy last point
+ simplifiedFlatCoordinates[simplifiedOffset++] = x2;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y2;
+ }
+ return simplifiedOffset;
+};
+
+
+/**
+ * @param {number} value Value.
+ * @param {number} tolerance Tolerance.
+ * @return {number} Rounded value.
+ */
+ol.geom.flat.simplify.snap = function(value, tolerance) {
+ return tolerance * Math.round(value / tolerance);
+};
+
+
+/**
+ * Simplifies a line string using an algorithm designed by Tim Schaub.
+ * Coordinates are snapped to the nearest value in a virtual grid and
+ * consecutive duplicate coordinates are discarded. This effectively preserves
+ * topology as the simplification of any subsection of a line string is
+ * independent of the rest of the line string. This means that, for examples,
+ * the common edge between two polygons will be simplified to the same line
+ * string independently in both polygons. This implementation uses a single
+ * pass over the coordinates and eliminates intermediate collinear points.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} tolerance Tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.quantize = function(flatCoordinates, offset, end, stride,
+ tolerance, simplifiedFlatCoordinates, simplifiedOffset) {
+ // do nothing if the line is empty
+ if (offset == end) {
+ return simplifiedOffset;
+ }
+ // snap the first coordinate (P1)
+ var x1 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance);
+ var y1 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance);
+ offset += stride;
+ // add the first coordinate to the output
+ simplifiedFlatCoordinates[simplifiedOffset++] = x1;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y1;
+ // find the next coordinate that does not snap to the same value as the first
+ // coordinate (P2)
+ var x2, y2;
+ do {
+ x2 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance);
+ y2 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance);
+ offset += stride;
+ if (offset == end) {
+ // all coordinates snap to the same value, the line collapses to a point
+ // push the last snapped value anyway to ensure that the output contains
+ // at least two points
+ // FIXME should we really return at least two points anyway?
+ simplifiedFlatCoordinates[simplifiedOffset++] = x2;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y2;
+ return simplifiedOffset;
+ }
+ } while (x2 == x1 && y2 == y1);
+ while (offset < end) {
+ var x3, y3;
+ // snap the next coordinate (P3)
+ x3 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance);
+ y3 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance);
+ offset += stride;
+ // skip P3 if it is equal to P2
+ if (x3 == x2 && y3 == y2) {
+ continue;
+ }
+ // calculate the delta between P1 and P2
+ var dx1 = x2 - x1;
+ var dy1 = y2 - y1;
+ // calculate the delta between P3 and P1
+ var dx2 = x3 - x1;
+ var dy2 = y3 - y1;
+ // if P1, P2, and P3 are colinear and P3 is further from P1 than P2 is from
+ // P1 in the same direction then P2 is on the straight line between P1 and
+ // P3
+ if ((dx1 * dy2 == dy1 * dx2) &&
+ ((dx1 < 0 && dx2 < dx1) || dx1 == dx2 || (dx1 > 0 && dx2 > dx1)) &&
+ ((dy1 < 0 && dy2 < dy1) || dy1 == dy2 || (dy1 > 0 && dy2 > dy1))) {
+ // discard P2 and set P2 = P3
+ x2 = x3;
+ y2 = y3;
+ continue;
+ }
+ // either P1, P2, and P3 are not colinear, or they are colinear but P3 is
+ // between P3 and P1 or on the opposite half of the line to P2. add P2,
+ // and continue with P1 = P2 and P2 = P3
+ simplifiedFlatCoordinates[simplifiedOffset++] = x2;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y2;
+ x1 = x2;
+ y1 = y2;
+ x2 = x3;
+ y2 = y3;
+ }
+ // add the last point (P2)
+ simplifiedFlatCoordinates[simplifiedOffset++] = x2;
+ simplifiedFlatCoordinates[simplifiedOffset++] = y2;
+ return simplifiedOffset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {number} tolerance Tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @param {Array.<number>} simplifiedEnds Simplified ends.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.quantizes = function(
+ flatCoordinates, offset, ends, stride,
+ tolerance,
+ simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) {
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ simplifiedOffset = ol.geom.flat.simplify.quantize(
+ flatCoordinates, offset, end, stride,
+ tolerance,
+ simplifiedFlatCoordinates, simplifiedOffset);
+ simplifiedEnds.push(simplifiedOffset);
+ offset = end;
+ }
+ return simplifiedOffset;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {number} tolerance Tolerance.
+ * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
+ * coordinates.
+ * @param {number} simplifiedOffset Simplified offset.
+ * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss.
+ * @return {number} Simplified offset.
+ */
+ol.geom.flat.simplify.quantizess = function(
+ flatCoordinates, offset, endss, stride,
+ tolerance,
+ simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) {
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ var simplifiedEnds = [];
+ simplifiedOffset = ol.geom.flat.simplify.quantizes(
+ flatCoordinates, offset, ends, stride,
+ tolerance,
+ simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds);
+ simplifiedEndss.push(simplifiedEnds);
+ offset = ends[ends.length - 1];
+ }
+ return simplifiedOffset;
+};
+
+goog.provide('ol.geom.LinearRing');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.area');
+goog.require('ol.geom.flat.closest');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.geom.flat.simplify');
+
+
+/**
+ * @classdesc
+ * Linear ring geometry. Only used as part of polygon; cannot be rendered
+ * on its own.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.LinearRing = function(coordinates, opt_layout) {
+
+ ol.geom.SimpleGeometry.call(this);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDelta_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDeltaRevision_ = -1;
+
+ this.setCoordinates(coordinates, opt_layout);
+
+};
+ol.inherits(ol.geom.LinearRing, ol.geom.SimpleGeometry);
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.LinearRing} Clone.
+ * @api stable
+ */
+ol.geom.LinearRing.prototype.clone = function() {
+ var linearRing = new ol.geom.LinearRing(null);
+ linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
+ return linearRing;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.LinearRing.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ if (this.maxDeltaRevision_ != this.getRevision()) {
+ this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
+ this.maxDeltaRevision_ = this.getRevision();
+ }
+ return ol.geom.flat.closest.getClosestPoint(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
+};
+
+
+/**
+ * Return the area of the linear ring on projected plane.
+ * @return {number} Area (on projected plane).
+ * @api stable
+ */
+ol.geom.LinearRing.prototype.getArea = function() {
+ return ol.geom.flat.area.linearRing(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
+};
+
+
+/**
+ * Return the coordinates of the linear ring.
+ * @return {Array.<ol.Coordinate>} Coordinates.
+ * @api stable
+ */
+ol.geom.LinearRing.prototype.getCoordinates = function() {
+ return ol.geom.flat.inflate.coordinates(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.LinearRing.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
+ var simplifiedFlatCoordinates = [];
+ simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ squaredTolerance, simplifiedFlatCoordinates, 0);
+ var simplifiedLinearRing = new ol.geom.LinearRing(null);
+ simplifiedLinearRing.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates);
+ return simplifiedLinearRing;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.LinearRing.prototype.getType = function() {
+ return ol.geom.GeometryType.LINEAR_RING;
+};
+
+
+/**
+ * Set the coordinates of the linear ring.
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.LinearRing.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
+ } else {
+ this.setLayout(opt_layout, coordinates, 1);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ this.flatCoordinates.length = ol.geom.flat.deflate.coordinates(
+ this.flatCoordinates, 0, coordinates, this.stride);
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ */
+ol.geom.LinearRing.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.changed();
+};
+
+goog.provide('ol.geom.Point');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.math');
+
+
+/**
+ * @classdesc
+ * Point geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {ol.Coordinate} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.Point = function(coordinates, opt_layout) {
+ ol.geom.SimpleGeometry.call(this);
+ this.setCoordinates(coordinates, opt_layout);
+};
+ol.inherits(ol.geom.Point, ol.geom.SimpleGeometry);
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.Point} Clone.
+ * @api stable
+ */
+ol.geom.Point.prototype.clone = function() {
+ var point = new ol.geom.Point(null);
+ point.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
+ return point;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Point.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ var flatCoordinates = this.flatCoordinates;
+ var squaredDistance = ol.math.squaredDistance(
+ x, y, flatCoordinates[0], flatCoordinates[1]);
+ if (squaredDistance < minSquaredDistance) {
+ var stride = this.stride;
+ var i;
+ for (i = 0; i < stride; ++i) {
+ closestPoint[i] = flatCoordinates[i];
+ }
+ closestPoint.length = stride;
+ return squaredDistance;
+ } else {
+ return minSquaredDistance;
+ }
+};
+
+
+/**
+ * Return the coordinate of the point.
+ * @return {ol.Coordinate} Coordinates.
+ * @api stable
+ */
+ol.geom.Point.prototype.getCoordinates = function() {
+ return !this.flatCoordinates ? [] : this.flatCoordinates.slice();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Point.prototype.computeExtent = function(extent) {
+ return ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates, extent);
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.Point.prototype.getType = function() {
+ return ol.geom.GeometryType.POINT;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.Point.prototype.intersectsExtent = function(extent) {
+ return ol.extent.containsXY(extent,
+ this.flatCoordinates[0], this.flatCoordinates[1]);
+};
+
+
+/**
+ * Set the coordinate of the point.
+ * @param {ol.Coordinate} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.Point.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
+ } else {
+ this.setLayout(opt_layout, coordinates, 0);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ this.flatCoordinates.length = ol.geom.flat.deflate.coordinate(
+ this.flatCoordinates, 0, coordinates, this.stride);
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ */
+ol.geom.Point.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.changed();
+};
+
+goog.provide('ol.geom.flat.contains');
+
+goog.require('ol');
+goog.require('ol.extent');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} Contains extent.
+ */
+ol.geom.flat.contains.linearRingContainsExtent = function(flatCoordinates, offset, end, stride, extent) {
+ var outside = ol.extent.forEachCorner(extent,
+ /**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {boolean} Contains (x, y).
+ */
+ function(coordinate) {
+ return !ol.geom.flat.contains.linearRingContainsXY(flatCoordinates,
+ offset, end, stride, coordinate[0], coordinate[1]);
+ });
+ return !outside;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {boolean} Contains (x, y).
+ */
+ol.geom.flat.contains.linearRingContainsXY = function(flatCoordinates, offset, end, stride, x, y) {
+ // http://geomalgorithms.com/a03-_inclusion.html
+ // Copyright 2000 softSurfer, 2012 Dan Sunday
+ // This code may be freely used and modified for any purpose
+ // providing that this copyright notice is included with it.
+ // SoftSurfer makes no warranty for this code, and cannot be held
+ // liable for any real or imagined damage resulting from its use.
+ // Users of this code must verify correctness for their application.
+ var wn = 0;
+ var x1 = flatCoordinates[end - stride];
+ var y1 = flatCoordinates[end - stride + 1];
+ for (; offset < end; offset += stride) {
+ var x2 = flatCoordinates[offset];
+ var y2 = flatCoordinates[offset + 1];
+ if (y1 <= y) {
+ if (y2 > y && ((x2 - x1) * (y - y1)) - ((x - x1) * (y2 - y1)) > 0) {
+ wn++;
+ }
+ } else if (y2 <= y && ((x2 - x1) * (y - y1)) - ((x - x1) * (y2 - y1)) < 0) {
+ wn--;
+ }
+ x1 = x2;
+ y1 = y2;
+ }
+ return wn !== 0;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {boolean} Contains (x, y).
+ */
+ol.geom.flat.contains.linearRingsContainsXY = function(flatCoordinates, offset, ends, stride, x, y) {
+ ol.DEBUG && console.assert(ends.length > 0, 'ends should not be an empty array');
+ if (ends.length === 0) {
+ return false;
+ }
+ if (!ol.geom.flat.contains.linearRingContainsXY(
+ flatCoordinates, offset, ends[0], stride, x, y)) {
+ return false;
+ }
+ var i, ii;
+ for (i = 1, ii = ends.length; i < ii; ++i) {
+ if (ol.geom.flat.contains.linearRingContainsXY(
+ flatCoordinates, ends[i - 1], ends[i], stride, x, y)) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {boolean} Contains (x, y).
+ */
+ol.geom.flat.contains.linearRingssContainsXY = function(flatCoordinates, offset, endss, stride, x, y) {
+ ol.DEBUG && console.assert(endss.length > 0, 'endss should not be an empty array');
+ if (endss.length === 0) {
+ return false;
+ }
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ if (ol.geom.flat.contains.linearRingsContainsXY(
+ flatCoordinates, offset, ends, stride, x, y)) {
+ return true;
+ }
+ offset = ends[ends.length - 1];
+ }
+ return false;
+};
+
+goog.provide('ol.geom.flat.interiorpoint');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.geom.flat.contains');
+
+
+/**
+ * Calculates a point that is likely to lie in the interior of the linear rings.
+ * Inspired by JTS's com.vividsolutions.jts.geom.Geometry#getInteriorPoint.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {Array.<number>} flatCenters Flat centers.
+ * @param {number} flatCentersOffset Flat center offset.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Destination.
+ */
+ol.geom.flat.interiorpoint.linearRings = function(flatCoordinates, offset,
+ ends, stride, flatCenters, flatCentersOffset, opt_dest) {
+ var i, ii, x, x1, x2, y1, y2;
+ var y = flatCenters[flatCentersOffset + 1];
+ /** @type {Array.<number>} */
+ var intersections = [];
+ // Calculate intersections with the horizontal line
+ var end = ends[0];
+ x1 = flatCoordinates[end - stride];
+ y1 = flatCoordinates[end - stride + 1];
+ for (i = offset; i < end; i += stride) {
+ x2 = flatCoordinates[i];
+ y2 = flatCoordinates[i + 1];
+ if ((y <= y1 && y2 <= y) || (y1 <= y && y <= y2)) {
+ x = (y - y1) / (y2 - y1) * (x2 - x1) + x1;
+ intersections.push(x);
+ }
+ x1 = x2;
+ y1 = y2;
+ }
+ // Find the longest segment of the horizontal line that has its center point
+ // inside the linear ring.
+ var pointX = NaN;
+ var maxSegmentLength = -Infinity;
+ intersections.sort(ol.array.numberSafeCompareFunction);
+ x1 = intersections[0];
+ for (i = 1, ii = intersections.length; i < ii; ++i) {
+ x2 = intersections[i];
+ var segmentLength = Math.abs(x2 - x1);
+ if (segmentLength > maxSegmentLength) {
+ x = (x1 + x2) / 2;
+ if (ol.geom.flat.contains.linearRingsContainsXY(
+ flatCoordinates, offset, ends, stride, x, y)) {
+ pointX = x;
+ maxSegmentLength = segmentLength;
+ }
+ }
+ x1 = x2;
+ }
+ if (isNaN(pointX)) {
+ // There is no horizontal line that has its center point inside the linear
+ // ring. Use the center of the the linear ring's extent.
+ pointX = flatCenters[flatCentersOffset];
+ }
+ if (opt_dest) {
+ opt_dest.push(pointX, y);
+ return opt_dest;
+ } else {
+ return [pointX, y];
+ }
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {Array.<number>} flatCenters Flat centers.
+ * @return {Array.<number>} Interior points.
+ */
+ol.geom.flat.interiorpoint.linearRingss = function(flatCoordinates, offset, endss, stride, flatCenters) {
+ ol.DEBUG && console.assert(2 * endss.length == flatCenters.length,
+ 'endss.length times 2 should be flatCenters.length');
+ var interiorPoints = [];
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ interiorPoints = ol.geom.flat.interiorpoint.linearRings(flatCoordinates,
+ offset, ends, stride, flatCenters, 2 * i, interiorPoints);
+ offset = ends[ends.length - 1];
+ }
+ return interiorPoints;
+};
+
+goog.provide('ol.geom.flat.segments');
+
+
+/**
+ * This function calls `callback` for each segment of the flat coordinates
+ * array. If the callback returns a truthy value the function returns that
+ * value immediately. Otherwise the function returns `false`.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function
+ * called for each segment.
+ * @param {S=} opt_this The object to be used as the value of 'this'
+ * within callback.
+ * @return {T|boolean} Value.
+ * @template T,S
+ */
+ol.geom.flat.segments.forEach = function(flatCoordinates, offset, end, stride, callback, opt_this) {
+ var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]];
+ var point2 = [];
+ var ret;
+ for (; (offset + stride) < end; offset += stride) {
+ point2[0] = flatCoordinates[offset + stride];
+ point2[1] = flatCoordinates[offset + stride + 1];
+ ret = callback.call(opt_this, point1, point2);
+ if (ret) {
+ return ret;
+ }
+ point1[0] = point2[0];
+ point1[1] = point2[1];
+ }
+ return false;
+};
+
+goog.provide('ol.geom.flat.intersectsextent');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.flat.contains');
+goog.require('ol.geom.flat.segments');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} True if the geometry and the extent intersect.
+ */
+ol.geom.flat.intersectsextent.lineString = function(flatCoordinates, offset, end, stride, extent) {
+ var coordinatesExtent = ol.extent.extendFlatCoordinates(
+ ol.extent.createEmpty(), flatCoordinates, offset, end, stride);
+ if (!ol.extent.intersects(extent, coordinatesExtent)) {
+ return false;
+ }
+ if (ol.extent.containsExtent(extent, coordinatesExtent)) {
+ return true;
+ }
+ if (coordinatesExtent[0] >= extent[0] &&
+ coordinatesExtent[2] <= extent[2]) {
+ return true;
+ }
+ if (coordinatesExtent[1] >= extent[1] &&
+ coordinatesExtent[3] <= extent[3]) {
+ return true;
+ }
+ return ol.geom.flat.segments.forEach(flatCoordinates, offset, end, stride,
+ /**
+ * @param {ol.Coordinate} point1 Start point.
+ * @param {ol.Coordinate} point2 End point.
+ * @return {boolean} `true` if the segment and the extent intersect,
+ * `false` otherwise.
+ */
+ function(point1, point2) {
+ return ol.extent.intersectsSegment(extent, point1, point2);
+ });
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} True if the geometry and the extent intersect.
+ */
+ol.geom.flat.intersectsextent.lineStrings = function(flatCoordinates, offset, ends, stride, extent) {
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ if (ol.geom.flat.intersectsextent.lineString(
+ flatCoordinates, offset, ends[i], stride, extent)) {
+ return true;
+ }
+ offset = ends[i];
+ }
+ return false;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} True if the geometry and the extent intersect.
+ */
+ol.geom.flat.intersectsextent.linearRing = function(flatCoordinates, offset, end, stride, extent) {
+ if (ol.geom.flat.intersectsextent.lineString(
+ flatCoordinates, offset, end, stride, extent)) {
+ return true;
+ }
+ if (ol.geom.flat.contains.linearRingContainsXY(
+ flatCoordinates, offset, end, stride, extent[0], extent[1])) {
+ return true;
+ }
+ if (ol.geom.flat.contains.linearRingContainsXY(
+ flatCoordinates, offset, end, stride, extent[0], extent[3])) {
+ return true;
+ }
+ if (ol.geom.flat.contains.linearRingContainsXY(
+ flatCoordinates, offset, end, stride, extent[2], extent[1])) {
+ return true;
+ }
+ if (ol.geom.flat.contains.linearRingContainsXY(
+ flatCoordinates, offset, end, stride, extent[2], extent[3])) {
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} True if the geometry and the extent intersect.
+ */
+ol.geom.flat.intersectsextent.linearRings = function(flatCoordinates, offset, ends, stride, extent) {
+ ol.DEBUG && console.assert(ends.length > 0, 'ends should not be an empty array');
+ if (!ol.geom.flat.intersectsextent.linearRing(
+ flatCoordinates, offset, ends[0], stride, extent)) {
+ return false;
+ }
+ if (ends.length === 1) {
+ return true;
+ }
+ var i, ii;
+ for (i = 1, ii = ends.length; i < ii; ++i) {
+ if (ol.geom.flat.contains.linearRingContainsExtent(
+ flatCoordinates, ends[i - 1], ends[i], stride, extent)) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} True if the geometry and the extent intersect.
+ */
+ol.geom.flat.intersectsextent.linearRingss = function(flatCoordinates, offset, endss, stride, extent) {
+ ol.DEBUG && console.assert(endss.length > 0, 'endss should not be an empty array');
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ if (ol.geom.flat.intersectsextent.linearRings(
+ flatCoordinates, offset, ends, stride, extent)) {
+ return true;
+ }
+ offset = ends[ends.length - 1];
+ }
+ return false;
+};
+
+goog.provide('ol.geom.flat.reverse');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ */
+ol.geom.flat.reverse.coordinates = function(flatCoordinates, offset, end, stride) {
+ while (offset < end - stride) {
+ var i;
+ for (i = 0; i < stride; ++i) {
+ var tmp = flatCoordinates[offset + i];
+ flatCoordinates[offset + i] = flatCoordinates[end - stride + i];
+ flatCoordinates[end - stride + i] = tmp;
+ }
+ offset += stride;
+ end -= stride;
+ }
+};
+
+goog.provide('ol.geom.flat.orient');
+
+goog.require('ol');
+goog.require('ol.geom.flat.reverse');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {boolean} Is clockwise.
+ */
+ol.geom.flat.orient.linearRingIsClockwise = function(flatCoordinates, offset, end, stride) {
+ // http://tinyurl.com/clockwise-method
+ // https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogrlinearring.cpp
+ var edge = 0;
+ var x1 = flatCoordinates[end - stride];
+ var y1 = flatCoordinates[end - stride + 1];
+ for (; offset < end; offset += stride) {
+ var x2 = flatCoordinates[offset];
+ var y2 = flatCoordinates[offset + 1];
+ edge += (x2 - x1) * (y2 + y1);
+ x1 = x2;
+ y1 = y2;
+ }
+ return edge > 0;
+};
+
+
+/**
+ * Determines if linear rings are oriented. By default, left-hand orientation
+ * is tested (first ring must be clockwise, remaining rings counter-clockwise).
+ * To test for right-hand orientation, use the `opt_right` argument.
+ *
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Array of end indexes.
+ * @param {number} stride Stride.
+ * @param {boolean=} opt_right Test for right-hand orientation
+ * (counter-clockwise exterior ring and clockwise interior rings).
+ * @return {boolean} Rings are correctly oriented.
+ */
+ol.geom.flat.orient.linearRingsAreOriented = function(flatCoordinates, offset, ends, stride, opt_right) {
+ var right = opt_right !== undefined ? opt_right : false;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(
+ flatCoordinates, offset, end, stride);
+ if (i === 0) {
+ if ((right && isClockwise) || (!right && !isClockwise)) {
+ return false;
+ }
+ } else {
+ if ((right && !isClockwise) || (!right && isClockwise)) {
+ return false;
+ }
+ }
+ offset = end;
+ }
+ return true;
+};
+
+
+/**
+ * Determines if linear rings are oriented. By default, left-hand orientation
+ * is tested (first ring must be clockwise, remaining rings counter-clockwise).
+ * To test for right-hand orientation, use the `opt_right` argument.
+ *
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Array of array of end indexes.
+ * @param {number} stride Stride.
+ * @param {boolean=} opt_right Test for right-hand orientation
+ * (counter-clockwise exterior ring and clockwise interior rings).
+ * @return {boolean} Rings are correctly oriented.
+ */
+ol.geom.flat.orient.linearRingssAreOriented = function(flatCoordinates, offset, endss, stride, opt_right) {
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ if (!ol.geom.flat.orient.linearRingsAreOriented(
+ flatCoordinates, offset, endss[i], stride, opt_right)) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * Orient coordinates in a flat array of linear rings. By default, rings
+ * are oriented following the left-hand rule (clockwise for exterior and
+ * counter-clockwise for interior rings). To orient according to the
+ * right-hand rule, use the `opt_right` argument.
+ *
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {boolean=} opt_right Follow the right-hand rule for orientation.
+ * @return {number} End.
+ */
+ol.geom.flat.orient.orientLinearRings = function(flatCoordinates, offset, ends, stride, opt_right) {
+ var right = opt_right !== undefined ? opt_right : false;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(
+ flatCoordinates, offset, end, stride);
+ var reverse = i === 0 ?
+ (right && isClockwise) || (!right && !isClockwise) :
+ (right && !isClockwise) || (!right && isClockwise);
+ if (reverse) {
+ ol.geom.flat.reverse.coordinates(flatCoordinates, offset, end, stride);
+ }
+ offset = end;
+ }
+ return offset;
+};
+
+
+/**
+ * Orient coordinates in a flat array of linear rings. By default, rings
+ * are oriented following the left-hand rule (clockwise for exterior and
+ * counter-clockwise for interior rings). To orient according to the
+ * right-hand rule, use the `opt_right` argument.
+ *
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Array of array of end indexes.
+ * @param {number} stride Stride.
+ * @param {boolean=} opt_right Follow the right-hand rule for orientation.
+ * @return {number} End.
+ */
+ol.geom.flat.orient.orientLinearRingss = function(flatCoordinates, offset, endss, stride, opt_right) {
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ offset = ol.geom.flat.orient.orientLinearRings(
+ flatCoordinates, offset, endss[i], stride, opt_right);
+ }
+ return offset;
+};
+
+goog.provide('ol.geom.Polygon');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.LinearRing');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.area');
+goog.require('ol.geom.flat.closest');
+goog.require('ol.geom.flat.contains');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.geom.flat.interiorpoint');
+goog.require('ol.geom.flat.intersectsextent');
+goog.require('ol.geom.flat.orient');
+goog.require('ol.geom.flat.simplify');
+goog.require('ol.math');
+
+
+/**
+ * @classdesc
+ * Polygon geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.Polygon = function(coordinates, opt_layout) {
+
+ ol.geom.SimpleGeometry.call(this);
+
+ /**
+ * @type {Array.<number>}
+ * @private
+ */
+ this.ends_ = [];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.flatInteriorPointRevision_ = -1;
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.flatInteriorPoint_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDelta_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDeltaRevision_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.orientedRevision_ = -1;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.orientedFlatCoordinates_ = null;
+
+ this.setCoordinates(coordinates, opt_layout);
+
+};
+ol.inherits(ol.geom.Polygon, ol.geom.SimpleGeometry);
+
+
+/**
+ * Append the passed linear ring to this polygon.
+ * @param {ol.geom.LinearRing} linearRing Linear ring.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.appendLinearRing = function(linearRing) {
+ ol.DEBUG && console.assert(linearRing.getLayout() == this.layout,
+ 'layout of linearRing should match layout');
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = linearRing.getFlatCoordinates().slice();
+ } else {
+ ol.array.extend(this.flatCoordinates, linearRing.getFlatCoordinates());
+ }
+ this.ends_.push(this.flatCoordinates.length);
+ this.changed();
+};
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.Polygon} Clone.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.clone = function() {
+ var polygon = new ol.geom.Polygon(null);
+ polygon.setFlatCoordinates(
+ this.layout, this.flatCoordinates.slice(), this.ends_.slice());
+ return polygon;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Polygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ if (this.maxDeltaRevision_ != this.getRevision()) {
+ this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta(
+ this.flatCoordinates, 0, this.ends_, this.stride, 0));
+ this.maxDeltaRevision_ = this.getRevision();
+ }
+ return ol.geom.flat.closest.getsClosestPoint(
+ this.flatCoordinates, 0, this.ends_, this.stride,
+ this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Polygon.prototype.containsXY = function(x, y) {
+ return ol.geom.flat.contains.linearRingsContainsXY(
+ this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y);
+};
+
+
+/**
+ * Return the area of the polygon on projected plane.
+ * @return {number} Area (on projected plane).
+ * @api stable
+ */
+ol.geom.Polygon.prototype.getArea = function() {
+ return ol.geom.flat.area.linearRings(
+ this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride);
+};
+
+
+/**
+ * Get the coordinate array for this geometry. This array has the structure
+ * of a GeoJSON coordinate array for polygons.
+ *
+ * @param {boolean=} opt_right Orient coordinates according to the right-hand
+ * rule (counter-clockwise for exterior and clockwise for interior rings).
+ * If `false`, coordinates will be oriented according to the left-hand rule
+ * (clockwise for exterior and counter-clockwise for interior rings).
+ * By default, coordinate orientation will depend on how the geometry was
+ * constructed.
+ * @return {Array.<Array.<ol.Coordinate>>} Coordinates.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.getCoordinates = function(opt_right) {
+ var flatCoordinates;
+ if (opt_right !== undefined) {
+ flatCoordinates = this.getOrientedFlatCoordinates().slice();
+ ol.geom.flat.orient.orientLinearRings(
+ flatCoordinates, 0, this.ends_, this.stride, opt_right);
+ } else {
+ flatCoordinates = this.flatCoordinates;
+ }
+
+ return ol.geom.flat.inflate.coordinatess(
+ flatCoordinates, 0, this.ends_, this.stride);
+};
+
+
+/**
+ * @return {Array.<number>} Ends.
+ */
+ol.geom.Polygon.prototype.getEnds = function() {
+ return this.ends_;
+};
+
+
+/**
+ * @return {Array.<number>} Interior point.
+ */
+ol.geom.Polygon.prototype.getFlatInteriorPoint = function() {
+ if (this.flatInteriorPointRevision_ != this.getRevision()) {
+ var flatCenter = ol.extent.getCenter(this.getExtent());
+ this.flatInteriorPoint_ = ol.geom.flat.interiorpoint.linearRings(
+ this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride,
+ flatCenter, 0);
+ this.flatInteriorPointRevision_ = this.getRevision();
+ }
+ return this.flatInteriorPoint_;
+};
+
+
+/**
+ * Return an interior point of the polygon.
+ * @return {ol.geom.Point} Interior point.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.getInteriorPoint = function() {
+ return new ol.geom.Point(this.getFlatInteriorPoint());
+};
+
+
+/**
+ * Return the number of rings of the polygon, this includes the exterior
+ * ring and any interior rings.
+ *
+ * @return {number} Number of rings.
+ * @api
+ */
+ol.geom.Polygon.prototype.getLinearRingCount = function() {
+ return this.ends_.length;
+};
+
+
+/**
+ * Return the Nth linear ring of the polygon geometry. Return `null` if the
+ * given index is out of range.
+ * The exterior linear ring is available at index `0` and the interior rings
+ * at index `1` and beyond.
+ *
+ * @param {number} index Index.
+ * @return {ol.geom.LinearRing} Linear ring.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.getLinearRing = function(index) {
+ ol.DEBUG && console.assert(0 <= index && index < this.ends_.length,
+ 'index should be in between 0 and and length of this.ends_');
+ if (index < 0 || this.ends_.length <= index) {
+ return null;
+ }
+ var linearRing = new ol.geom.LinearRing(null);
+ linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice(
+ index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]));
+ return linearRing;
+};
+
+
+/**
+ * Return the linear rings of the polygon.
+ * @return {Array.<ol.geom.LinearRing>} Linear rings.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.getLinearRings = function() {
+ var layout = this.layout;
+ var flatCoordinates = this.flatCoordinates;
+ var ends = this.ends_;
+ var linearRings = [];
+ var offset = 0;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ var linearRing = new ol.geom.LinearRing(null);
+ linearRing.setFlatCoordinates(layout, flatCoordinates.slice(offset, end));
+ linearRings.push(linearRing);
+ offset = end;
+ }
+ return linearRings;
+};
+
+
+/**
+ * @return {Array.<number>} Oriented flat coordinates.
+ */
+ol.geom.Polygon.prototype.getOrientedFlatCoordinates = function() {
+ if (this.orientedRevision_ != this.getRevision()) {
+ var flatCoordinates = this.flatCoordinates;
+ if (ol.geom.flat.orient.linearRingsAreOriented(
+ flatCoordinates, 0, this.ends_, this.stride)) {
+ this.orientedFlatCoordinates_ = flatCoordinates;
+ } else {
+ this.orientedFlatCoordinates_ = flatCoordinates.slice();
+ this.orientedFlatCoordinates_.length =
+ ol.geom.flat.orient.orientLinearRings(
+ this.orientedFlatCoordinates_, 0, this.ends_, this.stride);
+ }
+ this.orientedRevision_ = this.getRevision();
+ }
+ return this.orientedFlatCoordinates_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Polygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
+ var simplifiedFlatCoordinates = [];
+ var simplifiedEnds = [];
+ simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizes(
+ this.flatCoordinates, 0, this.ends_, this.stride,
+ Math.sqrt(squaredTolerance),
+ simplifiedFlatCoordinates, 0, simplifiedEnds);
+ var simplifiedPolygon = new ol.geom.Polygon(null);
+ simplifiedPolygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds);
+ return simplifiedPolygon;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.Polygon.prototype.getType = function() {
+ return ol.geom.GeometryType.POLYGON;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.Polygon.prototype.intersectsExtent = function(extent) {
+ return ol.geom.flat.intersectsextent.linearRings(
+ this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent);
+};
+
+
+/**
+ * Set the coordinates of the polygon.
+ * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.Polygon.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_);
+ } else {
+ this.setLayout(opt_layout, coordinates, 2);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ var ends = ol.geom.flat.deflate.coordinatess(
+ this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
+ this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {Array.<number>} ends Ends.
+ */
+ol.geom.Polygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) {
+ if (!flatCoordinates) {
+ ol.DEBUG && console.assert(ends && ends.length === 0,
+ 'ends must be an empty array');
+ } else if (ends.length === 0) {
+ ol.DEBUG && console.assert(flatCoordinates.length === 0,
+ 'flatCoordinates should be an empty array');
+ } else {
+ ol.DEBUG && console.assert(flatCoordinates.length == ends[ends.length - 1],
+ 'the length of flatCoordinates should be the last entry of ends');
+ }
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.ends_ = ends;
+ this.changed();
+};
+
+
+/**
+ * Create an approximation of a circle on the surface of a sphere.
+ * @param {ol.Sphere} sphere The sphere.
+ * @param {ol.Coordinate} center Center (`[lon, lat]` in degrees).
+ * @param {number} radius The great-circle distance from the center to
+ * the polygon vertices.
+ * @param {number=} opt_n Optional number of vertices for the resulting
+ * polygon. Default is `32`.
+ * @return {ol.geom.Polygon} The "circular" polygon.
+ * @api stable
+ */
+ol.geom.Polygon.circular = function(sphere, center, radius, opt_n) {
+ var n = opt_n ? opt_n : 32;
+ /** @type {Array.<number>} */
+ var flatCoordinates = [];
+ var i;
+ for (i = 0; i < n; ++i) {
+ ol.array.extend(
+ flatCoordinates, sphere.offset(center, radius, 2 * Math.PI * i / n));
+ }
+ flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]);
+ var polygon = new ol.geom.Polygon(null);
+ polygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
+ return polygon;
+};
+
+
+/**
+ * Create a polygon from an extent. The layout used is `XY`.
+ * @param {ol.Extent} extent The extent.
+ * @return {ol.geom.Polygon} The polygon.
+ * @api
+ */
+ol.geom.Polygon.fromExtent = function(extent) {
+ var minX = extent[0];
+ var minY = extent[1];
+ var maxX = extent[2];
+ var maxY = extent[3];
+ var flatCoordinates =
+ [minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY];
+ var polygon = new ol.geom.Polygon(null);
+ polygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
+ return polygon;
+};
+
+
+/**
+ * Create a regular polygon from a circle.
+ * @param {ol.geom.Circle} circle Circle geometry.
+ * @param {number=} opt_sides Number of sides of the polygon. Default is 32.
+ * @param {number=} opt_angle Start angle for the first vertex of the polygon in
+ * radians. Default is 0.
+ * @return {ol.geom.Polygon} Polygon geometry.
+ * @api
+ */
+ol.geom.Polygon.fromCircle = function(circle, opt_sides, opt_angle) {
+ var sides = opt_sides ? opt_sides : 32;
+ var stride = circle.getStride();
+ var layout = circle.getLayout();
+ var polygon = new ol.geom.Polygon(null, layout);
+ var arrayLength = stride * (sides + 1);
+ var flatCoordinates = new Array(arrayLength);
+ for (var i = 0; i < arrayLength; i++) {
+ flatCoordinates[i] = 0;
+ }
+ var ends = [flatCoordinates.length];
+ polygon.setFlatCoordinates(layout, flatCoordinates, ends);
+ ol.geom.Polygon.makeRegular(
+ polygon, circle.getCenter(), circle.getRadius(), opt_angle);
+ return polygon;
+};
+
+
+/**
+ * Modify the coordinates of a polygon to make it a regular polygon.
+ * @param {ol.geom.Polygon} polygon Polygon geometry.
+ * @param {ol.Coordinate} center Center of the regular polygon.
+ * @param {number} radius Radius of the regular polygon.
+ * @param {number=} opt_angle Start angle for the first vertex of the polygon in
+ * radians. Default is 0.
+ */
+ol.geom.Polygon.makeRegular = function(polygon, center, radius, opt_angle) {
+ var flatCoordinates = polygon.getFlatCoordinates();
+ var layout = polygon.getLayout();
+ var stride = polygon.getStride();
+ var ends = polygon.getEnds();
+ ol.DEBUG && console.assert(ends.length === 1, 'only 1 ring is supported');
+ var sides = flatCoordinates.length / stride - 1;
+ var startAngle = opt_angle ? opt_angle : 0;
+ var angle, offset;
+ for (var i = 0; i <= sides; ++i) {
+ offset = i * stride;
+ angle = startAngle + (ol.math.modulo(i, sides) * 2 * Math.PI / sides);
+ flatCoordinates[offset] = center[0] + (radius * Math.cos(angle));
+ flatCoordinates[offset + 1] = center[1] + (radius * Math.sin(angle));
+ }
+ polygon.setFlatCoordinates(layout, flatCoordinates, ends);
+};
+
+goog.provide('ol.View');
+
+goog.require('ol');
+goog.require('ol.CenterConstraint');
+goog.require('ol.Constraints');
+goog.require('ol.Object');
+goog.require('ol.ResolutionConstraint');
+goog.require('ol.RotationConstraint');
+goog.require('ol.array');
+goog.require('ol.asserts');
+goog.require('ol.coordinate');
+goog.require('ol.easing');
+goog.require('ol.extent');
+goog.require('ol.geom.Polygon');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.proj');
+goog.require('ol.proj.Units');
+
+
+/**
+ * @classdesc
+ * An ol.View object represents a simple 2D view of the map.
+ *
+ * This is the object to act upon to change the center, resolution,
+ * and rotation of the map.
+ *
+ * ### The view states
+ *
+ * An `ol.View` is determined by three states: `center`, `resolution`,
+ * and `rotation`. Each state has a corresponding getter and setter, e.g.
+ * `getCenter` and `setCenter` for the `center` state.
+ *
+ * An `ol.View` has a `projection`. The projection determines the
+ * coordinate system of the center, and its units determine the units of the
+ * resolution (projection units per pixel). The default projection is
+ * Spherical Mercator (EPSG:3857).
+ *
+ * ### The constraints
+ *
+ * `setCenter`, `setResolution` and `setRotation` can be used to change the
+ * states of the view. Any value can be passed to the setters. And the value
+ * that is passed to a setter will effectively be the value set in the view,
+ * and returned by the corresponding getter.
+ *
+ * But an `ol.View` object also has a *resolution constraint*, a
+ * *rotation constraint* and a *center constraint*.
+ *
+ * As said above, no constraints are applied when the setters are used to set
+ * new states for the view. Applying constraints is done explicitly through
+ * the use of the `constrain*` functions (`constrainResolution` and
+ * `constrainRotation` and `constrainCenter`).
+ *
+ * The main users of the constraints are the interactions and the
+ * controls. For example, double-clicking on the map changes the view to
+ * the "next" resolution. And releasing the fingers after pinch-zooming
+ * snaps to the closest resolution (with an animation).
+ *
+ * The *resolution constraint* snaps to specific resolutions. It is
+ * determined by the following options: `resolutions`, `maxResolution`,
+ * `maxZoom`, and `zoomFactor`. If `resolutions` is set, the other three
+ * options are ignored. See documentation for each option for more
+ * information.
+ *
+ * The *rotation constraint* snaps to specific angles. It is determined
+ * by the following options: `enableRotation` and `constrainRotation`.
+ * By default the rotation value is snapped to zero when approaching the
+ * horizontal.
+ *
+ * The *center constraint* is determined by the `extent` option. By
+ * default the center is not constrained at all.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {olx.ViewOptions=} opt_options View options.
+ * @api stable
+ */
+ol.View = function(opt_options) {
+ ol.Object.call(this);
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.hints_ = [0, 0];
+
+ /**
+ * @private
+ * @type {Array.<Array.<ol.ViewAnimation>>}
+ */
+ this.animations_ = [];
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.updateAnimationKey_;
+
+ this.updateAnimations_ = this.updateAnimations_.bind(this);
+
+ /**
+ * @type {Object.<string, *>}
+ */
+ var properties = {};
+ properties[ol.View.Property.CENTER] = options.center !== undefined ?
+ options.center : null;
+
+ /**
+ * @private
+ * @const
+ * @type {ol.proj.Projection}
+ */
+ this.projection_ = ol.proj.createProjection(options.projection, 'EPSG:3857');
+
+ var resolutionConstraintInfo = ol.View.createResolutionConstraint_(
+ options);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxResolution_ = resolutionConstraintInfo.maxResolution;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.minResolution_ = resolutionConstraintInfo.minResolution;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.zoomFactor_ = resolutionConstraintInfo.zoomFactor;
+
+ /**
+ * @private
+ * @type {Array.<number>|undefined}
+ */
+ this.resolutions_ = options.resolutions;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.minZoom_ = resolutionConstraintInfo.minZoom;
+
+ var centerConstraint = ol.View.createCenterConstraint_(options);
+ var resolutionConstraint = resolutionConstraintInfo.constraint;
+ var rotationConstraint = ol.View.createRotationConstraint_(options);
+
+ /**
+ * @private
+ * @type {ol.Constraints}
+ */
+ this.constraints_ = new ol.Constraints(
+ centerConstraint, resolutionConstraint, rotationConstraint);
+
+ if (options.resolution !== undefined) {
+ properties[ol.View.Property.RESOLUTION] = options.resolution;
+ } else if (options.zoom !== undefined) {
+ properties[ol.View.Property.RESOLUTION] = this.constrainResolution(
+ this.maxResolution_, options.zoom - this.minZoom_);
+ }
+ properties[ol.View.Property.ROTATION] =
+ options.rotation !== undefined ? options.rotation : 0;
+ this.setProperties(properties);
+};
+ol.inherits(ol.View, ol.Object);
+
+
+/**
+ * Animate the view. The view's center, zoom (or resolution), and rotation
+ * can be animated for smooth transitions between view states. For example,
+ * to animate the view to a new zoom level:
+ *
+ * view.animate({zoom: view.getZoom() + 1});
+ *
+ * By default, the animation lasts one second and uses in-and-out easing. You
+ * can customize this behavior by including `duration` (in milliseconds) and
+ * `easing` options (see {@link ol.easing}).
+ *
+ * To chain together multiple animations, call the method with multiple
+ * animation objects. For example, to first zoom and then pan:
+ *
+ * view.animate({zoom: 10}, {center: [0, 0]});
+ *
+ * If you provide a function as the last argument to the animate method, it
+ * will get called at the end of an animation series. The callback will be
+ * called with `true` if the animation series completed on its own or `false`
+ * if it was cancelled.
+ *
+ * Animations are cancelled by user interactions (e.g. dragging the map) or by
+ * calling `view.setCenter()`, `view.setResolution()`, or `view.setRotation()`
+ * (or another method that calls one of these).
+ *
+ * @param {...(olx.AnimationOptions|function(boolean))} var_args Animation
+ * options. Multiple animations can be run in series by passing multiple
+ * options objects. To run multiple animations in parallel, call the method
+ * multiple times. An optional callback can be provided as a final
+ * argument. The callback will be called with a boolean indicating whether
+ * the animation completed without being cancelled.
+ * @api
+ */
+ol.View.prototype.animate = function(var_args) {
+ var start = Date.now();
+ var center = this.getCenter().slice();
+ var resolution = this.getResolution();
+ var rotation = this.getRotation();
+ var animationCount = arguments.length;
+ var callback;
+ if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') {
+ callback = arguments[animationCount - 1];
+ --animationCount;
+ }
+ var series = [];
+ for (var i = 0; i < animationCount; ++i) {
+ var options = /** @type olx.AnimationOptions */ (arguments[i]);
+
+ var animation = /** @type {ol.ViewAnimation} */ ({
+ start: start,
+ complete: false,
+ anchor: options.anchor,
+ duration: options.duration !== undefined ? options.duration : 1000,
+ easing: options.easing || ol.easing.inAndOut
+ });
+
+ if (options.center) {
+ animation.sourceCenter = center;
+ animation.targetCenter = options.center;
+ center = animation.targetCenter;
+ }
+
+ if (options.zoom !== undefined) {
+ animation.sourceResolution = resolution;
+ animation.targetResolution = this.constrainResolution(
+ this.maxResolution_, options.zoom - this.minZoom_, 0);
+ resolution = animation.targetResolution;
+ } else if (options.resolution) {
+ animation.sourceResolution = resolution;
+ animation.targetResolution = options.resolution;
+ resolution = animation.targetResolution;
+ }
+
+ if (options.rotation !== undefined) {
+ animation.sourceRotation = rotation;
+ animation.targetRotation = options.rotation;
+ rotation = animation.targetRotation;
+ }
+
+ animation.callback = callback;
+ start += animation.duration;
+ series.push(animation);
+ }
+ this.animations_.push(series);
+ this.setHint(ol.View.Hint.ANIMATING, 1);
+ this.updateAnimations_();
+};
+
+
+/**
+ * Determine if the view is being animated.
+ * @return {boolean} The view is being animated.
+ */
+ol.View.prototype.getAnimating = function() {
+ return this.getHints()[ol.View.Hint.ANIMATING] > 0;
+};
+
+
+/**
+ * Cancel any ongoing animations.
+ */
+ol.View.prototype.cancelAnimations = function() {
+ this.setHint(ol.View.Hint.ANIMATING, -this.getHints()[ol.View.Hint.ANIMATING]);
+ for (var i = 0, ii = this.animations_.length; i < ii; ++i) {
+ var series = this.animations_[i];
+ if (series[0].callback) {
+ series[0].callback(false);
+ }
+ }
+ this.animations_.length = 0;
+};
+
+/**
+ * Update all animations.
+ */
+ol.View.prototype.updateAnimations_ = function() {
+ if (this.updateAnimationKey_ !== undefined) {
+ cancelAnimationFrame(this.updateAnimationKey_);
+ this.updateAnimationKey_ = undefined;
+ }
+ if (!this.getAnimating()) {
+ return;
+ }
+ var now = Date.now();
+ var more = false;
+ for (var i = this.animations_.length - 1; i >= 0; --i) {
+ var series = this.animations_[i];
+ var seriesComplete = true;
+ for (var j = 0, jj = series.length; j < jj; ++j) {
+ var animation = series[j];
+ if (animation.complete) {
+ continue;
+ }
+ var elapsed = now - animation.start;
+ var fraction = animation.duration > 0 ? elapsed / animation.duration : 1;
+ if (fraction >= 1) {
+ animation.complete = true;
+ fraction = 1;
+ } else {
+ seriesComplete = false;
+ }
+ var progress = animation.easing(fraction);
+ if (animation.sourceCenter) {
+ var x0 = animation.sourceCenter[0];
+ var y0 = animation.sourceCenter[1];
+ var x1 = animation.targetCenter[0];
+ var y1 = animation.targetCenter[1];
+ var x = x0 + progress * (x1 - x0);
+ var y = y0 + progress * (y1 - y0);
+ this.set(ol.View.Property.CENTER, [x, y]);
+ }
+ if (animation.sourceResolution) {
+ var resolution = animation.sourceResolution +
+ progress * (animation.targetResolution - animation.sourceResolution);
+ if (animation.anchor) {
+ this.set(ol.View.Property.CENTER,
+ this.calculateCenterZoom(resolution, animation.anchor));
+ }
+ this.set(ol.View.Property.RESOLUTION, resolution);
+ }
+ if (animation.sourceRotation !== undefined) {
+ var rotation = animation.sourceRotation +
+ progress * (animation.targetRotation - animation.sourceRotation);
+ if (animation.anchor) {
+ this.set(ol.View.Property.CENTER,
+ this.calculateCenterRotate(rotation, animation.anchor));
+ }
+ this.set(ol.View.Property.ROTATION, rotation);
+ }
+ more = true;
+ if (!animation.complete) {
+ break;
+ }
+ }
+ if (seriesComplete) {
+ this.animations_[i] = null;
+ this.setHint(ol.View.Hint.ANIMATING, -1);
+ var callback = series[0].callback;
+ if (callback) {
+ callback(true);
+ }
+ }
+ }
+ // prune completed series
+ this.animations_ = this.animations_.filter(Boolean);
+ if (more && this.updateAnimationKey_ === undefined) {
+ this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_);
+ }
+};
+
+/**
+ * @param {number} rotation Target rotation.
+ * @param {ol.Coordinate} anchor Rotation anchor.
+ * @return {ol.Coordinate|undefined} Center for rotation and anchor.
+ */
+ol.View.prototype.calculateCenterRotate = function(rotation, anchor) {
+ var center;
+ var currentCenter = this.getCenter();
+ if (currentCenter !== undefined) {
+ center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]];
+ ol.coordinate.rotate(center, rotation - this.getRotation());
+ ol.coordinate.add(center, anchor);
+ }
+ return center;
+};
+
+
+/**
+ * @param {number} resolution Target resolution.
+ * @param {ol.Coordinate} anchor Zoom anchor.
+ * @return {ol.Coordinate|undefined} Center for resolution and anchor.
+ */
+ol.View.prototype.calculateCenterZoom = function(resolution, anchor) {
+ var center;
+ var currentCenter = this.getCenter();
+ var currentResolution = this.getResolution();
+ if (currentCenter !== undefined && currentResolution !== undefined) {
+ var x = anchor[0] -
+ resolution * (anchor[0] - currentCenter[0]) / currentResolution;
+ var y = anchor[1] -
+ resolution * (anchor[1] - currentCenter[1]) / currentResolution;
+ center = [x, y];
+ }
+ return center;
+};
+
+
+/**
+ * Get the constrained center of this view.
+ * @param {ol.Coordinate|undefined} center Center.
+ * @return {ol.Coordinate|undefined} Constrained center.
+ * @api
+ */
+ol.View.prototype.constrainCenter = function(center) {
+ return this.constraints_.center(center);
+};
+
+
+/**
+ * Get the constrained resolution of this view.
+ * @param {number|undefined} resolution Resolution.
+ * @param {number=} opt_delta Delta. Default is `0`.
+ * @param {number=} opt_direction Direction. Default is `0`.
+ * @return {number|undefined} Constrained resolution.
+ * @api
+ */
+ol.View.prototype.constrainResolution = function(
+ resolution, opt_delta, opt_direction) {
+ var delta = opt_delta || 0;
+ var direction = opt_direction || 0;
+ return this.constraints_.resolution(resolution, delta, direction);
+};
+
+
+/**
+ * Get the constrained rotation of this view.
+ * @param {number|undefined} rotation Rotation.
+ * @param {number=} opt_delta Delta. Default is `0`.
+ * @return {number|undefined} Constrained rotation.
+ * @api
+ */
+ol.View.prototype.constrainRotation = function(rotation, opt_delta) {
+ var delta = opt_delta || 0;
+ return this.constraints_.rotation(rotation, delta);
+};
+
+
+/**
+ * Get the view center.
+ * @return {ol.Coordinate|undefined} The center of the view.
+ * @observable
+ * @api stable
+ */
+ol.View.prototype.getCenter = function() {
+ return /** @type {ol.Coordinate|undefined} */ (
+ this.get(ol.View.Property.CENTER));
+};
+
+
+/**
+ * @param {Array.<number>=} opt_hints Destination array.
+ * @return {Array.<number>} Hint.
+ */
+ol.View.prototype.getHints = function(opt_hints) {
+ if (opt_hints !== undefined) {
+ opt_hints[0] = this.hints_[0];
+ opt_hints[1] = this.hints_[1];
+ return opt_hints;
+ } else {
+ return this.hints_.slice();
+ }
+};
+
+
+/**
+ * Calculate the extent for the current view state and the passed size.
+ * The size is the pixel dimensions of the box into which the calculated extent
+ * should fit. In most cases you want to get the extent of the entire map,
+ * that is `map.getSize()`.
+ * @param {ol.Size} size Box pixel size.
+ * @return {ol.Extent} Extent.
+ * @api stable
+ */
+ol.View.prototype.calculateExtent = function(size) {
+ var center = /** @type {!ol.Coordinate} */ (this.getCenter());
+ ol.asserts.assert(center, 1); // The view center is not defined
+ var resolution = /** @type {!number} */ (this.getResolution());
+ ol.asserts.assert(resolution !== undefined, 2); // The view resolution is not defined
+ var rotation = /** @type {!number} */ (this.getRotation());
+ ol.asserts.assert(rotation !== undefined, 3); // The view rotation is not defined
+
+ return ol.extent.getForViewAndSize(center, resolution, rotation, size);
+};
+
+
+/**
+ * Get the maximum resolution of the view.
+ * @return {number} The maximum resolution of the view.
+ * @api
+ */
+ol.View.prototype.getMaxResolution = function() {
+ return this.maxResolution_;
+};
+
+
+/**
+ * Get the minimum resolution of the view.
+ * @return {number} The minimum resolution of the view.
+ * @api
+ */
+ol.View.prototype.getMinResolution = function() {
+ return this.minResolution_;
+};
+
+
+/**
+ * Get the view projection.
+ * @return {ol.proj.Projection} The projection of the view.
+ * @api stable
+ */
+ol.View.prototype.getProjection = function() {
+ return this.projection_;
+};
+
+
+/**
+ * Get the view resolution.
+ * @return {number|undefined} The resolution of the view.
+ * @observable
+ * @api stable
+ */
+ol.View.prototype.getResolution = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.View.Property.RESOLUTION));
+};
+
+
+/**
+ * Get the resolutions for the view. This returns the array of resolutions
+ * passed to the constructor of the {ol.View}, or undefined if none were given.
+ * @return {Array.<number>|undefined} The resolutions of the view.
+ * @api stable
+ */
+ol.View.prototype.getResolutions = function() {
+ return this.resolutions_;
+};
+
+
+/**
+ * Get the resolution for a provided extent (in map units) and size (in pixels).
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Size} size Box pixel size.
+ * @return {number} The resolution at which the provided extent will render at
+ * the given size.
+ */
+ol.View.prototype.getResolutionForExtent = function(extent, size) {
+ var xResolution = ol.extent.getWidth(extent) / size[0];
+ var yResolution = ol.extent.getHeight(extent) / size[1];
+ return Math.max(xResolution, yResolution);
+};
+
+
+/**
+ * Return a function that returns a value between 0 and 1 for a
+ * resolution. Exponential scaling is assumed.
+ * @param {number=} opt_power Power.
+ * @return {function(number): number} Resolution for value function.
+ */
+ol.View.prototype.getResolutionForValueFunction = function(opt_power) {
+ var power = opt_power || 2;
+ var maxResolution = this.maxResolution_;
+ var minResolution = this.minResolution_;
+ var max = Math.log(maxResolution / minResolution) / Math.log(power);
+ return (
+ /**
+ * @param {number} value Value.
+ * @return {number} Resolution.
+ */
+ function(value) {
+ var resolution = maxResolution / Math.pow(power, value * max);
+ ol.DEBUG && console.assert(resolution >= minResolution &&
+ resolution <= maxResolution,
+ 'calculated resolution outside allowed bounds (%s <= %s <= %s)',
+ minResolution, resolution, maxResolution);
+ return resolution;
+ });
+};
+
+
+/**
+ * Get the view rotation.
+ * @return {number} The rotation of the view in radians.
+ * @observable
+ * @api stable
+ */
+ol.View.prototype.getRotation = function() {
+ return /** @type {number} */ (this.get(ol.View.Property.ROTATION));
+};
+
+
+/**
+ * Return a function that returns a resolution for a value between
+ * 0 and 1. Exponential scaling is assumed.
+ * @param {number=} opt_power Power.
+ * @return {function(number): number} Value for resolution function.
+ */
+ol.View.prototype.getValueForResolutionFunction = function(opt_power) {
+ var power = opt_power || 2;
+ var maxResolution = this.maxResolution_;
+ var minResolution = this.minResolution_;
+ var max = Math.log(maxResolution / minResolution) / Math.log(power);
+ return (
+ /**
+ * @param {number} resolution Resolution.
+ * @return {number} Value.
+ */
+ function(resolution) {
+ var value =
+ (Math.log(maxResolution / resolution) / Math.log(power)) / max;
+ ol.DEBUG && console.assert(value >= 0 && value <= 1,
+ 'calculated value (%s) ouside allowed range (0-1)', value);
+ return value;
+ });
+};
+
+
+/**
+ * @return {olx.ViewState} View state.
+ */
+ol.View.prototype.getState = function() {
+ ol.DEBUG && console.assert(this.isDef(),
+ 'the view was not defined (had no center and/or resolution)');
+ var center = /** @type {ol.Coordinate} */ (this.getCenter());
+ var projection = this.getProjection();
+ var resolution = /** @type {number} */ (this.getResolution());
+ var rotation = this.getRotation();
+ return /** @type {olx.ViewState} */ ({
+ center: center.slice(),
+ projection: projection !== undefined ? projection : null,
+ resolution: resolution,
+ rotation: rotation
+ });
+};
+
+
+/**
+ * Get the current zoom level. Return undefined if the current
+ * resolution is undefined or not within the "resolution constraints".
+ * @return {number|undefined} Zoom.
+ * @api stable
+ */
+ol.View.prototype.getZoom = function() {
+ var zoom;
+ var resolution = this.getResolution();
+ if (resolution !== undefined &&
+ resolution >= this.minResolution_ && resolution <= this.maxResolution_) {
+ var offset = this.minZoom_ || 0;
+ var max, zoomFactor;
+ if (this.resolutions_) {
+ var nearest = ol.array.linearFindNearest(this.resolutions_, resolution, 1);
+ offset += nearest;
+ if (nearest == this.resolutions_.length - 1) {
+ return offset;
+ }
+ max = this.resolutions_[nearest];
+ zoomFactor = max / this.resolutions_[nearest + 1];
+ } else {
+ max = this.maxResolution_;
+ zoomFactor = this.zoomFactor_;
+ }
+ zoom = offset + Math.log(max / resolution) / Math.log(zoomFactor);
+ }
+ return zoom;
+};
+
+
+/**
+ * Fit the given geometry or extent based on the given map size and border.
+ * The size is pixel dimensions of the box to fit the extent into.
+ * In most cases you will want to use the map size, that is `map.getSize()`.
+ * Takes care of the map angle.
+ * @param {ol.geom.SimpleGeometry|ol.Extent} geometry Geometry.
+ * @param {ol.Size} size Box pixel size.
+ * @param {olx.view.FitOptions=} opt_options Options.
+ * @api
+ */
+ol.View.prototype.fit = function(geometry, size, opt_options) {
+ if (!(geometry instanceof ol.geom.SimpleGeometry)) {
+ ol.asserts.assert(Array.isArray(geometry),
+ 24); // Invalid extent or geometry provided as `geometry`
+ ol.asserts.assert(!ol.extent.isEmpty(geometry),
+ 25); // Cannot fit empty extent provided as `geometry`
+ geometry = ol.geom.Polygon.fromExtent(geometry);
+ }
+
+ var options = opt_options || {};
+
+ var padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0];
+ var constrainResolution = options.constrainResolution !== undefined ?
+ options.constrainResolution : true;
+ var nearest = options.nearest !== undefined ? options.nearest : false;
+ var minResolution;
+ if (options.minResolution !== undefined) {
+ minResolution = options.minResolution;
+ } else if (options.maxZoom !== undefined) {
+ minResolution = this.constrainResolution(
+ this.maxResolution_, options.maxZoom - this.minZoom_, 0);
+ } else {
+ minResolution = 0;
+ }
+ var coords = geometry.getFlatCoordinates();
+
+ // calculate rotated extent
+ var rotation = this.getRotation();
+ ol.DEBUG && console.assert(rotation !== undefined, 'rotation was not defined');
+ var cosAngle = Math.cos(-rotation);
+ var sinAngle = Math.sin(-rotation);
+ var minRotX = +Infinity;
+ var minRotY = +Infinity;
+ var maxRotX = -Infinity;
+ var maxRotY = -Infinity;
+ var stride = geometry.getStride();
+ for (var i = 0, ii = coords.length; i < ii; i += stride) {
+ var rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle;
+ var rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle;
+ minRotX = Math.min(minRotX, rotX);
+ minRotY = Math.min(minRotY, rotY);
+ maxRotX = Math.max(maxRotX, rotX);
+ maxRotY = Math.max(maxRotY, rotY);
+ }
+
+ // calculate resolution
+ var resolution = this.getResolutionForExtent(
+ [minRotX, minRotY, maxRotX, maxRotY],
+ [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
+ resolution = isNaN(resolution) ? minResolution :
+ Math.max(resolution, minResolution);
+ if (constrainResolution) {
+ var constrainedResolution = this.constrainResolution(resolution, 0, 0);
+ if (!nearest && constrainedResolution < resolution) {
+ constrainedResolution = this.constrainResolution(
+ constrainedResolution, -1, 0);
+ }
+ resolution = constrainedResolution;
+ }
+
+ // calculate center
+ sinAngle = -sinAngle; // go back to original rotation
+ var centerRotX = (minRotX + maxRotX) / 2;
+ var centerRotY = (minRotY + maxRotY) / 2;
+ centerRotX += (padding[1] - padding[3]) / 2 * resolution;
+ centerRotY += (padding[0] - padding[2]) / 2 * resolution;
+ var centerX = centerRotX * cosAngle - centerRotY * sinAngle;
+ var centerY = centerRotY * cosAngle + centerRotX * sinAngle;
+ var center = [centerX, centerY];
+
+ if (options.duration !== undefined) {
+ this.animate({
+ resolution: resolution,
+ center: center,
+ duration: options.duration,
+ easing: options.easing
+ });
+ } else {
+ this.setResolution(resolution);
+ this.setCenter(center);
+ }
+};
+
+
+/**
+ * Center on coordinate and view position.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.Size} size Box pixel size.
+ * @param {ol.Pixel} position Position on the view to center on.
+ * @api
+ */
+ol.View.prototype.centerOn = function(coordinate, size, position) {
+ // calculate rotated position
+ var rotation = this.getRotation();
+ var cosAngle = Math.cos(-rotation);
+ var sinAngle = Math.sin(-rotation);
+ var rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
+ var rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
+ var resolution = this.getResolution();
+ rotX += (size[0] / 2 - position[0]) * resolution;
+ rotY += (position[1] - size[1] / 2) * resolution;
+
+ // go back to original angle
+ sinAngle = -sinAngle; // go back to original rotation
+ var centerX = rotX * cosAngle - rotY * sinAngle;
+ var centerY = rotY * cosAngle + rotX * sinAngle;
+
+ this.setCenter([centerX, centerY]);
+};
+
+
+/**
+ * @return {boolean} Is defined.
+ */
+ol.View.prototype.isDef = function() {
+ return !!this.getCenter() && this.getResolution() !== undefined;
+};
+
+
+/**
+ * Rotate the view around a given coordinate.
+ * @param {number} rotation New rotation value for the view.
+ * @param {ol.Coordinate=} opt_anchor The rotation center.
+ * @api stable
+ */
+ol.View.prototype.rotate = function(rotation, opt_anchor) {
+ if (opt_anchor !== undefined) {
+ var center = this.calculateCenterRotate(rotation, opt_anchor);
+ this.setCenter(center);
+ }
+ this.setRotation(rotation);
+};
+
+
+/**
+ * Set the center of the current view.
+ * @param {ol.Coordinate|undefined} center The center of the view.
+ * @observable
+ * @api stable
+ */
+ol.View.prototype.setCenter = function(center) {
+ this.set(ol.View.Property.CENTER, center);
+ if (this.getAnimating()) {
+ this.cancelAnimations();
+ }
+};
+
+
+/**
+ * @param {ol.View.Hint} hint Hint.
+ * @param {number} delta Delta.
+ * @return {number} New value.
+ */
+ol.View.prototype.setHint = function(hint, delta) {
+ ol.DEBUG && console.assert(0 <= hint && hint < this.hints_.length,
+ 'illegal hint (%s), must be between 0 and %s', hint, this.hints_.length);
+ this.hints_[hint] += delta;
+ ol.DEBUG && console.assert(this.hints_[hint] >= 0,
+ 'Hint at %s must be positive, was %s', hint, this.hints_[hint]);
+ this.changed();
+ return this.hints_[hint];
+};
+
+
+/**
+ * Set the resolution for this view.
+ * @param {number|undefined} resolution The resolution of the view.
+ * @observable
+ * @api stable
+ */
+ol.View.prototype.setResolution = function(resolution) {
+ this.set(ol.View.Property.RESOLUTION, resolution);
+ if (this.getAnimating()) {
+ this.cancelAnimations();
+ }
+};
+
+
+/**
+ * Set the rotation for this view.
+ * @param {number} rotation The rotation of the view in radians.
+ * @observable
+ * @api stable
+ */
+ol.View.prototype.setRotation = function(rotation) {
+ this.set(ol.View.Property.ROTATION, rotation);
+ if (this.getAnimating()) {
+ this.cancelAnimations();
+ }
+};
+
+
+/**
+ * Zoom to a specific zoom level.
+ * @param {number} zoom Zoom level.
+ * @api stable
+ */
+ol.View.prototype.setZoom = function(zoom) {
+ var resolution = this.constrainResolution(
+ this.maxResolution_, zoom - this.minZoom_, 0);
+ this.setResolution(resolution);
+};
+
+
+/**
+ * @param {olx.ViewOptions} options View options.
+ * @private
+ * @return {ol.CenterConstraintType} The constraint.
+ */
+ol.View.createCenterConstraint_ = function(options) {
+ if (options.extent !== undefined) {
+ return ol.CenterConstraint.createExtent(options.extent);
+ } else {
+ return ol.CenterConstraint.none;
+ }
+};
+
+
+/**
+ * @private
+ * @param {olx.ViewOptions} options View options.
+ * @return {{constraint: ol.ResolutionConstraintType, maxResolution: number,
+ * minResolution: number, zoomFactor: number}} The constraint.
+ */
+ol.View.createResolutionConstraint_ = function(options) {
+ var resolutionConstraint;
+ var maxResolution;
+ var minResolution;
+
+ // TODO: move these to be ol constants
+ // see https://github.com/openlayers/ol3/issues/2076
+ var defaultMaxZoom = 28;
+ var defaultZoomFactor = 2;
+
+ var minZoom = options.minZoom !== undefined ?
+ options.minZoom : ol.DEFAULT_MIN_ZOOM;
+
+ var maxZoom = options.maxZoom !== undefined ?
+ options.maxZoom : defaultMaxZoom;
+
+ var zoomFactor = options.zoomFactor !== undefined ?
+ options.zoomFactor : defaultZoomFactor;
+
+ if (options.resolutions !== undefined) {
+ var resolutions = options.resolutions;
+ maxResolution = resolutions[0];
+ minResolution = resolutions[resolutions.length - 1];
+ resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions(
+ resolutions);
+ } else {
+ // calculate the default min and max resolution
+ var projection = ol.proj.createProjection(options.projection, 'EPSG:3857');
+ var extent = projection.getExtent();
+ var size = !extent ?
+ // use an extent that can fit the whole world if need be
+ 360 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] /
+ projection.getMetersPerUnit() :
+ Math.max(ol.extent.getWidth(extent), ol.extent.getHeight(extent));
+
+ var defaultMaxResolution = size / ol.DEFAULT_TILE_SIZE / Math.pow(
+ defaultZoomFactor, ol.DEFAULT_MIN_ZOOM);
+
+ var defaultMinResolution = defaultMaxResolution / Math.pow(
+ defaultZoomFactor, defaultMaxZoom - ol.DEFAULT_MIN_ZOOM);
+
+ // user provided maxResolution takes precedence
+ maxResolution = options.maxResolution;
+ if (maxResolution !== undefined) {
+ minZoom = 0;
+ } else {
+ maxResolution = defaultMaxResolution / Math.pow(zoomFactor, minZoom);
+ }
+
+ // user provided minResolution takes precedence
+ minResolution = options.minResolution;
+ if (minResolution === undefined) {
+ if (options.maxZoom !== undefined) {
+ if (options.maxResolution !== undefined) {
+ minResolution = maxResolution / Math.pow(zoomFactor, maxZoom);
+ } else {
+ minResolution = defaultMaxResolution / Math.pow(zoomFactor, maxZoom);
+ }
+ } else {
+ minResolution = defaultMinResolution;
+ }
+ }
+
+ // given discrete zoom levels, minResolution may be different than provided
+ maxZoom = minZoom + Math.floor(
+ Math.log(maxResolution / minResolution) / Math.log(zoomFactor));
+ minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom);
+
+ resolutionConstraint = ol.ResolutionConstraint.createSnapToPower(
+ zoomFactor, maxResolution, maxZoom - minZoom);
+ }
+ return {constraint: resolutionConstraint, maxResolution: maxResolution,
+ minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor};
+};
+
+
+/**
+ * @private
+ * @param {olx.ViewOptions} options View options.
+ * @return {ol.RotationConstraintType} Rotation constraint.
+ */
+ol.View.createRotationConstraint_ = function(options) {
+ var enableRotation = options.enableRotation !== undefined ?
+ options.enableRotation : true;
+ if (enableRotation) {
+ var constrainRotation = options.constrainRotation;
+ if (constrainRotation === undefined || constrainRotation === true) {
+ return ol.RotationConstraint.createSnapToZero();
+ } else if (constrainRotation === false) {
+ return ol.RotationConstraint.none;
+ } else if (typeof constrainRotation === 'number') {
+ return ol.RotationConstraint.createSnapToN(constrainRotation);
+ } else {
+ ol.DEBUG && console.assert(false,
+ 'illegal option for constrainRotation (%s)', constrainRotation);
+ return ol.RotationConstraint.none;
+ }
+ } else {
+ return ol.RotationConstraint.disable;
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.View.Property = {
+ CENTER: 'center',
+ RESOLUTION: 'resolution',
+ ROTATION: 'rotation'
+};
+
+
+/**
+ * @enum {number}
+ */
+ol.View.Hint = {
+ ANIMATING: 0,
+ INTERACTING: 1
+};
+
+goog.provide('ol.animation');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.coordinate');
+goog.require('ol.easing');
+
+
+/**
+ * Deprecated (use {@link ol.View#animate} instead).
+ * Generate an animated transition that will "bounce" the resolution as it
+ * approaches the final value.
+ * @param {olx.animation.BounceOptions} options Bounce options.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ * @api
+ */
+ol.animation.bounce = function(options) {
+ ol.DEBUG && console.warn('ol.animation.bounce() is deprecated. Use view.animate() instead.');
+ var resolution = options.resolution;
+ var start = options.start ? options.start : Date.now();
+ var duration = options.duration !== undefined ? options.duration : 1000;
+ var easing = options.easing ?
+ options.easing : ol.easing.upAndDown;
+ return (
+ /**
+ * @param {ol.Map} map Map.
+ * @param {?olx.FrameState} frameState Frame state.
+ * @return {boolean} Run this function in the next frame.
+ */
+ function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = easing((frameState.time - start) / duration);
+ var deltaResolution = resolution - frameState.viewState.resolution;
+ frameState.animate = true;
+ frameState.viewState.resolution += delta * deltaResolution;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else {
+ return false;
+ }
+ });
+};
+
+
+/**
+ * Deprecated (use {@link ol.View#animate} instead).
+ * Generate an animated transition while updating the view center.
+ * @param {olx.animation.PanOptions} options Pan options.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ * @api
+ */
+ol.animation.pan = function(options) {
+ ol.DEBUG && console.warn('ol.animation.pan() is deprecated. Use view.animate() instead.');
+ var source = options.source;
+ var start = options.start ? options.start : Date.now();
+ var sourceX = source[0];
+ var sourceY = source[1];
+ var duration = options.duration !== undefined ? options.duration : 1000;
+ var easing = options.easing ?
+ options.easing : ol.easing.inAndOut;
+ return (
+ /**
+ * @param {ol.Map} map Map.
+ * @param {?olx.FrameState} frameState Frame state.
+ * @return {boolean} Run this function in the next frame.
+ */
+ function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = 1 - easing((frameState.time - start) / duration);
+ var deltaX = sourceX - frameState.viewState.center[0];
+ var deltaY = sourceY - frameState.viewState.center[1];
+ frameState.animate = true;
+ frameState.viewState.center[0] += delta * deltaX;
+ frameState.viewState.center[1] += delta * deltaY;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else {
+ return false;
+ }
+ });
+};
+
+
+/**
+ * Deprecated (use {@link ol.View#animate} instead).
+ * Generate an animated transition while updating the view rotation.
+ * @param {olx.animation.RotateOptions} options Rotate options.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ * @api
+ */
+ol.animation.rotate = function(options) {
+ ol.DEBUG && console.warn('ol.animation.rotate() is deprecated. Use view.animate() instead.');
+ var sourceRotation = options.rotation ? options.rotation : 0;
+ var start = options.start ? options.start : Date.now();
+ var duration = options.duration !== undefined ? options.duration : 1000;
+ var easing = options.easing ?
+ options.easing : ol.easing.inAndOut;
+ var anchor = options.anchor ?
+ options.anchor : null;
+
+ return (
+ /**
+ * @param {ol.Map} map Map.
+ * @param {?olx.FrameState} frameState Frame state.
+ * @return {boolean} Run this function in the next frame.
+ */
+ function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = 1 - easing((frameState.time - start) / duration);
+ var deltaRotation =
+ (sourceRotation - frameState.viewState.rotation) * delta;
+ frameState.animate = true;
+ frameState.viewState.rotation += deltaRotation;
+ if (anchor) {
+ var center = frameState.viewState.center;
+ ol.coordinate.sub(center, anchor);
+ ol.coordinate.rotate(center, deltaRotation);
+ ol.coordinate.add(center, anchor);
+ }
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else {
+ return false;
+ }
+ });
+};
+
+
+/**
+ * Deprecated (use {@link ol.View#animate} instead).
+ * Generate an animated transition while updating the view resolution.
+ * @param {olx.animation.ZoomOptions} options Zoom options.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ * @api
+ */
+ol.animation.zoom = function(options) {
+ ol.DEBUG && console.warn('ol.animation.zoom() is deprecated. Use view.animate() instead.');
+ var sourceResolution = options.resolution;
+ var start = options.start ? options.start : Date.now();
+ var duration = options.duration !== undefined ? options.duration : 1000;
+ var easing = options.easing ?
+ options.easing : ol.easing.inAndOut;
+ return (
+ /**
+ * @param {ol.Map} map Map.
+ * @param {?olx.FrameState} frameState Frame state.
+ * @return {boolean} Run this function in the next frame.
+ */
+ function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = 1 - easing((frameState.time - start) / duration);
+ var deltaResolution =
+ sourceResolution - frameState.viewState.resolution;
+ frameState.animate = true;
+ frameState.viewState.resolution += delta * deltaResolution;
+ frameState.viewHints[ol.View.Hint.ANIMATING] += 1;
+ return true;
+ } else {
+ return false;
+ }
+ });
+};
+
+goog.provide('ol.TileRange');
+
+
+/**
+ * A representation of a contiguous block of tiles. A tile range is specified
+ * by its min/max tile coordinates and is inclusive of coordinates.
+ *
+ * @constructor
+ * @param {number} minX Minimum X.
+ * @param {number} maxX Maximum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxY Maximum Y.
+ * @struct
+ */
+ol.TileRange = function(minX, maxX, minY, maxY) {
+
+ /**
+ * @type {number}
+ */
+ this.minX = minX;
+
+ /**
+ * @type {number}
+ */
+ this.maxX = maxX;
+
+ /**
+ * @type {number}
+ */
+ this.minY = minY;
+
+ /**
+ * @type {number}
+ */
+ this.maxY = maxY;
+
+};
+
+
+/**
+ * @param {number} minX Minimum X.
+ * @param {number} maxX Maximum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxY Maximum Y.
+ * @param {ol.TileRange|undefined} tileRange TileRange.
+ * @return {ol.TileRange} Tile range.
+ */
+ol.TileRange.createOrUpdate = function(minX, maxX, minY, maxY, tileRange) {
+ if (tileRange !== undefined) {
+ tileRange.minX = minX;
+ tileRange.maxX = maxX;
+ tileRange.minY = minY;
+ tileRange.maxY = maxY;
+ return tileRange;
+ } else {
+ return new ol.TileRange(minX, maxX, minY, maxY);
+ }
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @return {boolean} Contains tile coordinate.
+ */
+ol.TileRange.prototype.contains = function(tileCoord) {
+ return this.containsXY(tileCoord[1], tileCoord[2]);
+};
+
+
+/**
+ * @param {ol.TileRange} tileRange Tile range.
+ * @return {boolean} Contains.
+ */
+ol.TileRange.prototype.containsTileRange = function(tileRange) {
+ return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX &&
+ this.minY <= tileRange.minY && tileRange.maxY <= this.maxY;
+};
+
+
+/**
+ * @param {number} x Tile coordinate x.
+ * @param {number} y Tile coordinate y.
+ * @return {boolean} Contains coordinate.
+ */
+ol.TileRange.prototype.containsXY = function(x, y) {
+ return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY;
+};
+
+
+/**
+ * @param {ol.TileRange} tileRange Tile range.
+ * @return {boolean} Equals.
+ */
+ol.TileRange.prototype.equals = function(tileRange) {
+ return this.minX == tileRange.minX && this.minY == tileRange.minY &&
+ this.maxX == tileRange.maxX && this.maxY == tileRange.maxY;
+};
+
+
+/**
+ * @param {ol.TileRange} tileRange Tile range.
+ */
+ol.TileRange.prototype.extend = function(tileRange) {
+ if (tileRange.minX < this.minX) {
+ this.minX = tileRange.minX;
+ }
+ if (tileRange.maxX > this.maxX) {
+ this.maxX = tileRange.maxX;
+ }
+ if (tileRange.minY < this.minY) {
+ this.minY = tileRange.minY;
+ }
+ if (tileRange.maxY > this.maxY) {
+ this.maxY = tileRange.maxY;
+ }
+};
+
+
+/**
+ * @return {number} Height.
+ */
+ol.TileRange.prototype.getHeight = function() {
+ return this.maxY - this.minY + 1;
+};
+
+
+/**
+ * @return {ol.Size} Size.
+ */
+ol.TileRange.prototype.getSize = function() {
+ return [this.getWidth(), this.getHeight()];
+};
+
+
+/**
+ * @return {number} Width.
+ */
+ol.TileRange.prototype.getWidth = function() {
+ return this.maxX - this.minX + 1;
+};
+
+
+/**
+ * @param {ol.TileRange} tileRange Tile range.
+ * @return {boolean} Intersects.
+ */
+ol.TileRange.prototype.intersects = function(tileRange) {
+ return this.minX <= tileRange.maxX &&
+ this.maxX >= tileRange.minX &&
+ this.minY <= tileRange.maxY &&
+ this.maxY >= tileRange.minY;
+};
+
+goog.provide('ol.size');
+
+
+/**
+ * Returns a buffered size.
+ * @param {ol.Size} size Size.
+ * @param {number} buffer Buffer.
+ * @param {ol.Size=} opt_size Optional reusable size array.
+ * @return {ol.Size} The buffered size.
+ */
+ol.size.buffer = function(size, buffer, opt_size) {
+ if (opt_size === undefined) {
+ opt_size = [0, 0];
+ }
+ opt_size[0] = size[0] + 2 * buffer;
+ opt_size[1] = size[1] + 2 * buffer;
+ return opt_size;
+};
+
+
+/**
+ * Determines if a size has a positive area.
+ * @param {ol.Size} size The size to test.
+ * @return {boolean} The size has a positive area.
+ */
+ol.size.hasArea = function(size) {
+ return size[0] > 0 && size[1] > 0;
+};
+
+
+/**
+ * Returns a size scaled by a ratio. The result will be an array of integers.
+ * @param {ol.Size} size Size.
+ * @param {number} ratio Ratio.
+ * @param {ol.Size=} opt_size Optional reusable size array.
+ * @return {ol.Size} The scaled size.
+ */
+ol.size.scale = function(size, ratio, opt_size) {
+ if (opt_size === undefined) {
+ opt_size = [0, 0];
+ }
+ opt_size[0] = (size[0] * ratio + 0.5) | 0;
+ opt_size[1] = (size[1] * ratio + 0.5) | 0;
+ return opt_size;
+};
+
+
+/**
+ * Returns an `ol.Size` array for the passed in number (meaning: square) or
+ * `ol.Size` array.
+ * (meaning: non-square),
+ * @param {number|ol.Size} size Width and height.
+ * @param {ol.Size=} opt_size Optional reusable size array.
+ * @return {ol.Size} Size.
+ * @api stable
+ */
+ol.size.toSize = function(size, opt_size) {
+ if (Array.isArray(size)) {
+ return size;
+ } else {
+ if (opt_size === undefined) {
+ opt_size = [size, size];
+ } else {
+ opt_size[0] = opt_size[1] = /** @type {number} */ (size);
+ }
+ return opt_size;
+ }
+};
+
+goog.provide('ol.tilecoord');
+
+
+/**
+ * @param {number} z Z.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {ol.TileCoord=} opt_tileCoord Tile coordinate.
+ * @return {ol.TileCoord} Tile coordinate.
+ */
+ol.tilecoord.createOrUpdate = function(z, x, y, opt_tileCoord) {
+ if (opt_tileCoord !== undefined) {
+ opt_tileCoord[0] = z;
+ opt_tileCoord[1] = x;
+ opt_tileCoord[2] = y;
+ return opt_tileCoord;
+ } else {
+ return [z, x, y];
+ }
+};
+
+
+/**
+ * @param {number} z Z.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {string} Key.
+ */
+ol.tilecoord.getKeyZXY = function(z, x, y) {
+ return z + '/' + x + '/' + y;
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coord.
+ * @return {number} Hash.
+ */
+ol.tilecoord.hash = function(tileCoord) {
+ return (tileCoord[1] << tileCoord[0]) + tileCoord[2];
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coord.
+ * @return {string} Quad key.
+ */
+ol.tilecoord.quadKey = function(tileCoord) {
+ var z = tileCoord[0];
+ var digits = new Array(z);
+ var mask = 1 << (z - 1);
+ var i, charCode;
+ for (i = 0; i < z; ++i) {
+ // 48 is charCode for 0 - '0'.charCodeAt(0)
+ charCode = 48;
+ if (tileCoord[1] & mask) {
+ charCode += 1;
+ }
+ if (tileCoord[2] & mask) {
+ charCode += 2;
+ }
+ digits[i] = String.fromCharCode(charCode);
+ mask >>= 1;
+ }
+ return digits.join('');
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @return {boolean} Tile coordinate is within extent and zoom level range.
+ */
+ol.tilecoord.withinExtentAndZ = function(tileCoord, tileGrid) {
+ var z = tileCoord[0];
+ var x = tileCoord[1];
+ var y = tileCoord[2];
+
+ if (tileGrid.getMinZoom() > z || z > tileGrid.getMaxZoom()) {
+ return false;
+ }
+ var extent = tileGrid.getExtent();
+ var tileRange;
+ if (!extent) {
+ tileRange = tileGrid.getFullTileRange(z);
+ } else {
+ tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
+ }
+ if (!tileRange) {
+ return true;
+ } else {
+ return tileRange.containsXY(x, y);
+ }
+};
+
+goog.provide('ol.tilegrid.TileGrid');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.TileRange');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.math');
+goog.require('ol.size');
+goog.require('ol.tilecoord');
+
+
+/**
+ * @classdesc
+ * Base class for setting the grid pattern for sources accessing tiled-image
+ * servers.
+ *
+ * @constructor
+ * @param {olx.tilegrid.TileGridOptions} options Tile grid options.
+ * @struct
+ * @api stable
+ */
+ol.tilegrid.TileGrid = function(options) {
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.minZoom = options.minZoom !== undefined ? options.minZoom : 0;
+
+ /**
+ * @private
+ * @type {!Array.<number>}
+ */
+ this.resolutions_ = options.resolutions;
+ ol.asserts.assert(ol.array.isSorted(this.resolutions_, function(a, b) {
+ return b - a;
+ }, true), 17); // `resolutions` must be sorted in descending order
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.maxZoom = this.resolutions_.length - 1;
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.origin_ = options.origin !== undefined ? options.origin : null;
+
+ /**
+ * @private
+ * @type {Array.<ol.Coordinate>}
+ */
+ this.origins_ = null;
+ if (options.origins !== undefined) {
+ this.origins_ = options.origins;
+ ol.asserts.assert(this.origins_.length == this.resolutions_.length,
+ 20); // Number of `origins` and `resolutions` must be equal
+ }
+
+ var extent = options.extent;
+
+ if (extent !== undefined &&
+ !this.origin_ && !this.origins_) {
+ this.origin_ = ol.extent.getTopLeft(extent);
+ }
+
+ ol.asserts.assert(
+ (!this.origin_ && this.origins_) || (this.origin_ && !this.origins_),
+ 18); // Either `origin` or `origins` must be configured, never both
+
+ /**
+ * @private
+ * @type {Array.<number|ol.Size>}
+ */
+ this.tileSizes_ = null;
+ if (options.tileSizes !== undefined) {
+ this.tileSizes_ = options.tileSizes;
+ ol.asserts.assert(this.tileSizes_.length == this.resolutions_.length,
+ 19); // Number of `tileSizes` and `resolutions` must be equal
+ }
+
+ /**
+ * @private
+ * @type {number|ol.Size}
+ */
+ this.tileSize_ = options.tileSize !== undefined ?
+ options.tileSize :
+ !this.tileSizes_ ? ol.DEFAULT_TILE_SIZE : null;
+ ol.asserts.assert(
+ (!this.tileSize_ && this.tileSizes_) ||
+ (this.tileSize_ && !this.tileSizes_),
+ 22); // Either `tileSize` or `tileSizes` must be configured, never both
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.extent_ = extent !== undefined ? extent : null;
+
+
+ /**
+ * @private
+ * @type {Array.<ol.TileRange>}
+ */
+ this.fullTileRanges_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.tmpSize_ = [0, 0];
+
+ if (options.sizes !== undefined) {
+ ol.DEBUG && console.assert(options.sizes.length == this.resolutions_.length,
+ 'number of sizes and resolutions must be equal');
+ this.fullTileRanges_ = options.sizes.map(function(size, z) {
+ ol.DEBUG && console.assert(size[0] !== 0, 'width must not be 0');
+ ol.DEBUG && console.assert(size[1] !== 0, 'height must not be 0');
+ var tileRange = new ol.TileRange(
+ Math.min(0, size[0]), Math.max(size[0] - 1, -1),
+ Math.min(0, size[1]), Math.max(size[1] - 1, -1));
+ return tileRange;
+ }, this);
+ } else if (extent) {
+ this.calculateTileRanges_(extent);
+ }
+
+};
+
+
+/**
+ * @private
+ * @type {ol.TileCoord}
+ */
+ol.tilegrid.TileGrid.tmpTileCoord_ = [0, 0, 0];
+
+
+/**
+ * Call a function with each tile coordinate for a given extent and zoom level.
+ *
+ * @param {ol.Extent} extent Extent.
+ * @param {number} zoom Zoom level.
+ * @param {function(ol.TileCoord)} callback Function called with each tile coordinate.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.forEachTileCoord = function(extent, zoom, callback) {
+ var tileRange = this.getTileRangeForExtentAndZ(extent, zoom);
+ for (var i = tileRange.minX, ii = tileRange.maxX; i <= ii; ++i) {
+ for (var j = tileRange.minY, jj = tileRange.maxY; j <= jj; ++j) {
+ callback([zoom, i, j]);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {function(this: T, number, ol.TileRange): boolean} callback Callback.
+ * @param {T=} opt_this The object to use as `this` in `callback`.
+ * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object.
+ * @param {ol.Extent=} opt_extent Temporary ol.Extent object.
+ * @return {boolean} Callback succeeded.
+ * @template T
+ */
+ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange = function(tileCoord, callback, opt_this, opt_tileRange, opt_extent) {
+ var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent);
+ var z = tileCoord[0] - 1;
+ while (z >= this.minZoom) {
+ if (callback.call(opt_this, z,
+ this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange))) {
+ return true;
+ }
+ --z;
+ }
+ return false;
+};
+
+
+/**
+ * Get the extent for this tile grid, if it was configured.
+ * @return {ol.Extent} Extent.
+ */
+ol.tilegrid.TileGrid.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+
+/**
+ * Get the maximum zoom level for the grid.
+ * @return {number} Max zoom.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.getMaxZoom = function() {
+ return this.maxZoom;
+};
+
+
+/**
+ * Get the minimum zoom level for the grid.
+ * @return {number} Min zoom.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.getMinZoom = function() {
+ return this.minZoom;
+};
+
+
+/**
+ * Get the origin for the grid at the given zoom level.
+ * @param {number} z Z.
+ * @return {ol.Coordinate} Origin.
+ * @api stable
+ */
+ol.tilegrid.TileGrid.prototype.getOrigin = function(z) {
+ if (this.origin_) {
+ return this.origin_;
+ } else {
+ ol.DEBUG && console.assert(this.minZoom <= z && z <= this.maxZoom,
+ 'given z is not in allowed range (%s <= %s <= %s)',
+ this.minZoom, z, this.maxZoom);
+ return this.origins_[z];
+ }
+};
+
+
+/**
+ * Get the resolution for the given zoom level.
+ * @param {number} z Z.
+ * @return {number} Resolution.
+ * @api stable
+ */
+ol.tilegrid.TileGrid.prototype.getResolution = function(z) {
+ ol.DEBUG && console.assert(this.minZoom <= z && z <= this.maxZoom,
+ 'given z is not in allowed range (%s <= %s <= %s)',
+ this.minZoom, z, this.maxZoom);
+ return this.resolutions_[z];
+};
+
+
+/**
+ * Get the list of resolutions for the tile grid.
+ * @return {Array.<number>} Resolutions.
+ * @api stable
+ */
+ol.tilegrid.TileGrid.prototype.getResolutions = function() {
+ return this.resolutions_;
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object.
+ * @param {ol.Extent=} opt_extent Temporary ol.Extent object.
+ * @return {ol.TileRange} Tile range.
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordChildTileRange = function(tileCoord, opt_tileRange, opt_extent) {
+ if (tileCoord[0] < this.maxZoom) {
+ var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent);
+ return this.getTileRangeForExtentAndZ(
+ tileCoordExtent, tileCoord[0] + 1, opt_tileRange);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @param {number} z Z.
+ * @param {ol.TileRange} tileRange Tile range.
+ * @param {ol.Extent=} opt_extent Temporary ol.Extent object.
+ * @return {ol.Extent} Extent.
+ */
+ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange, opt_extent) {
+ var origin = this.getOrigin(z);
+ var resolution = this.getResolution(z);
+ var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_);
+ var minX = origin[0] + tileRange.minX * tileSize[0] * resolution;
+ var maxX = origin[0] + (tileRange.maxX + 1) * tileSize[0] * resolution;
+ var minY = origin[1] + tileRange.minY * tileSize[1] * resolution;
+ var maxY = origin[1] + (tileRange.maxY + 1) * tileSize[1] * resolution;
+ return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {ol.TileRange=} opt_tileRange Temporary tile range object.
+ * @return {ol.TileRange} Tile range.
+ */
+ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function(extent, resolution, opt_tileRange) {
+ var tileCoord = ol.tilegrid.TileGrid.tmpTileCoord_;
+ this.getTileCoordForXYAndResolution_(
+ extent[0], extent[1], resolution, false, tileCoord);
+ var minX = tileCoord[1];
+ var minY = tileCoord[2];
+ this.getTileCoordForXYAndResolution_(
+ extent[2], extent[3], resolution, true, tileCoord);
+ return ol.TileRange.createOrUpdate(
+ minX, tileCoord[1], minY, tileCoord[2], opt_tileRange);
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} z Z.
+ * @param {ol.TileRange=} opt_tileRange Temporary tile range object.
+ * @return {ol.TileRange} Tile range.
+ */
+ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndZ = function(extent, z, opt_tileRange) {
+ var resolution = this.getResolution(z);
+ return this.getTileRangeForExtentAndResolution(
+ extent, resolution, opt_tileRange);
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @return {ol.Coordinate} Tile center.
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordCenter = function(tileCoord) {
+ var origin = this.getOrigin(tileCoord[0]);
+ var resolution = this.getResolution(tileCoord[0]);
+ var tileSize = ol.size.toSize(this.getTileSize(tileCoord[0]), this.tmpSize_);
+ return [
+ origin[0] + (tileCoord[1] + 0.5) * tileSize[0] * resolution,
+ origin[1] + (tileCoord[2] + 0.5) * tileSize[1] * resolution
+ ];
+};
+
+
+/**
+ * Get the extent of a tile coordinate.
+ *
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Extent=} opt_extent Temporary extent object.
+ * @return {ol.Extent} Extent.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord, opt_extent) {
+ var origin = this.getOrigin(tileCoord[0]);
+ var resolution = this.getResolution(tileCoord[0]);
+ var tileSize = ol.size.toSize(this.getTileSize(tileCoord[0]), this.tmpSize_);
+ var minX = origin[0] + tileCoord[1] * tileSize[0] * resolution;
+ var minY = origin[1] + tileCoord[2] * tileSize[1] * resolution;
+ var maxX = minX + tileSize[0] * resolution;
+ var maxY = minY + tileSize[1] * resolution;
+ return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
+};
+
+
+/**
+ * Get the tile coordinate for the given map coordinate and resolution. This
+ * method considers that coordinates that intersect tile boundaries should be
+ * assigned the higher tile coordinate.
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object.
+ * @return {ol.TileCoord} Tile coordinate.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function(coordinate, resolution, opt_tileCoord) {
+ return this.getTileCoordForXYAndResolution_(
+ coordinate[0], coordinate[1], resolution, false, opt_tileCoord);
+};
+
+
+/**
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @param {number} resolution Resolution.
+ * @param {boolean} reverseIntersectionPolicy Instead of letting edge
+ * intersections go to the higher tile coordinate, let edge intersections
+ * go to the lower tile coordinate.
+ * @param {ol.TileCoord=} opt_tileCoord Temporary ol.TileCoord object.
+ * @return {ol.TileCoord} Tile coordinate.
+ * @private
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordForXYAndResolution_ = function(
+ x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) {
+ var z = this.getZForResolution(resolution);
+ var scale = resolution / this.getResolution(z);
+ var origin = this.getOrigin(z);
+ var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_);
+
+ var adjustX = reverseIntersectionPolicy ? 0.5 : 0;
+ var adjustY = reverseIntersectionPolicy ? 0 : 0.5;
+ var xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX);
+ var yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY);
+ var tileCoordX = scale * xFromOrigin / tileSize[0];
+ var tileCoordY = scale * yFromOrigin / tileSize[1];
+
+ if (reverseIntersectionPolicy) {
+ tileCoordX = Math.ceil(tileCoordX) - 1;
+ tileCoordY = Math.ceil(tileCoordY) - 1;
+ } else {
+ tileCoordX = Math.floor(tileCoordX);
+ tileCoordY = Math.floor(tileCoordY);
+ }
+
+ return ol.tilecoord.createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord);
+};
+
+
+/**
+ * Get a tile coordinate given a map coordinate and zoom level.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} z Zoom level.
+ * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object.
+ * @return {ol.TileCoord} Tile coordinate.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z, opt_tileCoord) {
+ var resolution = this.getResolution(z);
+ return this.getTileCoordForXYAndResolution_(
+ coordinate[0], coordinate[1], resolution, false, opt_tileCoord);
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @return {number} Tile resolution.
+ */
+ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) {
+ ol.DEBUG && console.assert(
+ this.minZoom <= tileCoord[0] && tileCoord[0] <= this.maxZoom,
+ 'z of given tilecoord is not in allowed range (%s <= %s <= %s',
+ this.minZoom, tileCoord[0], this.maxZoom);
+ return this.resolutions_[tileCoord[0]];
+};
+
+
+/**
+ * Get the tile size for a zoom level. The type of the return value matches the
+ * `tileSize` or `tileSizes` that the tile grid was configured with. To always
+ * get an `ol.Size`, run the result through `ol.size.toSize()`.
+ * @param {number} z Z.
+ * @return {number|ol.Size} Tile size.
+ * @api stable
+ */
+ol.tilegrid.TileGrid.prototype.getTileSize = function(z) {
+ if (this.tileSize_) {
+ return this.tileSize_;
+ } else {
+ ol.DEBUG && console.assert(this.minZoom <= z && z <= this.maxZoom,
+ 'z is not in allowed range (%s <= %s <= %s',
+ this.minZoom, z, this.maxZoom);
+ return this.tileSizes_[z];
+ }
+};
+
+
+/**
+ * @param {number} z Zoom level.
+ * @return {ol.TileRange} Extent tile range for the specified zoom level.
+ */
+ol.tilegrid.TileGrid.prototype.getFullTileRange = function(z) {
+ if (!this.fullTileRanges_) {
+ return null;
+ } else {
+ ol.DEBUG && console.assert(this.minZoom <= z && z <= this.maxZoom,
+ 'z is not in allowed range (%s <= %s <= %s',
+ this.minZoom, z, this.maxZoom);
+ return this.fullTileRanges_[z];
+ }
+};
+
+
+/**
+ * @param {number} resolution Resolution.
+ * @param {number=} opt_direction If 0, the nearest resolution will be used.
+ * If 1, the nearest lower resolution will be used. If -1, the nearest
+ * higher resolution will be used. Default is 0.
+ * @return {number} Z.
+ * @api
+ */
+ol.tilegrid.TileGrid.prototype.getZForResolution = function(
+ resolution, opt_direction) {
+ var z = ol.array.linearFindNearest(this.resolutions_, resolution,
+ opt_direction || 0);
+ return ol.math.clamp(z, this.minZoom, this.maxZoom);
+};
+
+
+/**
+ * @param {!ol.Extent} extent Extent for this tile grid.
+ * @private
+ */
+ol.tilegrid.TileGrid.prototype.calculateTileRanges_ = function(extent) {
+ var length = this.resolutions_.length;
+ var fullTileRanges = new Array(length);
+ for (var z = this.minZoom; z < length; ++z) {
+ fullTileRanges[z] = this.getTileRangeForExtentAndZ(extent, z);
+ }
+ this.fullTileRanges_ = fullTileRanges;
+};
+
+goog.provide('ol.tilegrid');
+
+goog.require('ol');
+goog.require('ol.size');
+goog.require('ol.extent');
+goog.require('ol.extent.Corner');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.proj.Units');
+goog.require('ol.tilegrid.TileGrid');
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {!ol.tilegrid.TileGrid} Default tile grid for the passed projection.
+ */
+ol.tilegrid.getForProjection = function(projection) {
+ var tileGrid = projection.getDefaultTileGrid();
+ if (!tileGrid) {
+ tileGrid = ol.tilegrid.createForProjection(projection);
+ projection.setDefaultTileGrid(tileGrid);
+ }
+ return tileGrid;
+};
+
+
+/**
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {ol.TileCoord} Tile coordinate.
+ */
+ol.tilegrid.wrapX = function(tileGrid, tileCoord, projection) {
+ var z = tileCoord[0];
+ var center = tileGrid.getTileCoordCenter(tileCoord);
+ var projectionExtent = ol.tilegrid.extentFromProjection(projection);
+ if (!ol.extent.containsCoordinate(projectionExtent, center)) {
+ var worldWidth = ol.extent.getWidth(projectionExtent);
+ var worldsAway = Math.ceil((projectionExtent[0] - center[0]) / worldWidth);
+ center[0] += worldWidth * worldsAway;
+ return tileGrid.getTileCoordForCoordAndZ(center, z);
+ } else {
+ return tileCoord;
+ }
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number=} opt_maxZoom Maximum zoom level (default is
+ * ol.DEFAULT_MAX_ZOOM).
+ * @param {number|ol.Size=} opt_tileSize Tile size (default uses
+ * ol.DEFAULT_TILE_SIZE).
+ * @param {ol.extent.Corner=} opt_corner Extent corner (default is
+ * ol.extent.Corner.TOP_LEFT).
+ * @return {!ol.tilegrid.TileGrid} TileGrid instance.
+ */
+ol.tilegrid.createForExtent = function(extent, opt_maxZoom, opt_tileSize, opt_corner) {
+ var corner = opt_corner !== undefined ?
+ opt_corner : ol.extent.Corner.TOP_LEFT;
+
+ var resolutions = ol.tilegrid.resolutionsFromExtent(
+ extent, opt_maxZoom, opt_tileSize);
+
+ return new ol.tilegrid.TileGrid({
+ extent: extent,
+ origin: ol.extent.getCorner(extent, corner),
+ resolutions: resolutions,
+ tileSize: opt_tileSize
+ });
+};
+
+
+/**
+ * Creates a tile grid with a standard XYZ tiling scheme.
+ * @param {olx.tilegrid.XYZOptions=} opt_options Tile grid options.
+ * @return {ol.tilegrid.TileGrid} Tile grid instance.
+ * @api
+ */
+ol.tilegrid.createXYZ = function(opt_options) {
+ var options = /** @type {olx.tilegrid.TileGridOptions} */ ({});
+ ol.obj.assign(options, opt_options !== undefined ?
+ opt_options : /** @type {olx.tilegrid.XYZOptions} */ ({}));
+ if (options.extent === undefined) {
+ options.extent = ol.proj.get('EPSG:3857').getExtent();
+ }
+ options.resolutions = ol.tilegrid.resolutionsFromExtent(
+ options.extent, options.maxZoom, options.tileSize);
+ delete options.maxZoom;
+
+ return new ol.tilegrid.TileGrid(options);
+};
+
+
+/**
+ * Create a resolutions array from an extent. A zoom factor of 2 is assumed.
+ * @param {ol.Extent} extent Extent.
+ * @param {number=} opt_maxZoom Maximum zoom level (default is
+ * ol.DEFAULT_MAX_ZOOM).
+ * @param {number|ol.Size=} opt_tileSize Tile size (default uses
+ * ol.DEFAULT_TILE_SIZE).
+ * @return {!Array.<number>} Resolutions array.
+ */
+ol.tilegrid.resolutionsFromExtent = function(extent, opt_maxZoom, opt_tileSize) {
+ var maxZoom = opt_maxZoom !== undefined ?
+ opt_maxZoom : ol.DEFAULT_MAX_ZOOM;
+
+ var height = ol.extent.getHeight(extent);
+ var width = ol.extent.getWidth(extent);
+
+ var tileSize = ol.size.toSize(opt_tileSize !== undefined ?
+ opt_tileSize : ol.DEFAULT_TILE_SIZE);
+ var maxResolution = Math.max(
+ width / tileSize[0], height / tileSize[1]);
+
+ var length = maxZoom + 1;
+ var resolutions = new Array(length);
+ for (var z = 0; z < length; ++z) {
+ resolutions[z] = maxResolution / Math.pow(2, z);
+ }
+ return resolutions;
+};
+
+
+/**
+ * @param {ol.ProjectionLike} projection Projection.
+ * @param {number=} opt_maxZoom Maximum zoom level (default is
+ * ol.DEFAULT_MAX_ZOOM).
+ * @param {ol.Size=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
+ * @param {ol.extent.Corner=} opt_corner Extent corner (default is
+ * ol.extent.Corner.BOTTOM_LEFT).
+ * @return {!ol.tilegrid.TileGrid} TileGrid instance.
+ */
+ol.tilegrid.createForProjection = function(projection, opt_maxZoom, opt_tileSize, opt_corner) {
+ var extent = ol.tilegrid.extentFromProjection(projection);
+ return ol.tilegrid.createForExtent(
+ extent, opt_maxZoom, opt_tileSize, opt_corner);
+};
+
+
+/**
+ * Generate a tile grid extent from a projection. If the projection has an
+ * extent, it is used. If not, a global extent is assumed.
+ * @param {ol.ProjectionLike} projection Projection.
+ * @return {ol.Extent} Extent.
+ */
+ol.tilegrid.extentFromProjection = function(projection) {
+ projection = ol.proj.get(projection);
+ var extent = projection.getExtent();
+ if (!extent) {
+ var half = 180 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] /
+ projection.getMetersPerUnit();
+ extent = ol.extent.createOrUpdate(-half, -half, half, half);
+ }
+ return extent;
+};
+
+goog.provide('ol.Attribution');
+
+goog.require('ol.TileRange');
+goog.require('ol.math');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * An attribution for a layer source.
+ *
+ * Example:
+ *
+ * source: new ol.source.OSM({
+ * attributions: [
+ * new ol.Attribution({
+ * html: 'All maps &copy; ' +
+ * '<a href="https://www.opencyclemap.org/">OpenCycleMap</a>'
+ * }),
+ * ol.source.OSM.ATTRIBUTION
+ * ],
+ * ..
+ *
+ * @constructor
+ * @param {olx.AttributionOptions} options Attribution options.
+ * @struct
+ * @api stable
+ */
+ol.Attribution = function(options) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.html_ = options.html;
+
+ /**
+ * @private
+ * @type {Object.<string, Array.<ol.TileRange>>}
+ */
+ this.tileRanges_ = options.tileRanges ? options.tileRanges : null;
+
+};
+
+
+/**
+ * Get the attribution markup.
+ * @return {string} The attribution HTML.
+ * @api stable
+ */
+ol.Attribution.prototype.getHTML = function() {
+ return this.html_;
+};
+
+
+/**
+ * @param {Object.<string, ol.TileRange>} tileRanges Tile ranges.
+ * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @param {!ol.proj.Projection} projection Projection.
+ * @return {boolean} Intersects any tile range.
+ */
+ol.Attribution.prototype.intersectsAnyTileRange = function(tileRanges, tileGrid, projection) {
+ if (!this.tileRanges_) {
+ return true;
+ }
+ var i, ii, tileRange, zKey;
+ for (zKey in tileRanges) {
+ if (!(zKey in this.tileRanges_)) {
+ continue;
+ }
+ tileRange = tileRanges[zKey];
+ var testTileRange;
+ for (i = 0, ii = this.tileRanges_[zKey].length; i < ii; ++i) {
+ testTileRange = this.tileRanges_[zKey][i];
+ if (testTileRange.intersects(tileRange)) {
+ return true;
+ }
+ var extentTileRange = tileGrid.getTileRangeForExtentAndZ(
+ ol.tilegrid.extentFromProjection(projection), parseInt(zKey, 10));
+ var width = extentTileRange.getWidth();
+ if (tileRange.minX < extentTileRange.minX ||
+ tileRange.maxX > extentTileRange.maxX) {
+ if (testTileRange.intersects(new ol.TileRange(
+ ol.math.modulo(tileRange.minX, width),
+ ol.math.modulo(tileRange.maxX, width),
+ tileRange.minY, tileRange.maxY))) {
+ return true;
+ }
+ if (tileRange.getWidth() > width &&
+ testTileRange.intersects(extentTileRange)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+};
+
+/**
+ * An implementation of Google Maps' MVCArray.
+ * @see https://developers.google.com/maps/documentation/javascript/reference
+ */
+
+goog.provide('ol.Collection');
+
+goog.require('ol');
+goog.require('ol.events.Event');
+goog.require('ol.Object');
+
+
+/**
+ * @classdesc
+ * An expanded version of standard JS Array, adding convenience methods for
+ * manipulation. Add and remove changes to the Collection trigger a Collection
+ * event. Note that this does not cover changes to the objects _within_ the
+ * Collection; they trigger events on the appropriate object, not on the
+ * Collection as a whole.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @fires ol.Collection.Event
+ * @param {!Array.<T>=} opt_array Array.
+ * @template T
+ * @api stable
+ */
+ol.Collection = function(opt_array) {
+
+ ol.Object.call(this);
+
+ /**
+ * @private
+ * @type {!Array.<T>}
+ */
+ this.array_ = opt_array ? opt_array : [];
+
+ this.updateLength_();
+
+};
+ol.inherits(ol.Collection, ol.Object);
+
+
+/**
+ * Remove all elements from the collection.
+ * @api stable
+ */
+ol.Collection.prototype.clear = function() {
+ while (this.getLength() > 0) {
+ this.pop();
+ }
+};
+
+
+/**
+ * Add elements to the collection. This pushes each item in the provided array
+ * to the end of the collection.
+ * @param {!Array.<T>} arr Array.
+ * @return {ol.Collection.<T>} This collection.
+ * @api stable
+ */
+ol.Collection.prototype.extend = function(arr) {
+ var i, ii;
+ for (i = 0, ii = arr.length; i < ii; ++i) {
+ this.push(arr[i]);
+ }
+ return this;
+};
+
+
+/**
+ * Iterate over each element, calling the provided callback.
+ * @param {function(this: S, T, number, Array.<T>): *} f The function to call
+ * for every element. This function takes 3 arguments (the element, the
+ * index and the array). The return value is ignored.
+ * @param {S=} opt_this The object to use as `this` in `f`.
+ * @template S
+ * @api stable
+ */
+ol.Collection.prototype.forEach = function(f, opt_this) {
+ this.array_.forEach(f, opt_this);
+};
+
+
+/**
+ * Get a reference to the underlying Array object. Warning: if the array
+ * is mutated, no events will be dispatched by the collection, and the
+ * collection's "length" property won't be in sync with the actual length
+ * of the array.
+ * @return {!Array.<T>} Array.
+ * @api stable
+ */
+ol.Collection.prototype.getArray = function() {
+ return this.array_;
+};
+
+
+/**
+ * Get the element at the provided index.
+ * @param {number} index Index.
+ * @return {T} Element.
+ * @api stable
+ */
+ol.Collection.prototype.item = function(index) {
+ return this.array_[index];
+};
+
+
+/**
+ * Get the length of this collection.
+ * @return {number} The length of the array.
+ * @observable
+ * @api stable
+ */
+ol.Collection.prototype.getLength = function() {
+ return /** @type {number} */ (this.get(ol.Collection.Property.LENGTH));
+};
+
+
+/**
+ * Insert an element at the provided index.
+ * @param {number} index Index.
+ * @param {T} elem Element.
+ * @api stable
+ */
+ol.Collection.prototype.insertAt = function(index, elem) {
+ this.array_.splice(index, 0, elem);
+ this.updateLength_();
+ this.dispatchEvent(
+ new ol.Collection.Event(ol.Collection.EventType.ADD, elem));
+};
+
+
+/**
+ * Remove the last element of the collection and return it.
+ * Return `undefined` if the collection is empty.
+ * @return {T|undefined} Element.
+ * @api stable
+ */
+ol.Collection.prototype.pop = function() {
+ return this.removeAt(this.getLength() - 1);
+};
+
+
+/**
+ * Insert the provided element at the end of the collection.
+ * @param {T} elem Element.
+ * @return {number} New length of the collection.
+ * @api stable
+ */
+ol.Collection.prototype.push = function(elem) {
+ var n = this.getLength();
+ this.insertAt(n, elem);
+ return this.getLength();
+};
+
+
+/**
+ * Remove the first occurrence of an element from the collection.
+ * @param {T} elem Element.
+ * @return {T|undefined} The removed element or undefined if none found.
+ * @api stable
+ */
+ol.Collection.prototype.remove = function(elem) {
+ var arr = this.array_;
+ var i, ii;
+ for (i = 0, ii = arr.length; i < ii; ++i) {
+ if (arr[i] === elem) {
+ return this.removeAt(i);
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * Remove the element at the provided index and return it.
+ * Return `undefined` if the collection does not contain this index.
+ * @param {number} index Index.
+ * @return {T|undefined} Value.
+ * @api stable
+ */
+ol.Collection.prototype.removeAt = function(index) {
+ var prev = this.array_[index];
+ this.array_.splice(index, 1);
+ this.updateLength_();
+ this.dispatchEvent(
+ new ol.Collection.Event(ol.Collection.EventType.REMOVE, prev));
+ return prev;
+};
+
+
+/**
+ * Set the element at the provided index.
+ * @param {number} index Index.
+ * @param {T} elem Element.
+ * @api stable
+ */
+ol.Collection.prototype.setAt = function(index, elem) {
+ var n = this.getLength();
+ if (index < n) {
+ var prev = this.array_[index];
+ this.array_[index] = elem;
+ this.dispatchEvent(
+ new ol.Collection.Event(ol.Collection.EventType.REMOVE, prev));
+ this.dispatchEvent(
+ new ol.Collection.Event(ol.Collection.EventType.ADD, elem));
+ } else {
+ var j;
+ for (j = n; j < index; ++j) {
+ this.insertAt(j, undefined);
+ }
+ this.insertAt(index, elem);
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.Collection.prototype.updateLength_ = function() {
+ this.set(ol.Collection.Property.LENGTH, this.array_.length);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.Collection.Property = {
+ LENGTH: 'length'
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.Collection.EventType = {
+ /**
+ * Triggered when an item is added to the collection.
+ * @event ol.Collection.Event#add
+ * @api stable
+ */
+ ADD: 'add',
+ /**
+ * Triggered when an item is removed from the collection.
+ * @event ol.Collection.Event#remove
+ * @api stable
+ */
+ REMOVE: 'remove'
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.Collection} instances are instances of this
+ * type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.Collection.Event}
+ * @param {ol.Collection.EventType} type Type.
+ * @param {*=} opt_element Element.
+ */
+ol.Collection.Event = function(type, opt_element) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The element that is added to or removed from the collection.
+ * @type {*}
+ * @api stable
+ */
+ this.element = opt_element;
+
+};
+ol.inherits(ol.Collection.Event, ol.events.Event);
+
+goog.provide('ol.color');
+
+goog.require('ol.asserts');
+goog.require('ol.math');
+
+
+/**
+ * This RegExp matches # followed by 3 or 6 hex digits.
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.color.HEX_COLOR_RE_ = /^#(?:[0-9a-f]{3}){1,2}$/i;
+
+
+/**
+ * Regular expression for matching potential named color style strings.
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.color.NAMED_COLOR_RE_ = /^([a-z]*)$/i;
+
+
+/**
+ * Return the color as an array. This function maintains a cache of calculated
+ * arrays which means the result should not be modified.
+ * @param {ol.Color|string} color Color.
+ * @return {ol.Color} Color.
+ * @api
+ */
+ol.color.asArray = function(color) {
+ if (Array.isArray(color)) {
+ return color;
+ } else {
+ return ol.color.fromString(/** @type {string} */ (color));
+ }
+};
+
+
+/**
+ * Return the color as an rgba string.
+ * @param {ol.Color|string} color Color.
+ * @return {string} Rgba string.
+ * @api
+ */
+ol.color.asString = function(color) {
+ if (typeof color === 'string') {
+ return color;
+ } else {
+ return ol.color.toString(color);
+ }
+};
+
+/**
+ * Return named color as an rgba string.
+ * @param {string} color Named color.
+ * @return {string} Rgb string.
+ */
+ol.color.fromNamed = function(color) {
+ var el = document.createElement('div');
+ el.style.color = color;
+ document.body.appendChild(el);
+ var rgb = getComputedStyle(el).color;
+ document.body.removeChild(el);
+ return rgb;
+};
+
+
+/**
+ * @param {string} s String.
+ * @return {ol.Color} Color.
+ */
+ol.color.fromString = (
+ function() {
+
+ // We maintain a small cache of parsed strings. To provide cheap LRU-like
+ // semantics, whenever the cache grows too large we simply delete an
+ // arbitrary 25% of the entries.
+
+ /**
+ * @const
+ * @type {number}
+ */
+ var MAX_CACHE_SIZE = 1024;
+
+ /**
+ * @type {Object.<string, ol.Color>}
+ */
+ var cache = {};
+
+ /**
+ * @type {number}
+ */
+ var cacheSize = 0;
+
+ return (
+ /**
+ * @param {string} s String.
+ * @return {ol.Color} Color.
+ */
+ function(s) {
+ var color;
+ if (cache.hasOwnProperty(s)) {
+ color = cache[s];
+ } else {
+ if (cacheSize >= MAX_CACHE_SIZE) {
+ var i = 0;
+ var key;
+ for (key in cache) {
+ if ((i++ & 3) === 0) {
+ delete cache[key];
+ --cacheSize;
+ }
+ }
+ }
+ color = ol.color.fromStringInternal_(s);
+ cache[s] = color;
+ ++cacheSize;
+ }
+ return color;
+ });
+
+ })();
+
+
+/**
+ * @param {string} s String.
+ * @private
+ * @return {ol.Color} Color.
+ */
+ol.color.fromStringInternal_ = function(s) {
+ var r, g, b, a, color, parts;
+
+ if (ol.color.NAMED_COLOR_RE_.exec(s)) {
+ s = ol.color.fromNamed(s);
+ }
+
+ if (ol.color.HEX_COLOR_RE_.exec(s)) { // hex
+ var n = s.length - 1; // number of hex digits
+ ol.asserts.assert(n == 3 || n == 6, 54); // Hex color should have 3 or 6 digits
+ var d = n == 3 ? 1 : 2; // number of digits per channel
+ r = parseInt(s.substr(1 + 0 * d, d), 16);
+ g = parseInt(s.substr(1 + 1 * d, d), 16);
+ b = parseInt(s.substr(1 + 2 * d, d), 16);
+ if (d == 1) {
+ r = (r << 4) + r;
+ g = (g << 4) + g;
+ b = (b << 4) + b;
+ }
+ a = 1;
+ color = [r, g, b, a];
+ } else if (s.indexOf('rgba(') == 0) { // rgba()
+ parts = s.slice(5, -1).split(',').map(Number);
+ color = ol.color.normalize(parts);
+ } else if (s.indexOf('rgb(') == 0) { // rgb()
+ parts = s.slice(4, -1).split(',').map(Number);
+ parts.push(1);
+ color = ol.color.normalize(parts);
+ } else {
+ ol.asserts.assert(false, 14); // Invalid color
+ }
+ return /** @type {ol.Color} */ (color);
+};
+
+
+/**
+ * @param {ol.Color} color Color.
+ * @param {ol.Color=} opt_color Color.
+ * @return {ol.Color} Clamped color.
+ */
+ol.color.normalize = function(color, opt_color) {
+ var result = opt_color || [];
+ result[0] = ol.math.clamp((color[0] + 0.5) | 0, 0, 255);
+ result[1] = ol.math.clamp((color[1] + 0.5) | 0, 0, 255);
+ result[2] = ol.math.clamp((color[2] + 0.5) | 0, 0, 255);
+ result[3] = ol.math.clamp(color[3], 0, 1);
+ return result;
+};
+
+
+/**
+ * @param {ol.Color} color Color.
+ * @return {string} String.
+ */
+ol.color.toString = function(color) {
+ var r = color[0];
+ if (r != (r | 0)) {
+ r = (r + 0.5) | 0;
+ }
+ var g = color[1];
+ if (g != (g | 0)) {
+ g = (g + 0.5) | 0;
+ }
+ var b = color[2];
+ if (b != (b | 0)) {
+ b = (b + 0.5) | 0;
+ }
+ var a = color[3] === undefined ? 1 : color[3];
+ return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
+};
+
+goog.provide('ol.colorlike');
+
+goog.require('ol.color');
+
+
+/**
+ * @param {ol.Color|ol.ColorLike} color Color.
+ * @return {ol.ColorLike} The color as an ol.ColorLike
+ * @api
+ */
+ol.colorlike.asColorLike = function(color) {
+ if (ol.colorlike.isColorLike(color)) {
+ return /** @type {string|CanvasPattern|CanvasGradient} */ (color);
+ } else {
+ return ol.color.asString(/** @type {ol.Color} */ (color));
+ }
+};
+
+
+/**
+ * @param {?} color The value that is potentially an ol.ColorLike
+ * @return {boolean} Whether the color is an ol.ColorLike
+ */
+ol.colorlike.isColorLike = function(color) {
+ return (
+ typeof color === 'string' ||
+ color instanceof CanvasPattern ||
+ color instanceof CanvasGradient
+ );
+};
+
+goog.provide('ol.dom');
+
+
+/**
+ * Create an html canvas element and returns its 2d context.
+ * @param {number=} opt_width Canvas width.
+ * @param {number=} opt_height Canvas height.
+ * @return {CanvasRenderingContext2D} The context.
+ */
+ol.dom.createCanvasContext2D = function(opt_width, opt_height) {
+ var canvas = document.createElement('CANVAS');
+ if (opt_width) {
+ canvas.width = opt_width;
+ }
+ if (opt_height) {
+ canvas.height = opt_height;
+ }
+ return canvas.getContext('2d');
+};
+
+
+/**
+ * Get the current computed width for the given element including margin,
+ * padding and border.
+ * Equivalent to jQuery's `$(el).outerWidth(true)`.
+ * @param {!Element} element Element.
+ * @return {number} The width.
+ */
+ol.dom.outerWidth = function(element) {
+ var width = element.offsetWidth;
+ var style = element.currentStyle || getComputedStyle(element);
+ width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10);
+
+ return width;
+};
+
+
+/**
+ * Get the current computed height for the given element including margin,
+ * padding and border.
+ * Equivalent to jQuery's `$(el).outerHeight(true)`.
+ * @param {!Element} element Element.
+ * @return {number} The height.
+ */
+ol.dom.outerHeight = function(element) {
+ var height = element.offsetHeight;
+ var style = element.currentStyle || getComputedStyle(element);
+ height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10);
+
+ return height;
+};
+
+/**
+ * @param {Node} newNode Node to replace old node
+ * @param {Node} oldNode The node to be replaced
+ */
+ol.dom.replaceNode = function(newNode, oldNode) {
+ var parent = oldNode.parentNode;
+ if (parent) {
+ parent.replaceChild(newNode, oldNode);
+ }
+};
+
+/**
+ * @param {Node} node The node to remove.
+ * @returns {Node} The node that was removed or null.
+ */
+ol.dom.removeNode = function(node) {
+ return node && node.parentNode ? node.parentNode.removeChild(node) : null;
+};
+
+/**
+ * @param {Node} node The node to remove the children from.
+ */
+ol.dom.removeChildren = function(node) {
+ while (node.lastChild) {
+ node.removeChild(node.lastChild);
+ }
+};
+
+goog.provide('ol.MapEvent');
+
+goog.require('ol');
+goog.require('ol.events.Event');
+
+
+/**
+ * @classdesc
+ * Events emitted as map events are instances of this type.
+ * See {@link ol.Map} for which events trigger a map event.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.MapEvent}
+ * @param {string} type Event type.
+ * @param {ol.Map} map Map.
+ * @param {?olx.FrameState=} opt_frameState Frame state.
+ */
+ol.MapEvent = function(type, map, opt_frameState) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The map where the event occurred.
+ * @type {ol.Map}
+ * @api stable
+ */
+ this.map = map;
+
+ /**
+ * The frame state at the time of the event.
+ * @type {?olx.FrameState}
+ * @api
+ */
+ this.frameState = opt_frameState !== undefined ? opt_frameState : null;
+
+};
+ol.inherits(ol.MapEvent, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.MapEvent.Type = {
+
+ /**
+ * Triggered after a map frame is rendered.
+ * @event ol.MapEvent#postrender
+ * @api
+ */
+ POSTRENDER: 'postrender',
+
+ /**
+ * Triggered after the map is moved.
+ * @event ol.MapEvent#moveend
+ * @api stable
+ */
+ MOVEEND: 'moveend'
+
+};
+
+goog.provide('ol.control.Control');
+
+goog.require('ol.events');
+goog.require('ol');
+goog.require('ol.MapEvent');
+goog.require('ol.Object');
+goog.require('ol.dom');
+
+
+/**
+ * @classdesc
+ * A control is a visible widget with a DOM element in a fixed position on the
+ * screen. They can involve user input (buttons), or be informational only;
+ * the position is determined using CSS. By default these are placed in the
+ * container with CSS class name `ol-overlaycontainer-stopevent`, but can use
+ * any outside DOM element.
+ *
+ * This is the base class for controls. You can use it for simple custom
+ * controls by creating the element with listeners, creating an instance:
+ * ```js
+ * var myControl = new ol.control.Control({element: myElement});
+ * ```
+ * and then adding this to the map.
+ *
+ * The main advantage of having this as a control rather than a simple separate
+ * DOM element is that preventing propagation is handled for you. Controls
+ * will also be `ol.Object`s in a `ol.Collection`, so you can use their
+ * methods.
+ *
+ * You can also extend this base for your own control class. See
+ * examples/custom-controls for an example of how to do this.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @implements {oli.control.Control}
+ * @param {olx.control.ControlOptions} options Control options.
+ * @api stable
+ */
+ol.control.Control = function(options) {
+
+ ol.Object.call(this);
+
+ /**
+ * @protected
+ * @type {Element}
+ */
+ this.element = options.element ? options.element : null;
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.target_ = null;
+
+ /**
+ * @private
+ * @type {ol.Map}
+ */
+ this.map_ = null;
+
+ /**
+ * @protected
+ * @type {!Array.<ol.EventsKey>}
+ */
+ this.listenerKeys = [];
+
+ /**
+ * @type {function(ol.MapEvent)}
+ */
+ this.render = options.render ? options.render : ol.nullFunction;
+
+ if (options.target) {
+ this.setTarget(options.target);
+ }
+
+};
+ol.inherits(ol.control.Control, ol.Object);
+
+
+/**
+ * @inheritDoc
+ */
+ol.control.Control.prototype.disposeInternal = function() {
+ ol.dom.removeNode(this.element);
+ ol.Object.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * Get the map associated with this control.
+ * @return {ol.Map} Map.
+ * @api stable
+ */
+ol.control.Control.prototype.getMap = function() {
+ return this.map_;
+};
+
+
+/**
+ * Remove the control from its current map and attach it to the new map.
+ * Subclasses may set up event handlers to get notified about changes to
+ * the map here.
+ * @param {ol.Map} map Map.
+ * @api stable
+ */
+ol.control.Control.prototype.setMap = function(map) {
+ if (this.map_) {
+ ol.dom.removeNode(this.element);
+ }
+ for (var i = 0, ii = this.listenerKeys.length; i < ii; ++i) {
+ ol.events.unlistenByKey(this.listenerKeys[i]);
+ }
+ this.listenerKeys.length = 0;
+ this.map_ = map;
+ if (this.map_) {
+ var target = this.target_ ?
+ this.target_ : map.getOverlayContainerStopEvent();
+ target.appendChild(this.element);
+ if (this.render !== ol.nullFunction) {
+ this.listenerKeys.push(ol.events.listen(map,
+ ol.MapEvent.Type.POSTRENDER, this.render, this));
+ }
+ map.render();
+ }
+};
+
+
+/**
+ * This function is used to set a target element for the control. It has no
+ * effect if it is called after the control has been added to the map (i.e.
+ * after `setMap` is called on the control). If no `target` is set in the
+ * options passed to the control constructor and if `setTarget` is not called
+ * then the control is added to the map's overlay container.
+ * @param {Element|string} target Target.
+ * @api
+ */
+ol.control.Control.prototype.setTarget = function(target) {
+ this.target_ = typeof target === 'string' ?
+ document.getElementById(target) :
+ target;
+};
+
+goog.provide('ol.css');
+
+
+/**
+ * The CSS class for hidden feature.
+ *
+ * @const
+ * @type {string}
+ */
+ol.css.CLASS_HIDDEN = 'ol-hidden';
+
+
+/**
+ * The CSS class that we'll give the DOM elements to have them unselectable.
+ *
+ * @const
+ * @type {string}
+ */
+ol.css.CLASS_UNSELECTABLE = 'ol-unselectable';
+
+
+/**
+ * The CSS class for unsupported feature.
+ *
+ * @const
+ * @type {string}
+ */
+ol.css.CLASS_UNSUPPORTED = 'ol-unsupported';
+
+
+/**
+ * The CSS class for controls.
+ *
+ * @const
+ * @type {string}
+ */
+ol.css.CLASS_CONTROL = 'ol-control';
+
+// FIXME handle date line wrap
+
+goog.provide('ol.control.Attribution');
+
+goog.require('ol');
+goog.require('ol.dom');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * Control to show all the attributions associated with the layer sources
+ * in the map. This control is one of the default controls included in maps.
+ * By default it will show in the bottom right portion of the map, but this can
+ * be changed by using a css selector for `.ol-attribution`.
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.AttributionOptions=} opt_options Attribution options.
+ * @api stable
+ */
+ol.control.Attribution = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.ulElement_ = document.createElement('UL');
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.logoLi_ = document.createElement('LI');
+
+ this.ulElement_.appendChild(this.logoLi_);
+ this.logoLi_.style.display = 'none';
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.collapsible_ = options.collapsible !== undefined ?
+ options.collapsible : true;
+
+ if (!this.collapsible_) {
+ this.collapsed_ = false;
+ }
+
+ var className = options.className !== undefined ? options.className : 'ol-attribution';
+
+ var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions';
+
+ var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB';
+
+ if (typeof collapseLabel === 'string') {
+ /**
+ * @private
+ * @type {Node}
+ */
+ this.collapseLabel_ = document.createElement('span');
+ this.collapseLabel_.textContent = collapseLabel;
+ } else {
+ this.collapseLabel_ = collapseLabel;
+ }
+
+ var label = options.label !== undefined ? options.label : 'i';
+
+ if (typeof label === 'string') {
+ /**
+ * @private
+ * @type {Node}
+ */
+ this.label_ = document.createElement('span');
+ this.label_.textContent = label;
+ } else {
+ this.label_ = label;
+ }
+
+
+ var activeLabel = (this.collapsible_ && !this.collapsed_) ?
+ this.collapseLabel_ : this.label_;
+ var button = document.createElement('button');
+ button.setAttribute('type', 'button');
+ button.title = tipLabel;
+ button.appendChild(activeLabel);
+
+ ol.events.listen(button, ol.events.EventType.CLICK, this.handleClick_, this);
+
+ var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
+ ol.css.CLASS_CONTROL +
+ (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') +
+ (this.collapsible_ ? '' : ' ol-uncollapsible');
+ var element = document.createElement('div');
+ element.className = cssClasses;
+ element.appendChild(this.ulElement_);
+ element.appendChild(button);
+
+ var render = options.render ? options.render : ol.control.Attribution.render;
+
+ ol.control.Control.call(this, {
+ element: element,
+ render: render,
+ target: options.target
+ });
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.renderedVisible_ = true;
+
+ /**
+ * @private
+ * @type {Object.<string, Element>}
+ */
+ this.attributionElements_ = {};
+
+ /**
+ * @private
+ * @type {Object.<string, boolean>}
+ */
+ this.attributionElementRenderedVisible_ = {};
+
+ /**
+ * @private
+ * @type {Object.<string, Element>}
+ */
+ this.logoElements_ = {};
+
+};
+ol.inherits(ol.control.Attribution, ol.control.Control);
+
+
+/**
+ * @param {?olx.FrameState} frameState Frame state.
+ * @return {Array.<Object.<string, ol.Attribution>>} Attributions.
+ */
+ol.control.Attribution.prototype.getSourceAttributions = function(frameState) {
+ var i, ii, j, jj, tileRanges, source, sourceAttribution,
+ sourceAttributionKey, sourceAttributions, sourceKey;
+ var intersectsTileRange;
+ var layerStatesArray = frameState.layerStatesArray;
+ /** @type {Object.<string, ol.Attribution>} */
+ var attributions = ol.obj.assign({}, frameState.attributions);
+ /** @type {Object.<string, ol.Attribution>} */
+ var hiddenAttributions = {};
+ var uniqueAttributions = {};
+ var projection = /** @type {!ol.proj.Projection} */ (frameState.viewState.projection);
+ for (i = 0, ii = layerStatesArray.length; i < ii; i++) {
+ source = layerStatesArray[i].layer.getSource();
+ if (!source) {
+ continue;
+ }
+ sourceKey = ol.getUid(source).toString();
+ sourceAttributions = source.getAttributions();
+ if (!sourceAttributions) {
+ continue;
+ }
+ for (j = 0, jj = sourceAttributions.length; j < jj; j++) {
+ sourceAttribution = sourceAttributions[j];
+ sourceAttributionKey = ol.getUid(sourceAttribution).toString();
+ if (sourceAttributionKey in attributions) {
+ continue;
+ }
+ tileRanges = frameState.usedTiles[sourceKey];
+ if (tileRanges) {
+ var tileGrid = /** @type {ol.source.Tile} */ (source).getTileGridForProjection(projection);
+ intersectsTileRange = sourceAttribution.intersectsAnyTileRange(
+ tileRanges, tileGrid, projection);
+ } else {
+ intersectsTileRange = false;
+ }
+ if (intersectsTileRange) {
+ if (sourceAttributionKey in hiddenAttributions) {
+ delete hiddenAttributions[sourceAttributionKey];
+ }
+ var html = sourceAttribution.getHTML();
+ if (!(html in uniqueAttributions)) {
+ uniqueAttributions[html] = true;
+ attributions[sourceAttributionKey] = sourceAttribution;
+ }
+ } else {
+ hiddenAttributions[sourceAttributionKey] = sourceAttribution;
+ }
+ }
+ }
+ return [attributions, hiddenAttributions];
+};
+
+
+/**
+ * Update the attribution element.
+ * @param {ol.MapEvent} mapEvent Map event.
+ * @this {ol.control.Attribution}
+ * @api
+ */
+ol.control.Attribution.render = function(mapEvent) {
+ this.updateElement_(mapEvent.frameState);
+};
+
+
+/**
+ * @private
+ * @param {?olx.FrameState} frameState Frame state.
+ */
+ol.control.Attribution.prototype.updateElement_ = function(frameState) {
+
+ if (!frameState) {
+ if (this.renderedVisible_) {
+ this.element.style.display = 'none';
+ this.renderedVisible_ = false;
+ }
+ return;
+ }
+
+ var attributions = this.getSourceAttributions(frameState);
+ /** @type {Object.<string, ol.Attribution>} */
+ var visibleAttributions = attributions[0];
+ /** @type {Object.<string, ol.Attribution>} */
+ var hiddenAttributions = attributions[1];
+
+ var attributionElement, attributionKey;
+ for (attributionKey in this.attributionElements_) {
+ if (attributionKey in visibleAttributions) {
+ if (!this.attributionElementRenderedVisible_[attributionKey]) {
+ this.attributionElements_[attributionKey].style.display = '';
+ this.attributionElementRenderedVisible_[attributionKey] = true;
+ }
+ delete visibleAttributions[attributionKey];
+ } else if (attributionKey in hiddenAttributions) {
+ if (this.attributionElementRenderedVisible_[attributionKey]) {
+ this.attributionElements_[attributionKey].style.display = 'none';
+ delete this.attributionElementRenderedVisible_[attributionKey];
+ }
+ delete hiddenAttributions[attributionKey];
+ } else {
+ ol.dom.removeNode(this.attributionElements_[attributionKey]);
+ delete this.attributionElements_[attributionKey];
+ delete this.attributionElementRenderedVisible_[attributionKey];
+ }
+ }
+ for (attributionKey in visibleAttributions) {
+ attributionElement = document.createElement('LI');
+ attributionElement.innerHTML =
+ visibleAttributions[attributionKey].getHTML();
+ this.ulElement_.appendChild(attributionElement);
+ this.attributionElements_[attributionKey] = attributionElement;
+ this.attributionElementRenderedVisible_[attributionKey] = true;
+ }
+ for (attributionKey in hiddenAttributions) {
+ attributionElement = document.createElement('LI');
+ attributionElement.innerHTML =
+ hiddenAttributions[attributionKey].getHTML();
+ attributionElement.style.display = 'none';
+ this.ulElement_.appendChild(attributionElement);
+ this.attributionElements_[attributionKey] = attributionElement;
+ }
+
+ var renderVisible =
+ !ol.obj.isEmpty(this.attributionElementRenderedVisible_) ||
+ !ol.obj.isEmpty(frameState.logos);
+ if (this.renderedVisible_ != renderVisible) {
+ this.element.style.display = renderVisible ? '' : 'none';
+ this.renderedVisible_ = renderVisible;
+ }
+ if (renderVisible &&
+ ol.obj.isEmpty(this.attributionElementRenderedVisible_)) {
+ this.element.classList.add('ol-logo-only');
+ } else {
+ this.element.classList.remove('ol-logo-only');
+ }
+
+ this.insertLogos_(frameState);
+
+};
+
+
+/**
+ * @param {?olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.control.Attribution.prototype.insertLogos_ = function(frameState) {
+
+ var logo;
+ var logos = frameState.logos;
+ var logoElements = this.logoElements_;
+
+ for (logo in logoElements) {
+ if (!(logo in logos)) {
+ ol.dom.removeNode(logoElements[logo]);
+ delete logoElements[logo];
+ }
+ }
+
+ var image, logoElement, logoKey;
+ for (logoKey in logos) {
+ var logoValue = logos[logoKey];
+ if (logoValue instanceof HTMLElement) {
+ this.logoLi_.appendChild(logoValue);
+ logoElements[logoKey] = logoValue;
+ }
+ if (!(logoKey in logoElements)) {
+ image = new Image();
+ image.src = logoKey;
+ if (logoValue === '') {
+ logoElement = image;
+ } else {
+ logoElement = document.createElement('a');
+ logoElement.href = logoValue;
+ logoElement.appendChild(image);
+ }
+ this.logoLi_.appendChild(logoElement);
+ logoElements[logoKey] = logoElement;
+ }
+ }
+
+ this.logoLi_.style.display = !ol.obj.isEmpty(logos) ? '' : 'none';
+
+};
+
+
+/**
+ * @param {Event} event The event to handle
+ * @private
+ */
+ol.control.Attribution.prototype.handleClick_ = function(event) {
+ event.preventDefault();
+ this.handleToggle_();
+};
+
+
+/**
+ * @private
+ */
+ol.control.Attribution.prototype.handleToggle_ = function() {
+ this.element.classList.toggle('ol-collapsed');
+ if (this.collapsed_) {
+ ol.dom.replaceNode(this.collapseLabel_, this.label_);
+ } else {
+ ol.dom.replaceNode(this.label_, this.collapseLabel_);
+ }
+ this.collapsed_ = !this.collapsed_;
+};
+
+
+/**
+ * Return `true` if the attribution is collapsible, `false` otherwise.
+ * @return {boolean} True if the widget is collapsible.
+ * @api stable
+ */
+ol.control.Attribution.prototype.getCollapsible = function() {
+ return this.collapsible_;
+};
+
+
+/**
+ * Set whether the attribution should be collapsible.
+ * @param {boolean} collapsible True if the widget is collapsible.
+ * @api stable
+ */
+ol.control.Attribution.prototype.setCollapsible = function(collapsible) {
+ if (this.collapsible_ === collapsible) {
+ return;
+ }
+ this.collapsible_ = collapsible;
+ this.element.classList.toggle('ol-uncollapsible');
+ if (!collapsible && this.collapsed_) {
+ this.handleToggle_();
+ }
+};
+
+
+/**
+ * Collapse or expand the attribution according to the passed parameter. Will
+ * not do anything if the attribution isn't collapsible or if the current
+ * collapsed state is already the one requested.
+ * @param {boolean} collapsed True if the widget is collapsed.
+ * @api stable
+ */
+ol.control.Attribution.prototype.setCollapsed = function(collapsed) {
+ if (!this.collapsible_ || this.collapsed_ === collapsed) {
+ return;
+ }
+ this.handleToggle_();
+};
+
+
+/**
+ * Return `true` when the attribution is currently collapsed or `false`
+ * otherwise.
+ * @return {boolean} True if the widget is collapsed.
+ * @api stable
+ */
+ol.control.Attribution.prototype.getCollapsed = function() {
+ return this.collapsed_;
+};
+
+goog.provide('ol.control.FullScreen');
+
+goog.require('ol');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+
+
+/**
+ * @classdesc
+ * Provides a button that when clicked fills up the full screen with the map.
+ * The full screen source element is by default the element containing the map viewport unless
+ * overriden by providing the `source` option. In which case, the dom
+ * element introduced using this parameter will be displayed in full screen.
+ *
+ * When in full screen mode, a close button is shown to exit full screen mode.
+ * The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to
+ * toggle the map in full screen mode.
+ *
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.FullScreenOptions=} opt_options Options.
+ * @api stable
+ */
+ol.control.FullScreen = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.cssClassName_ = options.className !== undefined ? options.className :
+ 'ol-full-screen';
+
+ var label = options.label !== undefined ? options.label : '\u2922';
+
+ /**
+ * @private
+ * @type {Node}
+ */
+ this.labelNode_ = typeof label === 'string' ?
+ document.createTextNode(label) : label;
+
+ var labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7';
+
+ /**
+ * @private
+ * @type {Node}
+ */
+ this.labelActiveNode_ = typeof labelActive === 'string' ?
+ document.createTextNode(labelActive) : labelActive;
+
+ var tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen';
+ var button = document.createElement('button');
+ button.className = this.cssClassName_ + '-' + ol.control.FullScreen.isFullScreen();
+ button.setAttribute('type', 'button');
+ button.title = tipLabel;
+ button.appendChild(this.labelNode_);
+
+ ol.events.listen(button, ol.events.EventType.CLICK,
+ this.handleClick_, this);
+
+ var cssClasses = this.cssClassName_ + ' ' + ol.css.CLASS_UNSELECTABLE +
+ ' ' + ol.css.CLASS_CONTROL + ' ' +
+ (!ol.control.FullScreen.isFullScreenSupported() ? ol.css.CLASS_UNSUPPORTED : '');
+ var element = document.createElement('div');
+ element.className = cssClasses;
+ element.appendChild(button);
+
+ ol.control.Control.call(this, {
+ element: element,
+ target: options.target
+ });
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.keys_ = options.keys !== undefined ? options.keys : false;
+
+ /**
+ * @private
+ * @type {Element|string|undefined}
+ */
+ this.source_ = options.source;
+
+};
+ol.inherits(ol.control.FullScreen, ol.control.Control);
+
+
+/**
+ * @param {Event} event The event to handle
+ * @private
+ */
+ol.control.FullScreen.prototype.handleClick_ = function(event) {
+ event.preventDefault();
+ this.handleFullScreen_();
+};
+
+
+/**
+ * @private
+ */
+ol.control.FullScreen.prototype.handleFullScreen_ = function() {
+ if (!ol.control.FullScreen.isFullScreenSupported()) {
+ return;
+ }
+ var map = this.getMap();
+ if (!map) {
+ return;
+ }
+ if (ol.control.FullScreen.isFullScreen()) {
+ ol.control.FullScreen.exitFullScreen();
+ } else {
+ var element;
+ if (this.source_) {
+ element = typeof this.source_ === 'string' ?
+ document.getElementById(this.source_) :
+ this.source_;
+ } else {
+ element = map.getTargetElement();
+ }
+ if (this.keys_) {
+ ol.control.FullScreen.requestFullScreenWithKeys(element);
+
+ } else {
+ ol.control.FullScreen.requestFullScreen(element);
+ }
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.control.FullScreen.prototype.handleFullScreenChange_ = function() {
+ var button = this.element.firstElementChild;
+ var map = this.getMap();
+ if (ol.control.FullScreen.isFullScreen()) {
+ button.className = this.cssClassName_ + '-true';
+ ol.dom.replaceNode(this.labelActiveNode_, this.labelNode_);
+ } else {
+ button.className = this.cssClassName_ + '-false';
+ ol.dom.replaceNode(this.labelNode_, this.labelActiveNode_);
+ }
+ if (map) {
+ map.updateSize();
+ }
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.control.FullScreen.prototype.setMap = function(map) {
+ ol.control.Control.prototype.setMap.call(this, map);
+ if (map) {
+ this.listenerKeys.push(ol.events.listen(document,
+ ol.control.FullScreen.getChangeType_(),
+ this.handleFullScreenChange_, this)
+ );
+ }
+};
+
+/**
+ * @return {boolean} Fullscreen is supported by the current platform.
+ */
+ol.control.FullScreen.isFullScreenSupported = function() {
+ var body = document.body;
+ return !!(
+ body.webkitRequestFullscreen ||
+ (body.mozRequestFullScreen && document.mozFullScreenEnabled) ||
+ (body.msRequestFullscreen && document.msFullscreenEnabled) ||
+ (body.requestFullscreen && document.fullscreenEnabled)
+ );
+};
+
+/**
+ * @return {boolean} Element is currently in fullscreen.
+ */
+ol.control.FullScreen.isFullScreen = function() {
+ return !!(
+ document.webkitIsFullScreen || document.mozFullScreen ||
+ document.msFullscreenElement || document.fullscreenElement
+ );
+};
+
+/**
+ * Request to fullscreen an element.
+ * @param {Node} element Element to request fullscreen
+ */
+ol.control.FullScreen.requestFullScreen = function(element) {
+ if (element.requestFullscreen) {
+ element.requestFullscreen();
+ } else if (element.msRequestFullscreen) {
+ element.msRequestFullscreen();
+ } else if (element.mozRequestFullScreen) {
+ element.mozRequestFullScreen();
+ } else if (element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen();
+ }
+};
+
+/**
+ * Request to fullscreen an element with keyboard input.
+ * @param {Node} element Element to request fullscreen
+ */
+ol.control.FullScreen.requestFullScreenWithKeys = function(element) {
+ if (element.mozRequestFullScreenWithKeys) {
+ element.mozRequestFullScreenWithKeys();
+ } else if (element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
+ } else {
+ ol.control.FullScreen.requestFullScreen(element);
+ }
+};
+
+/**
+ * Exit fullscreen.
+ */
+ol.control.FullScreen.exitFullScreen = function() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ }
+};
+
+/**
+ * @return {string} Change type.
+ * @private
+ */
+ol.control.FullScreen.getChangeType_ = (function() {
+ var changeType;
+ return function() {
+ if (!changeType) {
+ var body = document.body;
+ if (body.webkitRequestFullscreen) {
+ changeType = 'webkitfullscreenchange';
+ } else if (body.mozRequestFullScreen) {
+ changeType = 'mozfullscreenchange';
+ } else if (body.msRequestFullscreen) {
+ changeType = 'MSFullscreenChange';
+ } else if (body.requestFullscreen) {
+ changeType = 'fullscreenchange';
+ }
+ }
+ return changeType;
+ };
+})();
+
+goog.provide('ol.control.Rotate');
+
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+goog.require('ol.easing');
+
+
+/**
+ * @classdesc
+ * A button control to reset rotation to 0.
+ * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css
+ * selector is added to the button when the rotation is 0.
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.RotateOptions=} opt_options Rotate options.
+ * @api stable
+ */
+ol.control.Rotate = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var className = options.className !== undefined ? options.className : 'ol-rotate';
+
+ var label = options.label !== undefined ? options.label : '\u21E7';
+
+ /**
+ * @type {Element}
+ * @private
+ */
+ this.label_ = null;
+
+ if (typeof label === 'string') {
+ this.label_ = document.createElement('span');
+ this.label_.className = 'ol-compass';
+ this.label_.textContent = label;
+ } else {
+ this.label_ = label;
+ this.label_.classList.add('ol-compass');
+ }
+
+ var tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation';
+
+ var button = document.createElement('button');
+ button.className = className + '-reset';
+ button.setAttribute('type', 'button');
+ button.title = tipLabel;
+ button.appendChild(this.label_);
+
+ ol.events.listen(button, ol.events.EventType.CLICK,
+ ol.control.Rotate.prototype.handleClick_, this);
+
+ var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
+ ol.css.CLASS_CONTROL;
+ var element = document.createElement('div');
+ element.className = cssClasses;
+ element.appendChild(button);
+
+ var render = options.render ? options.render : ol.control.Rotate.render;
+
+ this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;
+
+ ol.control.Control.call(this, {
+ element: element,
+ render: render,
+ target: options.target
+ });
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 250;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.rotation_ = undefined;
+
+ if (this.autoHide_) {
+ this.element.classList.add(ol.css.CLASS_HIDDEN);
+ }
+
+};
+ol.inherits(ol.control.Rotate, ol.control.Control);
+
+
+/**
+ * @param {Event} event The event to handle
+ * @private
+ */
+ol.control.Rotate.prototype.handleClick_ = function(event) {
+ event.preventDefault();
+ if (this.callResetNorth_ !== undefined) {
+ this.callResetNorth_();
+ } else {
+ this.resetNorth_();
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.control.Rotate.prototype.resetNorth_ = function() {
+ var map = this.getMap();
+ var view = map.getView();
+ if (!view) {
+ // the map does not have a view, so we can't act
+ // upon it
+ return;
+ }
+ var currentRotation = view.getRotation();
+ if (currentRotation !== undefined) {
+ if (this.duration_ > 0) {
+ currentRotation = currentRotation % (2 * Math.PI);
+ if (currentRotation < -Math.PI) {
+ currentRotation += 2 * Math.PI;
+ }
+ if (currentRotation > Math.PI) {
+ currentRotation -= 2 * Math.PI;
+ }
+ view.animate({
+ rotation: 0,
+ duration: this.duration_,
+ easing: ol.easing.easeOut
+ });
+ } else {
+ view.setRotation(0);
+ }
+ }
+};
+
+
+/**
+ * Update the rotate control element.
+ * @param {ol.MapEvent} mapEvent Map event.
+ * @this {ol.control.Rotate}
+ * @api
+ */
+ol.control.Rotate.render = function(mapEvent) {
+ var frameState = mapEvent.frameState;
+ if (!frameState) {
+ return;
+ }
+ var rotation = frameState.viewState.rotation;
+ if (rotation != this.rotation_) {
+ var transform = 'rotate(' + rotation + 'rad)';
+ if (this.autoHide_) {
+ var contains = this.element.classList.contains(ol.css.CLASS_HIDDEN);
+ if (!contains && rotation === 0) {
+ this.element.classList.add(ol.css.CLASS_HIDDEN);
+ } else if (contains && rotation !== 0) {
+ this.element.classList.remove(ol.css.CLASS_HIDDEN);
+ }
+ }
+ this.label_.style.msTransform = transform;
+ this.label_.style.webkitTransform = transform;
+ this.label_.style.transform = transform;
+ }
+ this.rotation_ = rotation;
+};
+
+goog.provide('ol.control.Zoom');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+goog.require('ol.easing');
+
+
+/**
+ * @classdesc
+ * A control with 2 buttons, one for zoom in and one for zoom out.
+ * This control is one of the default controls of a map. To style this control
+ * use css selectors `.ol-zoom-in` and `.ol-zoom-out`.
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.ZoomOptions=} opt_options Zoom options.
+ * @api stable
+ */
+ol.control.Zoom = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var className = options.className !== undefined ? options.className : 'ol-zoom';
+
+ var delta = options.delta !== undefined ? options.delta : 1;
+
+ var zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+';
+ var zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212';
+
+ var zoomInTipLabel = options.zoomInTipLabel !== undefined ?
+ options.zoomInTipLabel : 'Zoom in';
+ var zoomOutTipLabel = options.zoomOutTipLabel !== undefined ?
+ options.zoomOutTipLabel : 'Zoom out';
+
+ var inElement = document.createElement('button');
+ inElement.className = className + '-in';
+ inElement.setAttribute('type', 'button');
+ inElement.title = zoomInTipLabel;
+ inElement.appendChild(
+ typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel
+ );
+
+ ol.events.listen(inElement, ol.events.EventType.CLICK,
+ ol.control.Zoom.prototype.handleClick_.bind(this, delta));
+
+ var outElement = document.createElement('button');
+ outElement.className = className + '-out';
+ outElement.setAttribute('type', 'button');
+ outElement.title = zoomOutTipLabel;
+ outElement.appendChild(
+ typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel
+ );
+
+ ol.events.listen(outElement, ol.events.EventType.CLICK,
+ ol.control.Zoom.prototype.handleClick_.bind(this, -delta));
+
+ var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
+ ol.css.CLASS_CONTROL;
+ var element = document.createElement('div');
+ element.className = cssClasses;
+ element.appendChild(inElement);
+ element.appendChild(outElement);
+
+ ol.control.Control.call(this, {
+ element: element,
+ target: options.target
+ });
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 250;
+
+};
+ol.inherits(ol.control.Zoom, ol.control.Control);
+
+
+/**
+ * @param {number} delta Zoom delta.
+ * @param {Event} event The event to handle
+ * @private
+ */
+ol.control.Zoom.prototype.handleClick_ = function(delta, event) {
+ event.preventDefault();
+ this.zoomByDelta_(delta);
+};
+
+
+/**
+ * @param {number} delta Zoom delta.
+ * @private
+ */
+ol.control.Zoom.prototype.zoomByDelta_ = function(delta) {
+ var map = this.getMap();
+ var view = map.getView();
+ if (!view) {
+ // the map does not have a view, so we can't act
+ // upon it
+ return;
+ }
+ var currentResolution = view.getResolution();
+ if (currentResolution) {
+ var newResolution = view.constrainResolution(currentResolution, delta);
+ if (this.duration_ > 0) {
+ if (view.getAnimating()) {
+ view.cancelAnimations();
+ }
+ view.animate({
+ resolution: newResolution,
+ duration: this.duration_,
+ easing: ol.easing.easeOut
+ });
+ } else {
+ view.setResolution(newResolution);
+ }
+ }
+};
+
+goog.provide('ol.control');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.control.Attribution');
+goog.require('ol.control.Rotate');
+goog.require('ol.control.Zoom');
+
+
+/**
+ * Set of controls included in maps by default. Unless configured otherwise,
+ * this returns a collection containing an instance of each of the following
+ * controls:
+ * * {@link ol.control.Zoom}
+ * * {@link ol.control.Rotate}
+ * * {@link ol.control.Attribution}
+ *
+ * @param {olx.control.DefaultsOptions=} opt_options Defaults options.
+ * @return {ol.Collection.<ol.control.Control>} Controls.
+ * @api stable
+ */
+ol.control.defaults = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var controls = new ol.Collection();
+
+ var zoomControl = options.zoom !== undefined ? options.zoom : true;
+ if (zoomControl) {
+ controls.push(new ol.control.Zoom(options.zoomOptions));
+ }
+
+ var rotateControl = options.rotate !== undefined ? options.rotate : true;
+ if (rotateControl) {
+ controls.push(new ol.control.Rotate(options.rotateOptions));
+ }
+
+ var attributionControl = options.attribution !== undefined ?
+ options.attribution : true;
+ if (attributionControl) {
+ controls.push(new ol.control.Attribution(options.attributionOptions));
+ }
+
+ return controls;
+
+};
+
+// FIXME should listen on appropriate pane, once it is defined
+
+goog.provide('ol.control.MousePosition');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.Object');
+goog.require('ol.control.Control');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * A control to show the 2D coordinates of the mouse cursor. By default, these
+ * are in the view projection, but can be in any supported projection.
+ * By default the control is shown in the top right corner of the map, but this
+ * can be changed by using the css selector `.ol-mouse-position`.
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.MousePositionOptions=} opt_options Mouse position
+ * options.
+ * @api stable
+ */
+ol.control.MousePosition = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var element = document.createElement('DIV');
+ element.className = options.className !== undefined ? options.className : 'ol-mouse-position';
+
+ var render = options.render ?
+ options.render : ol.control.MousePosition.render;
+
+ ol.control.Control.call(this, {
+ element: element,
+ render: render,
+ target: options.target
+ });
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.control.MousePosition.Property.PROJECTION),
+ this.handleProjectionChanged_, this);
+
+ if (options.coordinateFormat) {
+ this.setCoordinateFormat(options.coordinateFormat);
+ }
+ if (options.projection) {
+ this.setProjection(ol.proj.get(options.projection));
+ }
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.undefinedHTML_ = options.undefinedHTML !== undefined ? options.undefinedHTML : '';
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.renderedHTML_ = element.innerHTML;
+
+ /**
+ * @private
+ * @type {ol.proj.Projection}
+ */
+ this.mapProjection_ = null;
+
+ /**
+ * @private
+ * @type {?ol.TransformFunction}
+ */
+ this.transform_ = null;
+
+ /**
+ * @private
+ * @type {ol.Pixel}
+ */
+ this.lastMouseMovePixel_ = null;
+
+};
+ol.inherits(ol.control.MousePosition, ol.control.Control);
+
+
+/**
+ * Update the mouseposition element.
+ * @param {ol.MapEvent} mapEvent Map event.
+ * @this {ol.control.MousePosition}
+ * @api
+ */
+ol.control.MousePosition.render = function(mapEvent) {
+ var frameState = mapEvent.frameState;
+ if (!frameState) {
+ this.mapProjection_ = null;
+ } else {
+ if (this.mapProjection_ != frameState.viewState.projection) {
+ this.mapProjection_ = frameState.viewState.projection;
+ this.transform_ = null;
+ }
+ }
+ this.updateHTML_(this.lastMouseMovePixel_);
+};
+
+
+/**
+ * @private
+ */
+ol.control.MousePosition.prototype.handleProjectionChanged_ = function() {
+ this.transform_ = null;
+};
+
+
+/**
+ * Return the coordinate format type used to render the current position or
+ * undefined.
+ * @return {ol.CoordinateFormatType|undefined} The format to render the current
+ * position in.
+ * @observable
+ * @api stable
+ */
+ol.control.MousePosition.prototype.getCoordinateFormat = function() {
+ return /** @type {ol.CoordinateFormatType|undefined} */ (
+ this.get(ol.control.MousePosition.Property.COORDINATE_FORMAT));
+};
+
+
+/**
+ * Return the projection that is used to report the mouse position.
+ * @return {ol.proj.Projection|undefined} The projection to report mouse
+ * position in.
+ * @observable
+ * @api stable
+ */
+ol.control.MousePosition.prototype.getProjection = function() {
+ return /** @type {ol.proj.Projection|undefined} */ (
+ this.get(ol.control.MousePosition.Property.PROJECTION));
+};
+
+
+/**
+ * @param {Event} event Browser event.
+ * @protected
+ */
+ol.control.MousePosition.prototype.handleMouseMove = function(event) {
+ var map = this.getMap();
+ this.lastMouseMovePixel_ = map.getEventPixel(event);
+ this.updateHTML_(this.lastMouseMovePixel_);
+};
+
+
+/**
+ * @param {Event} event Browser event.
+ * @protected
+ */
+ol.control.MousePosition.prototype.handleMouseOut = function(event) {
+ this.updateHTML_(null);
+ this.lastMouseMovePixel_ = null;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.control.MousePosition.prototype.setMap = function(map) {
+ ol.control.Control.prototype.setMap.call(this, map);
+ if (map) {
+ var viewport = map.getViewport();
+ this.listenerKeys.push(
+ ol.events.listen(viewport, ol.events.EventType.MOUSEMOVE,
+ this.handleMouseMove, this),
+ ol.events.listen(viewport, ol.events.EventType.MOUSEOUT,
+ this.handleMouseOut, this)
+ );
+ }
+};
+
+
+/**
+ * Set the coordinate format type used to render the current position.
+ * @param {ol.CoordinateFormatType} format The format to render the current
+ * position in.
+ * @observable
+ * @api stable
+ */
+ol.control.MousePosition.prototype.setCoordinateFormat = function(format) {
+ this.set(ol.control.MousePosition.Property.COORDINATE_FORMAT, format);
+};
+
+
+/**
+ * Set the projection that is used to report the mouse position.
+ * @param {ol.proj.Projection} projection The projection to report mouse
+ * position in.
+ * @observable
+ * @api stable
+ */
+ol.control.MousePosition.prototype.setProjection = function(projection) {
+ this.set(ol.control.MousePosition.Property.PROJECTION, projection);
+};
+
+
+/**
+ * @param {?ol.Pixel} pixel Pixel.
+ * @private
+ */
+ol.control.MousePosition.prototype.updateHTML_ = function(pixel) {
+ var html = this.undefinedHTML_;
+ if (pixel && this.mapProjection_) {
+ if (!this.transform_) {
+ var projection = this.getProjection();
+ if (projection) {
+ this.transform_ = ol.proj.getTransformFromProjections(
+ this.mapProjection_, projection);
+ } else {
+ this.transform_ = ol.proj.identityTransform;
+ }
+ }
+ var map = this.getMap();
+ var coordinate = map.getCoordinateFromPixel(pixel);
+ if (coordinate) {
+ this.transform_(coordinate, coordinate);
+ var coordinateFormat = this.getCoordinateFormat();
+ if (coordinateFormat) {
+ html = coordinateFormat(coordinate);
+ } else {
+ html = coordinate.toString();
+ }
+ }
+ }
+ if (!this.renderedHTML_ || html != this.renderedHTML_) {
+ this.element.innerHTML = html;
+ this.renderedHTML_ = html;
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.control.MousePosition.Property = {
+ PROJECTION: 'projection',
+ COORDINATE_FORMAT: 'coordinateFormat'
+};
+
+goog.provide('ol.MapBrowserEvent');
+
+goog.require('ol');
+goog.require('ol.MapEvent');
+goog.require('ol.events.EventType');
+
+
+/**
+ * @classdesc
+ * Events emitted as map browser events are instances of this type.
+ * See {@link ol.Map} for which events trigger a map browser event.
+ *
+ * @constructor
+ * @extends {ol.MapEvent}
+ * @implements {oli.MapBrowserEvent}
+ * @param {string} type Event type.
+ * @param {ol.Map} map Map.
+ * @param {Event} browserEvent Browser event.
+ * @param {boolean=} opt_dragging Is the map currently being dragged?
+ * @param {?olx.FrameState=} opt_frameState Frame state.
+ */
+ol.MapBrowserEvent = function(type, map, browserEvent, opt_dragging,
+ opt_frameState) {
+
+ ol.MapEvent.call(this, type, map, opt_frameState);
+
+ /**
+ * The original browser event.
+ * @const
+ * @type {Event}
+ * @api stable
+ */
+ this.originalEvent = browserEvent;
+
+ /**
+ * The map pixel relative to the viewport corresponding to the original browser event.
+ * @type {ol.Pixel}
+ * @api stable
+ */
+ this.pixel = map.getEventPixel(browserEvent);
+
+ /**
+ * The coordinate in view projection corresponding to the original browser event.
+ * @type {ol.Coordinate}
+ * @api stable
+ */
+ this.coordinate = map.getCoordinateFromPixel(this.pixel);
+
+ /**
+ * Indicates if the map is currently being dragged. Only set for
+ * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`.
+ *
+ * @type {boolean}
+ * @api stable
+ */
+ this.dragging = opt_dragging !== undefined ? opt_dragging : false;
+
+};
+ol.inherits(ol.MapBrowserEvent, ol.MapEvent);
+
+
+/**
+ * Prevents the default browser action.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
+ * @override
+ * @api stable
+ */
+ol.MapBrowserEvent.prototype.preventDefault = function() {
+ ol.MapEvent.prototype.preventDefault.call(this);
+ this.originalEvent.preventDefault();
+};
+
+
+/**
+ * Prevents further propagation of the current event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
+ * @override
+ * @api stable
+ */
+ol.MapBrowserEvent.prototype.stopPropagation = function() {
+ ol.MapEvent.prototype.stopPropagation.call(this);
+ this.originalEvent.stopPropagation();
+};
+
+
+/**
+ * Constants for event names.
+ * @enum {string}
+ */
+ol.MapBrowserEvent.EventType = {
+
+ /**
+ * A true single click with no dragging and no double click. Note that this
+ * event is delayed by 250 ms to ensure that it is not a double click.
+ * @event ol.MapBrowserEvent#singleclick
+ * @api stable
+ */
+ SINGLECLICK: 'singleclick',
+
+ /**
+ * A click with no dragging. A double click will fire two of this.
+ * @event ol.MapBrowserEvent#click
+ * @api stable
+ */
+ CLICK: ol.events.EventType.CLICK,
+
+ /**
+ * A true double click, with no dragging.
+ * @event ol.MapBrowserEvent#dblclick
+ * @api stable
+ */
+ DBLCLICK: ol.events.EventType.DBLCLICK,
+
+ /**
+ * Triggered when a pointer is dragged.
+ * @event ol.MapBrowserEvent#pointerdrag
+ * @api
+ */
+ POINTERDRAG: 'pointerdrag',
+
+ /**
+ * Triggered when a pointer is moved. Note that on touch devices this is
+ * triggered when the map is panned, so is not the same as mousemove.
+ * @event ol.MapBrowserEvent#pointermove
+ * @api stable
+ */
+ POINTERMOVE: 'pointermove',
+
+ POINTERDOWN: 'pointerdown',
+ POINTERUP: 'pointerup',
+ POINTEROVER: 'pointerover',
+ POINTEROUT: 'pointerout',
+ POINTERENTER: 'pointerenter',
+ POINTERLEAVE: 'pointerleave',
+ POINTERCANCEL: 'pointercancel'
+};
+
+goog.provide('ol.MapBrowserPointerEvent');
+
+goog.require('ol');
+goog.require('ol.MapBrowserEvent');
+
+
+/**
+ * @constructor
+ * @extends {ol.MapBrowserEvent}
+ * @param {string} type Event type.
+ * @param {ol.Map} map Map.
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @param {boolean=} opt_dragging Is the map currently being dragged?
+ * @param {?olx.FrameState=} opt_frameState Frame state.
+ */
+ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_dragging,
+ opt_frameState) {
+
+ ol.MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging,
+ opt_frameState);
+
+ /**
+ * @const
+ * @type {ol.pointer.PointerEvent}
+ */
+ this.pointerEvent = pointerEvent;
+
+};
+ol.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent);
+
+goog.provide('ol.pointer.EventType');
+
+
+/**
+ * Constants for event names.
+ * @enum {string}
+ */
+ol.pointer.EventType = {
+ POINTERMOVE: 'pointermove',
+ POINTERDOWN: 'pointerdown',
+ POINTERUP: 'pointerup',
+ POINTEROVER: 'pointerover',
+ POINTEROUT: 'pointerout',
+ POINTERENTER: 'pointerenter',
+ POINTERLEAVE: 'pointerleave',
+ POINTERCANCEL: 'pointercancel'
+};
+
+goog.provide('ol.webgl');
+
+
+/** Constants taken from goog.webgl
+ */
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.ONE = 1;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.SRC_ALPHA = 0x0302;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.COLOR_ATTACHMENT0 = 0x8CE0;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.COLOR_BUFFER_BIT = 0x00004000;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TRIANGLES = 0x0004;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TRIANGLE_STRIP = 0x0005;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.ONE_MINUS_SRC_ALPHA = 0x0303;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.ARRAY_BUFFER = 0x8892;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.ELEMENT_ARRAY_BUFFER = 0x8893;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.STREAM_DRAW = 0x88E0;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.STATIC_DRAW = 0x88E4;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.DYNAMIC_DRAW = 0x88E8;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.CULL_FACE = 0x0B44;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.BLEND = 0x0BE2;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.STENCIL_TEST = 0x0B90;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.DEPTH_TEST = 0x0B71;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.SCISSOR_TEST = 0x0C11;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.UNSIGNED_BYTE = 0x1401;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.UNSIGNED_SHORT = 0x1403;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.UNSIGNED_INT = 0x1405;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.FLOAT = 0x1406;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.RGBA = 0x1908;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.FRAGMENT_SHADER = 0x8B30;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.VERTEX_SHADER = 0x8B31;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.LINK_STATUS = 0x8B82;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.LINEAR = 0x2601;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TEXTURE_MAG_FILTER = 0x2800;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TEXTURE_MIN_FILTER = 0x2801;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TEXTURE_WRAP_S = 0x2802;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TEXTURE_WRAP_T = 0x2803;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TEXTURE_2D = 0x0DE1;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.TEXTURE0 = 0x84C0;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.CLAMP_TO_EDGE = 0x812F;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.COMPILE_STATUS = 0x8B81;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.webgl.FRAMEBUFFER = 0x8D40;
+
+
+/** end of goog.webgl constants
+ */
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.<string>}
+ */
+ol.webgl.CONTEXT_IDS_ = [
+ 'experimental-webgl',
+ 'webgl',
+ 'webkit-3d',
+ 'moz-webgl'
+];
+
+
+/**
+ * @param {HTMLCanvasElement} canvas Canvas.
+ * @param {Object=} opt_attributes Attributes.
+ * @return {WebGLRenderingContext} WebGL rendering context.
+ */
+ol.webgl.getContext = function(canvas, opt_attributes) {
+ var context, i, ii = ol.webgl.CONTEXT_IDS_.length;
+ for (i = 0; i < ii; ++i) {
+ try {
+ context = canvas.getContext(ol.webgl.CONTEXT_IDS_[i], opt_attributes);
+ if (context) {
+ return /** @type {!WebGLRenderingContext} */ (context);
+ }
+ } catch (e) {
+ // pass
+ }
+ }
+ return null;
+};
+
+goog.provide('ol.has');
+
+goog.require('ol');
+goog.require('ol.webgl');
+
+var ua = typeof navigator !== 'undefined' ?
+ navigator.userAgent.toLowerCase() : '';
+
+/**
+ * User agent string says we are dealing with Firefox as browser.
+ * @type {boolean}
+ */
+ol.has.FIREFOX = ua.indexOf('firefox') !== -1;
+
+/**
+ * User agent string says we are dealing with Safari as browser.
+ * @type {boolean}
+ */
+ol.has.SAFARI = ua.indexOf('safari') !== -1 && ua.indexOf('chrom') == -1;
+
+/**
+ * User agent string says we are dealing with a WebKit engine.
+ * @type {boolean}
+ */
+ol.has.WEBKIT = ua.indexOf('webkit') !== -1 && ua.indexOf('edge') == -1;
+
+/**
+ * User agent string says we are dealing with a Mac as platform.
+ * @type {boolean}
+ */
+ol.has.MAC = ua.indexOf('macintosh') !== -1;
+
+
+/**
+ * The ratio between physical pixels and device-independent pixels
+ * (dips) on the device (`window.devicePixelRatio`).
+ * @const
+ * @type {number}
+ * @api stable
+ */
+ol.has.DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
+
+
+/**
+ * True if the browser's Canvas implementation implements {get,set}LineDash.
+ * @type {boolean}
+ */
+ol.has.CANVAS_LINE_DASH = false;
+
+
+/**
+ * True if both the library and browser support Canvas. Always `false`
+ * if `ol.ENABLE_CANVAS` is set to `false` at compile time.
+ * @const
+ * @type {boolean}
+ * @api stable
+ */
+ol.has.CANVAS = ol.ENABLE_CANVAS && (
+ /**
+ * @return {boolean} Canvas supported.
+ */
+ function() {
+ if (!('HTMLCanvasElement' in window)) {
+ return false;
+ }
+ try {
+ var context = document.createElement('CANVAS').getContext('2d');
+ if (!context) {
+ return false;
+ } else {
+ if (context.setLineDash !== undefined) {
+ ol.has.CANVAS_LINE_DASH = true;
+ }
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+ })();
+
+
+/**
+ * Indicates if DeviceOrientation is supported in the user's browser.
+ * @const
+ * @type {boolean}
+ * @api stable
+ */
+ol.has.DEVICE_ORIENTATION = 'DeviceOrientationEvent' in window;
+
+
+/**
+ * Is HTML5 geolocation supported in the current browser?
+ * @const
+ * @type {boolean}
+ * @api stable
+ */
+ol.has.GEOLOCATION = 'geolocation' in navigator;
+
+
+/**
+ * True if browser supports touch events.
+ * @const
+ * @type {boolean}
+ * @api stable
+ */
+ol.has.TOUCH = ol.ASSUME_TOUCH || 'ontouchstart' in window;
+
+
+/**
+ * True if browser supports pointer events.
+ * @const
+ * @type {boolean}
+ */
+ol.has.POINTER = 'PointerEvent' in window;
+
+
+/**
+ * True if browser supports ms pointer events (IE 10).
+ * @const
+ * @type {boolean}
+ */
+ol.has.MSPOINTER = !!(navigator.msPointerEnabled);
+
+
+/**
+ * True if both OpenLayers and browser support WebGL. Always `false`
+ * if `ol.ENABLE_WEBGL` is set to `false` at compile time.
+ * @const
+ * @type {boolean}
+ * @api stable
+ */
+ol.has.WEBGL;
+
+
+(function() {
+ if (ol.ENABLE_WEBGL) {
+ var hasWebGL = false;
+ var textureSize;
+ var /** @type {Array.<string>} */ extensions = [];
+
+ if ('WebGLRenderingContext' in window) {
+ try {
+ var canvas = /** @type {HTMLCanvasElement} */
+ (document.createElement('CANVAS'));
+ var gl = ol.webgl.getContext(canvas, {
+ failIfMajorPerformanceCaveat: true
+ });
+ if (gl) {
+ hasWebGL = true;
+ textureSize = /** @type {number} */
+ (gl.getParameter(gl.MAX_TEXTURE_SIZE));
+ extensions = gl.getSupportedExtensions();
+ }
+ } catch (e) {
+ // pass
+ }
+ }
+ ol.has.WEBGL = hasWebGL;
+ ol.WEBGL_EXTENSIONS = extensions;
+ ol.WEBGL_MAX_TEXTURE_SIZE = textureSize;
+ }
+})();
+
+goog.provide('ol.pointer.EventSource');
+
+
+/**
+ * @param {ol.pointer.PointerEventHandler} dispatcher Event handler.
+ * @param {!Object.<string, function(Event)>} mapping Event
+ * mapping.
+ * @constructor
+ */
+ol.pointer.EventSource = function(dispatcher, mapping) {
+ /**
+ * @type {ol.pointer.PointerEventHandler}
+ */
+ this.dispatcher = dispatcher;
+
+ /**
+ * @private
+ * @const
+ * @type {!Object.<string, function(Event)>}
+ */
+ this.mapping_ = mapping;
+};
+
+
+/**
+ * List of events supported by this source.
+ * @return {Array.<string>} Event names
+ */
+ol.pointer.EventSource.prototype.getEvents = function() {
+ return Object.keys(this.mapping_);
+};
+
+
+/**
+ * Returns a mapping between the supported event types and
+ * the handlers that should handle an event.
+ * @return {Object.<string, function(Event)>}
+ * Event/Handler mapping
+ */
+ol.pointer.EventSource.prototype.getMapping = function() {
+ return this.mapping_;
+};
+
+
+/**
+ * Returns the handler that should handle a given event type.
+ * @param {string} eventType The event type.
+ * @return {function(Event)} Handler
+ */
+ol.pointer.EventSource.prototype.getHandlerForEvent = function(eventType) {
+ return this.mapping_[eventType];
+};
+
+// Based on https://github.com/Polymer/PointerEvents
+
+// Copyright (c) 2013 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use 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 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 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.
+
+goog.provide('ol.pointer.MouseSource');
+
+goog.require('ol');
+goog.require('ol.pointer.EventSource');
+
+
+/**
+ * @param {ol.pointer.PointerEventHandler} dispatcher Event handler.
+ * @constructor
+ * @extends {ol.pointer.EventSource}
+ */
+ol.pointer.MouseSource = function(dispatcher) {
+ var mapping = {
+ 'mousedown': this.mousedown,
+ 'mousemove': this.mousemove,
+ 'mouseup': this.mouseup,
+ 'mouseover': this.mouseover,
+ 'mouseout': this.mouseout
+ };
+ ol.pointer.EventSource.call(this, dispatcher, mapping);
+
+ /**
+ * @const
+ * @type {!Object.<string, Event|Object>}
+ */
+ this.pointerMap = dispatcher.pointerMap;
+
+ /**
+ * @const
+ * @type {Array.<ol.Pixel>}
+ */
+ this.lastTouches = [];
+};
+ol.inherits(ol.pointer.MouseSource, ol.pointer.EventSource);
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.pointer.MouseSource.POINTER_ID = 1;
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.pointer.MouseSource.POINTER_TYPE = 'mouse';
+
+
+/**
+ * Radius around touchend that swallows mouse events.
+ *
+ * @const
+ * @type {number}
+ */
+ol.pointer.MouseSource.DEDUP_DIST = 25;
+
+
+/**
+ * Detect if a mouse event was simulated from a touch by
+ * checking if previously there was a touch event at the
+ * same position.
+ *
+ * FIXME - Known problem with the native Android browser on
+ * Samsung GT-I9100 (Android 4.1.2):
+ * In case the page is scrolled, this function does not work
+ * correctly when a canvas is used (WebGL or canvas renderer).
+ * Mouse listeners on canvas elements (for this browser), create
+ * two mouse events: One 'good' and one 'bad' one (on other browsers or
+ * when a div is used, there is only one event). For the 'bad' one,
+ * clientX/clientY and also pageX/pageY are wrong when the page
+ * is scrolled. Because of that, this function can not detect if
+ * the events were simulated from a touch event. As result, a
+ * pointer event at a wrong position is dispatched, which confuses
+ * the map interactions.
+ * It is unclear, how one can get the correct position for the event
+ * or detect that the positions are invalid.
+ *
+ * @private
+ * @param {Event} inEvent The in event.
+ * @return {boolean} True, if the event was generated by a touch.
+ */
+ol.pointer.MouseSource.prototype.isEventSimulatedFromTouch_ = function(inEvent) {
+ var lts = this.lastTouches;
+ var x = inEvent.clientX, y = inEvent.clientY;
+ for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
+ // simulated mouse events will be swallowed near a primary touchend
+ var dx = Math.abs(x - t[0]), dy = Math.abs(y - t[1]);
+ if (dx <= ol.pointer.MouseSource.DEDUP_DIST &&
+ dy <= ol.pointer.MouseSource.DEDUP_DIST) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Creates a copy of the original event that will be used
+ * for the fake pointer event.
+ *
+ * @param {Event} inEvent The in event.
+ * @param {ol.pointer.PointerEventHandler} dispatcher Event handler.
+ * @return {Object} The copied event.
+ */
+ol.pointer.MouseSource.prepareEvent = function(inEvent, dispatcher) {
+ var e = dispatcher.cloneEvent(inEvent, inEvent);
+
+ // forward mouse preventDefault
+ var pd = e.preventDefault;
+ e.preventDefault = function() {
+ inEvent.preventDefault();
+ pd();
+ };
+
+ e.pointerId = ol.pointer.MouseSource.POINTER_ID;
+ e.isPrimary = true;
+ e.pointerType = ol.pointer.MouseSource.POINTER_TYPE;
+
+ return e;
+};
+
+
+/**
+ * Handler for `mousedown`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MouseSource.prototype.mousedown = function(inEvent) {
+ if (!this.isEventSimulatedFromTouch_(inEvent)) {
+ // TODO(dfreedman) workaround for some elements not sending mouseup
+ // http://crbug/149091
+ if (ol.pointer.MouseSource.POINTER_ID.toString() in this.pointerMap) {
+ this.cancel(inEvent);
+ }
+ var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
+ this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()] = inEvent;
+ this.dispatcher.down(e, inEvent);
+ }
+};
+
+
+/**
+ * Handler for `mousemove`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MouseSource.prototype.mousemove = function(inEvent) {
+ if (!this.isEventSimulatedFromTouch_(inEvent)) {
+ var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
+ this.dispatcher.move(e, inEvent);
+ }
+};
+
+
+/**
+ * Handler for `mouseup`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MouseSource.prototype.mouseup = function(inEvent) {
+ if (!this.isEventSimulatedFromTouch_(inEvent)) {
+ var p = this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()];
+
+ if (p && p.button === inEvent.button) {
+ var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
+ this.dispatcher.up(e, inEvent);
+ this.cleanupMouse();
+ }
+ }
+};
+
+
+/**
+ * Handler for `mouseover`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MouseSource.prototype.mouseover = function(inEvent) {
+ if (!this.isEventSimulatedFromTouch_(inEvent)) {
+ var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
+ this.dispatcher.enterOver(e, inEvent);
+ }
+};
+
+
+/**
+ * Handler for `mouseout`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MouseSource.prototype.mouseout = function(inEvent) {
+ if (!this.isEventSimulatedFromTouch_(inEvent)) {
+ var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
+ this.dispatcher.leaveOut(e, inEvent);
+ }
+};
+
+
+/**
+ * Dispatches a `pointercancel` event.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MouseSource.prototype.cancel = function(inEvent) {
+ var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
+ this.dispatcher.cancel(e, inEvent);
+ this.cleanupMouse();
+};
+
+
+/**
+ * Remove the mouse from the list of active pointers.
+ */
+ol.pointer.MouseSource.prototype.cleanupMouse = function() {
+ delete this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()];
+};
+
+// Based on https://github.com/Polymer/PointerEvents
+
+// Copyright (c) 2013 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use 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 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 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.
+
+goog.provide('ol.pointer.MsSource');
+
+goog.require('ol');
+goog.require('ol.pointer.EventSource');
+
+
+/**
+ * @param {ol.pointer.PointerEventHandler} dispatcher Event handler.
+ * @constructor
+ * @extends {ol.pointer.EventSource}
+ */
+ol.pointer.MsSource = function(dispatcher) {
+ var mapping = {
+ 'MSPointerDown': this.msPointerDown,
+ 'MSPointerMove': this.msPointerMove,
+ 'MSPointerUp': this.msPointerUp,
+ 'MSPointerOut': this.msPointerOut,
+ 'MSPointerOver': this.msPointerOver,
+ 'MSPointerCancel': this.msPointerCancel,
+ 'MSGotPointerCapture': this.msGotPointerCapture,
+ 'MSLostPointerCapture': this.msLostPointerCapture
+ };
+ ol.pointer.EventSource.call(this, dispatcher, mapping);
+
+ /**
+ * @const
+ * @type {!Object.<string, Event|Object>}
+ */
+ this.pointerMap = dispatcher.pointerMap;
+
+ /**
+ * @const
+ * @type {Array.<string>}
+ */
+ this.POINTER_TYPES = [
+ '',
+ 'unavailable',
+ 'touch',
+ 'pen',
+ 'mouse'
+ ];
+};
+ol.inherits(ol.pointer.MsSource, ol.pointer.EventSource);
+
+
+/**
+ * Creates a copy of the original event that will be used
+ * for the fake pointer event.
+ *
+ * @private
+ * @param {Event} inEvent The in event.
+ * @return {Object} The copied event.
+ */
+ol.pointer.MsSource.prototype.prepareEvent_ = function(inEvent) {
+ var e = inEvent;
+ if (typeof inEvent.pointerType === 'number') {
+ e = this.dispatcher.cloneEvent(inEvent, inEvent);
+ e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
+ }
+
+ return e;
+};
+
+
+/**
+ * Remove this pointer from the list of active pointers.
+ * @param {number} pointerId Pointer identifier.
+ */
+ol.pointer.MsSource.prototype.cleanup = function(pointerId) {
+ delete this.pointerMap[pointerId.toString()];
+};
+
+
+/**
+ * Handler for `msPointerDown`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msPointerDown = function(inEvent) {
+ this.pointerMap[inEvent.pointerId.toString()] = inEvent;
+ var e = this.prepareEvent_(inEvent);
+ this.dispatcher.down(e, inEvent);
+};
+
+
+/**
+ * Handler for `msPointerMove`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msPointerMove = function(inEvent) {
+ var e = this.prepareEvent_(inEvent);
+ this.dispatcher.move(e, inEvent);
+};
+
+
+/**
+ * Handler for `msPointerUp`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msPointerUp = function(inEvent) {
+ var e = this.prepareEvent_(inEvent);
+ this.dispatcher.up(e, inEvent);
+ this.cleanup(inEvent.pointerId);
+};
+
+
+/**
+ * Handler for `msPointerOut`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msPointerOut = function(inEvent) {
+ var e = this.prepareEvent_(inEvent);
+ this.dispatcher.leaveOut(e, inEvent);
+};
+
+
+/**
+ * Handler for `msPointerOver`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msPointerOver = function(inEvent) {
+ var e = this.prepareEvent_(inEvent);
+ this.dispatcher.enterOver(e, inEvent);
+};
+
+
+/**
+ * Handler for `msPointerCancel`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msPointerCancel = function(inEvent) {
+ var e = this.prepareEvent_(inEvent);
+ this.dispatcher.cancel(e, inEvent);
+ this.cleanup(inEvent.pointerId);
+};
+
+
+/**
+ * Handler for `msLostPointerCapture`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msLostPointerCapture = function(inEvent) {
+ var e = this.dispatcher.makeEvent('lostpointercapture',
+ inEvent, inEvent);
+ this.dispatcher.dispatchEvent(e);
+};
+
+
+/**
+ * Handler for `msGotPointerCapture`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.MsSource.prototype.msGotPointerCapture = function(inEvent) {
+ var e = this.dispatcher.makeEvent('gotpointercapture',
+ inEvent, inEvent);
+ this.dispatcher.dispatchEvent(e);
+};
+
+// Based on https://github.com/Polymer/PointerEvents
+
+// Copyright (c) 2013 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use 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 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 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.
+
+goog.provide('ol.pointer.NativeSource');
+
+goog.require('ol');
+goog.require('ol.pointer.EventSource');
+
+
+/**
+ * @param {ol.pointer.PointerEventHandler} dispatcher Event handler.
+ * @constructor
+ * @extends {ol.pointer.EventSource}
+ */
+ol.pointer.NativeSource = function(dispatcher) {
+ var mapping = {
+ 'pointerdown': this.pointerDown,
+ 'pointermove': this.pointerMove,
+ 'pointerup': this.pointerUp,
+ 'pointerout': this.pointerOut,
+ 'pointerover': this.pointerOver,
+ 'pointercancel': this.pointerCancel,
+ 'gotpointercapture': this.gotPointerCapture,
+ 'lostpointercapture': this.lostPointerCapture
+ };
+ ol.pointer.EventSource.call(this, dispatcher, mapping);
+};
+ol.inherits(ol.pointer.NativeSource, ol.pointer.EventSource);
+
+
+/**
+ * Handler for `pointerdown`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.pointerDown = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `pointermove`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.pointerMove = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `pointerup`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.pointerUp = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `pointerout`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.pointerOut = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `pointerover`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.pointerOver = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `pointercancel`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.pointerCancel = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `lostpointercapture`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.lostPointerCapture = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+
+/**
+ * Handler for `gotpointercapture`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.NativeSource.prototype.gotPointerCapture = function(inEvent) {
+ this.dispatcher.fireNativeEvent(inEvent);
+};
+
+// Based on https://github.com/Polymer/PointerEvents
+
+// Copyright (c) 2013 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use 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 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 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.
+
+goog.provide('ol.pointer.PointerEvent');
+
+
+goog.require('ol');
+goog.require('ol.events.Event');
+
+
+/**
+ * A class for pointer events.
+ *
+ * This class is used as an abstraction for mouse events,
+ * touch events and even native pointer events.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @param {string} type The type of the event to create.
+ * @param {Event} originalEvent The event.
+ * @param {Object.<string, ?>=} opt_eventDict An optional dictionary of
+ * initial event properties.
+ */
+ol.pointer.PointerEvent = function(type, originalEvent, opt_eventDict) {
+ ol.events.Event.call(this, type);
+
+ /**
+ * @const
+ * @type {Event}
+ */
+ this.originalEvent = originalEvent;
+
+ var eventDict = opt_eventDict ? opt_eventDict : {};
+
+ /**
+ * @type {number}
+ */
+ this.buttons = this.getButtons_(eventDict);
+
+ /**
+ * @type {number}
+ */
+ this.pressure = this.getPressure_(eventDict, this.buttons);
+
+ // MouseEvent related properties
+
+ /**
+ * @type {boolean}
+ */
+ this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false;
+
+ /**
+ * @type {boolean}
+ */
+ this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false;
+
+ /**
+ * @type {Object}
+ */
+ this.view = 'view' in eventDict ? eventDict['view'] : null;
+
+ /**
+ * @type {number}
+ */
+ this.detail = 'detail' in eventDict ? eventDict['detail'] : null;
+
+ /**
+ * @type {number}
+ */
+ this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0;
+
+ /**
+ * @type {boolean}
+ */
+ this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false;
+
+ /**
+ * @type {boolean}
+ */
+ this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false;
+
+ /**
+ * @type {boolean}
+ */
+ this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false;
+
+ /**
+ * @type {boolean}
+ */
+ this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false;
+
+ /**
+ * @type {number}
+ */
+ this.button = 'button' in eventDict ? eventDict['button'] : 0;
+
+ /**
+ * @type {Node}
+ */
+ this.relatedTarget = 'relatedTarget' in eventDict ?
+ eventDict['relatedTarget'] : null;
+
+ // PointerEvent related properties
+
+ /**
+ * @const
+ * @type {number}
+ */
+ this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.width = 'width' in eventDict ? eventDict['width'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.height = 'height' in eventDict ? eventDict['height'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0;
+
+ /**
+ * @type {number}
+ */
+ this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0;
+
+ /**
+ * @type {string}
+ */
+ this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : '';
+
+ /**
+ * @type {number}
+ */
+ this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0;
+
+ /**
+ * @type {boolean}
+ */
+ this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false;
+
+ // keep the semantics of preventDefault
+ if (originalEvent.preventDefault) {
+ this.preventDefault = function() {
+ originalEvent.preventDefault();
+ };
+ }
+};
+ol.inherits(ol.pointer.PointerEvent, ol.events.Event);
+
+
+/**
+ * @private
+ * @param {Object.<string, ?>} eventDict The event dictionary.
+ * @return {number} Button indicator.
+ */
+ol.pointer.PointerEvent.prototype.getButtons_ = function(eventDict) {
+ // According to the w3c spec,
+ // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
+ // MouseEvent.button == 0 can mean either no mouse button depressed, or the
+ // left mouse button depressed.
+ //
+ // As of now, the only way to distinguish between the two states of
+ // MouseEvent.button is by using the deprecated MouseEvent.which property, as
+ // this maps mouse buttons to positive integers > 0, and uses 0 to mean that
+ // no mouse button is held.
+ //
+ // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation,
+ // but initMouseEvent does not expose an argument with which to set
+ // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
+ // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations
+ // of app developers.
+ //
+ // The only way to propagate the correct state of MouseEvent.which and
+ // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
+ // is to call initMouseEvent with a buttonArg value of -1.
+ //
+ // This is fixed with DOM Level 4's use of buttons
+ var buttons;
+ if (eventDict.buttons || ol.pointer.PointerEvent.HAS_BUTTONS) {
+ buttons = eventDict.buttons;
+ } else {
+ switch (eventDict.which) {
+ case 1: buttons = 1; break;
+ case 2: buttons = 4; break;
+ case 3: buttons = 2; break;
+ default: buttons = 0;
+ }
+ }
+ return buttons;
+};
+
+
+/**
+ * @private
+ * @param {Object.<string, ?>} eventDict The event dictionary.
+ * @param {number} buttons Button indicator.
+ * @return {number} The pressure.
+ */
+ol.pointer.PointerEvent.prototype.getPressure_ = function(eventDict, buttons) {
+ // Spec requires that pointers without pressure specified use 0.5 for down
+ // state and 0 for up state.
+ var pressure = 0;
+ if (eventDict.pressure) {
+ pressure = eventDict.pressure;
+ } else {
+ pressure = buttons ? 0.5 : 0;
+ }
+ return pressure;
+};
+
+
+/**
+ * Is the `buttons` property supported?
+ * @type {boolean}
+ */
+ol.pointer.PointerEvent.HAS_BUTTONS = false;
+
+
+/**
+ * Checks if the `buttons` property is supported.
+ */
+(function() {
+ try {
+ var ev = new MouseEvent('click', {buttons: 1});
+ ol.pointer.PointerEvent.HAS_BUTTONS = ev.buttons === 1;
+ } catch (e) {
+ // pass
+ }
+})();
+
+// Based on https://github.com/Polymer/PointerEvents
+
+// Copyright (c) 2013 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use 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 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 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.
+
+goog.provide('ol.pointer.TouchSource');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.pointer.EventSource');
+goog.require('ol.pointer.MouseSource');
+
+
+/**
+ * @constructor
+ * @param {ol.pointer.PointerEventHandler} dispatcher The event handler.
+ * @param {ol.pointer.MouseSource} mouseSource Mouse source.
+ * @extends {ol.pointer.EventSource}
+ */
+ol.pointer.TouchSource = function(dispatcher, mouseSource) {
+ var mapping = {
+ 'touchstart': this.touchstart,
+ 'touchmove': this.touchmove,
+ 'touchend': this.touchend,
+ 'touchcancel': this.touchcancel
+ };
+ ol.pointer.EventSource.call(this, dispatcher, mapping);
+
+ /**
+ * @const
+ * @type {!Object.<string, Event|Object>}
+ */
+ this.pointerMap = dispatcher.pointerMap;
+
+ /**
+ * @const
+ * @type {ol.pointer.MouseSource}
+ */
+ this.mouseSource = mouseSource;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.firstTouchId_ = undefined;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.clickCount_ = 0;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.resetId_ = undefined;
+};
+ol.inherits(ol.pointer.TouchSource, ol.pointer.EventSource);
+
+
+/**
+ * Mouse event timeout: This should be long enough to
+ * ignore compat mouse events made by touch.
+ * @const
+ * @type {number}
+ */
+ol.pointer.TouchSource.DEDUP_TIMEOUT = 2500;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT = 200;
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.pointer.TouchSource.POINTER_TYPE = 'touch';
+
+
+/**
+ * @private
+ * @param {Touch} inTouch The in touch.
+ * @return {boolean} True, if this is the primary touch.
+ */
+ol.pointer.TouchSource.prototype.isPrimaryTouch_ = function(inTouch) {
+ return this.firstTouchId_ === inTouch.identifier;
+};
+
+
+/**
+ * Set primary touch if there are no pointers, or the only pointer is the mouse.
+ * @param {Touch} inTouch The in touch.
+ * @private
+ */
+ol.pointer.TouchSource.prototype.setPrimaryTouch_ = function(inTouch) {
+ var count = Object.keys(this.pointerMap).length;
+ if (count === 0 || (count === 1 &&
+ ol.pointer.MouseSource.POINTER_ID.toString() in this.pointerMap)) {
+ this.firstTouchId_ = inTouch.identifier;
+ this.cancelResetClickCount_();
+ }
+};
+
+
+/**
+ * @private
+ * @param {Object} inPointer The in pointer object.
+ */
+ol.pointer.TouchSource.prototype.removePrimaryPointer_ = function(inPointer) {
+ if (inPointer.isPrimary) {
+ this.firstTouchId_ = undefined;
+ this.resetClickCount_();
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.pointer.TouchSource.prototype.resetClickCount_ = function() {
+ this.resetId_ = setTimeout(
+ this.resetClickCountHandler_.bind(this),
+ ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT);
+};
+
+
+/**
+ * @private
+ */
+ol.pointer.TouchSource.prototype.resetClickCountHandler_ = function() {
+ this.clickCount_ = 0;
+ this.resetId_ = undefined;
+};
+
+
+/**
+ * @private
+ */
+ol.pointer.TouchSource.prototype.cancelResetClickCount_ = function() {
+ if (this.resetId_ !== undefined) {
+ clearTimeout(this.resetId_);
+ }
+};
+
+
+/**
+ * @private
+ * @param {Event} browserEvent Browser event
+ * @param {Touch} inTouch Touch event
+ * @return {Object} A pointer object.
+ */
+ol.pointer.TouchSource.prototype.touchToPointer_ = function(browserEvent, inTouch) {
+ var e = this.dispatcher.cloneEvent(browserEvent, inTouch);
+ // Spec specifies that pointerId 1 is reserved for Mouse.
+ // Touch identifiers can start at 0.
+ // Add 2 to the touch identifier for compatibility.
+ e.pointerId = inTouch.identifier + 2;
+ // TODO: check if this is necessary?
+ //e.target = findTarget(e);
+ e.bubbles = true;
+ e.cancelable = true;
+ e.detail = this.clickCount_;
+ e.button = 0;
+ e.buttons = 1;
+ e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
+ e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
+ e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
+ e.isPrimary = this.isPrimaryTouch_(inTouch);
+ e.pointerType = ol.pointer.TouchSource.POINTER_TYPE;
+
+ // make sure that the properties that are different for
+ // each `Touch` object are not copied from the BrowserEvent object
+ e.clientX = inTouch.clientX;
+ e.clientY = inTouch.clientY;
+ e.screenX = inTouch.screenX;
+ e.screenY = inTouch.screenY;
+
+ return e;
+};
+
+
+/**
+ * @private
+ * @param {Event} inEvent Touch event
+ * @param {function(Event, Object)} inFunction In function.
+ */
+ol.pointer.TouchSource.prototype.processTouches_ = function(inEvent, inFunction) {
+ var touches = Array.prototype.slice.call(
+ inEvent.changedTouches);
+ var count = touches.length;
+ function preventDefault() {
+ inEvent.preventDefault();
+ }
+ var i, pointer;
+ for (i = 0; i < count; ++i) {
+ pointer = this.touchToPointer_(inEvent, touches[i]);
+ // forward touch preventDefaults
+ pointer.preventDefault = preventDefault;
+ inFunction.call(this, inEvent, pointer);
+ }
+};
+
+
+/**
+ * @private
+ * @param {TouchList} touchList The touch list.
+ * @param {number} searchId Search identifier.
+ * @return {boolean} True, if the `Touch` with the given id is in the list.
+ */
+ol.pointer.TouchSource.prototype.findTouch_ = function(touchList, searchId) {
+ var l = touchList.length;
+ var touch;
+ for (var i = 0; i < l; i++) {
+ touch = touchList[i];
+ if (touch.identifier === searchId) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * In some instances, a touchstart can happen without a touchend. This
+ * leaves the pointermap in a broken state.
+ * Therefore, on every touchstart, we remove the touches that did not fire a
+ * touchend event.
+ * To keep state globally consistent, we fire a pointercancel for
+ * this "abandoned" touch
+ *
+ * @private
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.TouchSource.prototype.vacuumTouches_ = function(inEvent) {
+ var touchList = inEvent.touches;
+ // pointerMap.getCount() should be < touchList.length here,
+ // as the touchstart has not been processed yet.
+ var keys = Object.keys(this.pointerMap);
+ var count = keys.length;
+ if (count >= touchList.length) {
+ var d = [];
+ var i, key, value;
+ for (i = 0; i < count; ++i) {
+ key = keys[i];
+ value = this.pointerMap[key];
+ // Never remove pointerId == 1, which is mouse.
+ // Touch identifiers are 2 smaller than their pointerId, which is the
+ // index in pointermap.
+ if (key != ol.pointer.MouseSource.POINTER_ID &&
+ !this.findTouch_(touchList, key - 2)) {
+ d.push(value.out);
+ }
+ }
+ for (i = 0; i < d.length; ++i) {
+ this.cancelOut_(inEvent, d[i]);
+ }
+ }
+};
+
+
+/**
+ * Handler for `touchstart`, triggers `pointerover`,
+ * `pointerenter` and `pointerdown` events.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.TouchSource.prototype.touchstart = function(inEvent) {
+ this.vacuumTouches_(inEvent);
+ this.setPrimaryTouch_(inEvent.changedTouches[0]);
+ this.dedupSynthMouse_(inEvent);
+ this.clickCount_++;
+ this.processTouches_(inEvent, this.overDown_);
+};
+
+
+/**
+ * @private
+ * @param {Event} browserEvent The event.
+ * @param {Object} inPointer The in pointer object.
+ */
+ol.pointer.TouchSource.prototype.overDown_ = function(browserEvent, inPointer) {
+ this.pointerMap[inPointer.pointerId] = {
+ target: inPointer.target,
+ out: inPointer,
+ outTarget: inPointer.target
+ };
+ this.dispatcher.over(inPointer, browserEvent);
+ this.dispatcher.enter(inPointer, browserEvent);
+ this.dispatcher.down(inPointer, browserEvent);
+};
+
+
+/**
+ * Handler for `touchmove`.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.TouchSource.prototype.touchmove = function(inEvent) {
+ inEvent.preventDefault();
+ this.processTouches_(inEvent, this.moveOverOut_);
+};
+
+
+/**
+ * @private
+ * @param {Event} browserEvent The event.
+ * @param {Object} inPointer The in pointer.
+ */
+ol.pointer.TouchSource.prototype.moveOverOut_ = function(browserEvent, inPointer) {
+ var event = inPointer;
+ var pointer = this.pointerMap[event.pointerId];
+ // a finger drifted off the screen, ignore it
+ if (!pointer) {
+ return;
+ }
+ var outEvent = pointer.out;
+ var outTarget = pointer.outTarget;
+ this.dispatcher.move(event, browserEvent);
+ if (outEvent && outTarget !== event.target) {
+ outEvent.relatedTarget = event.target;
+ event.relatedTarget = outTarget;
+ // recover from retargeting by shadow
+ outEvent.target = outTarget;
+ if (event.target) {
+ this.dispatcher.leaveOut(outEvent, browserEvent);
+ this.dispatcher.enterOver(event, browserEvent);
+ } else {
+ // clean up case when finger leaves the screen
+ event.target = outTarget;
+ event.relatedTarget = null;
+ this.cancelOut_(browserEvent, event);
+ }
+ }
+ pointer.out = event;
+ pointer.outTarget = event.target;
+};
+
+
+/**
+ * Handler for `touchend`, triggers `pointerup`,
+ * `pointerout` and `pointerleave` events.
+ *
+ * @param {Event} inEvent The event.
+ */
+ol.pointer.TouchSource.prototype.touchend = function(inEvent) {
+ this.dedupSynthMouse_(inEvent);
+ this.processTouches_(inEvent, this.upOut_);
+};
+
+
+/**
+ * @private
+ * @param {Event} browserEvent An event.
+ * @param {Object} inPointer The inPointer object.
+ */
+ol.pointer.TouchSource.prototype.upOut_ = function(browserEvent, inPointer) {
+ this.dispatcher.up(inPointer, browserEvent);
+ this.dispatcher.out(inPointer, browserEvent);
+ this.dispatcher.leave(inPointer, browserEvent);
+ this.cleanUpPointer_(inPointer);
+};
+
+
+/**
+ * Handler for `touchcancel`, triggers `pointercancel`,
+ * `pointerout` and `pointerleave` events.
+ *
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.TouchSource.prototype.touchcancel = function(inEvent) {
+ this.processTouches_(inEvent, this.cancelOut_);
+};
+
+
+/**
+ * @private
+ * @param {Event} browserEvent The event.
+ * @param {Object} inPointer The in pointer.
+ */
+ol.pointer.TouchSource.prototype.cancelOut_ = function(browserEvent, inPointer) {
+ this.dispatcher.cancel(inPointer, browserEvent);
+ this.dispatcher.out(inPointer, browserEvent);
+ this.dispatcher.leave(inPointer, browserEvent);
+ this.cleanUpPointer_(inPointer);
+};
+
+
+/**
+ * @private
+ * @param {Object} inPointer The inPointer object.
+ */
+ol.pointer.TouchSource.prototype.cleanUpPointer_ = function(inPointer) {
+ delete this.pointerMap[inPointer.pointerId];
+ this.removePrimaryPointer_(inPointer);
+};
+
+
+/**
+ * Prevent synth mouse events from creating pointer events.
+ *
+ * @private
+ * @param {Event} inEvent The in event.
+ */
+ol.pointer.TouchSource.prototype.dedupSynthMouse_ = function(inEvent) {
+ var lts = this.mouseSource.lastTouches;
+ var t = inEvent.changedTouches[0];
+ // only the primary finger will synth mouse events
+ if (this.isPrimaryTouch_(t)) {
+ // remember x/y of last touch
+ var lt = [t.clientX, t.clientY];
+ lts.push(lt);
+
+ setTimeout(function() {
+ // remove touch after timeout
+ ol.array.remove(lts, lt);
+ }, ol.pointer.TouchSource.DEDUP_TIMEOUT);
+ }
+};
+
+// Based on https://github.com/Polymer/PointerEvents
+
+// Copyright (c) 2013 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use 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 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 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.
+
+goog.provide('ol.pointer.PointerEventHandler');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.events.EventTarget');
+
+goog.require('ol.has');
+goog.require('ol.pointer.EventType');
+goog.require('ol.pointer.MouseSource');
+goog.require('ol.pointer.MsSource');
+goog.require('ol.pointer.NativeSource');
+goog.require('ol.pointer.PointerEvent');
+goog.require('ol.pointer.TouchSource');
+
+
+/**
+ * @constructor
+ * @extends {ol.events.EventTarget}
+ * @param {Element|HTMLDocument} element Viewport element.
+ */
+ol.pointer.PointerEventHandler = function(element) {
+ ol.events.EventTarget.call(this);
+
+ /**
+ * @const
+ * @private
+ * @type {Element|HTMLDocument}
+ */
+ this.element_ = element;
+
+ /**
+ * @const
+ * @type {!Object.<string, Event|Object>}
+ */
+ this.pointerMap = {};
+
+ /**
+ * @type {Object.<string, function(Event)>}
+ * @private
+ */
+ this.eventMap_ = {};
+
+ /**
+ * @type {Array.<ol.pointer.EventSource>}
+ * @private
+ */
+ this.eventSourceList_ = [];
+
+ this.registerSources();
+};
+ol.inherits(ol.pointer.PointerEventHandler, ol.events.EventTarget);
+
+
+/**
+ * Set up the event sources (mouse, touch and native pointers)
+ * that generate pointer events.
+ */
+ol.pointer.PointerEventHandler.prototype.registerSources = function() {
+ if (ol.has.POINTER) {
+ this.registerSource('native', new ol.pointer.NativeSource(this));
+ } else if (ol.has.MSPOINTER) {
+ this.registerSource('ms', new ol.pointer.MsSource(this));
+ } else {
+ var mouseSource = new ol.pointer.MouseSource(this);
+ this.registerSource('mouse', mouseSource);
+
+ if (ol.has.TOUCH) {
+ this.registerSource('touch',
+ new ol.pointer.TouchSource(this, mouseSource));
+ }
+ }
+
+ // register events on the viewport element
+ this.register_();
+};
+
+
+/**
+ * Add a new event source that will generate pointer events.
+ *
+ * @param {string} name A name for the event source
+ * @param {ol.pointer.EventSource} source The source event.
+ */
+ol.pointer.PointerEventHandler.prototype.registerSource = function(name, source) {
+ var s = source;
+ var newEvents = s.getEvents();
+
+ if (newEvents) {
+ newEvents.forEach(function(e) {
+ var handler = s.getHandlerForEvent(e);
+
+ if (handler) {
+ this.eventMap_[e] = handler.bind(s);
+ }
+ }, this);
+ this.eventSourceList_.push(s);
+ }
+};
+
+
+/**
+ * Set up the events for all registered event sources.
+ * @private
+ */
+ol.pointer.PointerEventHandler.prototype.register_ = function() {
+ var l = this.eventSourceList_.length;
+ var eventSource;
+ for (var i = 0; i < l; i++) {
+ eventSource = this.eventSourceList_[i];
+ this.addEvents_(eventSource.getEvents());
+ }
+};
+
+
+/**
+ * Remove all registered events.
+ * @private
+ */
+ol.pointer.PointerEventHandler.prototype.unregister_ = function() {
+ var l = this.eventSourceList_.length;
+ var eventSource;
+ for (var i = 0; i < l; i++) {
+ eventSource = this.eventSourceList_[i];
+ this.removeEvents_(eventSource.getEvents());
+ }
+};
+
+
+/**
+ * Calls the right handler for a new event.
+ * @private
+ * @param {Event} inEvent Browser event.
+ */
+ol.pointer.PointerEventHandler.prototype.eventHandler_ = function(inEvent) {
+ var type = inEvent.type;
+ var handler = this.eventMap_[type];
+ if (handler) {
+ handler(inEvent);
+ }
+};
+
+
+/**
+ * Setup listeners for the given events.
+ * @private
+ * @param {Array.<string>} events List of events.
+ */
+ol.pointer.PointerEventHandler.prototype.addEvents_ = function(events) {
+ events.forEach(function(eventName) {
+ ol.events.listen(this.element_, eventName, this.eventHandler_, this);
+ }, this);
+};
+
+
+/**
+ * Unregister listeners for the given events.
+ * @private
+ * @param {Array.<string>} events List of events.
+ */
+ol.pointer.PointerEventHandler.prototype.removeEvents_ = function(events) {
+ events.forEach(function(e) {
+ ol.events.unlisten(this.element_, e, this.eventHandler_, this);
+ }, this);
+};
+
+
+/**
+ * Returns a snapshot of inEvent, with writable properties.
+ *
+ * @param {Event} event Browser event.
+ * @param {Event|Touch} inEvent An event that contains
+ * properties to copy.
+ * @return {Object} An object containing shallow copies of
+ * `inEvent`'s properties.
+ */
+ol.pointer.PointerEventHandler.prototype.cloneEvent = function(event, inEvent) {
+ var eventCopy = {}, p;
+ for (var i = 0, ii = ol.pointer.PointerEventHandler.CLONE_PROPS.length; i < ii; i++) {
+ p = ol.pointer.PointerEventHandler.CLONE_PROPS[i][0];
+ eventCopy[p] = event[p] || inEvent[p] || ol.pointer.PointerEventHandler.CLONE_PROPS[i][1];
+ }
+
+ return eventCopy;
+};
+
+
+// EVENTS
+
+
+/**
+ * Triggers a 'pointerdown' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.down = function(data, event) {
+ this.fireEvent(ol.pointer.EventType.POINTERDOWN, data, event);
+};
+
+
+/**
+ * Triggers a 'pointermove' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.move = function(data, event) {
+ this.fireEvent(ol.pointer.EventType.POINTERMOVE, data, event);
+};
+
+
+/**
+ * Triggers a 'pointerup' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.up = function(data, event) {
+ this.fireEvent(ol.pointer.EventType.POINTERUP, data, event);
+};
+
+
+/**
+ * Triggers a 'pointerenter' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.enter = function(data, event) {
+ data.bubbles = false;
+ this.fireEvent(ol.pointer.EventType.POINTERENTER, data, event);
+};
+
+
+/**
+ * Triggers a 'pointerleave' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.leave = function(data, event) {
+ data.bubbles = false;
+ this.fireEvent(ol.pointer.EventType.POINTERLEAVE, data, event);
+};
+
+
+/**
+ * Triggers a 'pointerover' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.over = function(data, event) {
+ data.bubbles = true;
+ this.fireEvent(ol.pointer.EventType.POINTEROVER, data, event);
+};
+
+
+/**
+ * Triggers a 'pointerout' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.out = function(data, event) {
+ data.bubbles = true;
+ this.fireEvent(ol.pointer.EventType.POINTEROUT, data, event);
+};
+
+
+/**
+ * Triggers a 'pointercancel' event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.cancel = function(data, event) {
+ this.fireEvent(ol.pointer.EventType.POINTERCANCEL, data, event);
+};
+
+
+/**
+ * Triggers a combination of 'pointerout' and 'pointerleave' events.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.leaveOut = function(data, event) {
+ this.out(data, event);
+ if (!this.contains_(data.target, data.relatedTarget)) {
+ this.leave(data, event);
+ }
+};
+
+
+/**
+ * Triggers a combination of 'pointerover' and 'pointerevents' events.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.enterOver = function(data, event) {
+ this.over(data, event);
+ if (!this.contains_(data.target, data.relatedTarget)) {
+ this.enter(data, event);
+ }
+};
+
+
+/**
+ * @private
+ * @param {Element} container The container element.
+ * @param {Element} contained The contained element.
+ * @return {boolean} Returns true if the container element
+ * contains the other element.
+ */
+ol.pointer.PointerEventHandler.prototype.contains_ = function(container, contained) {
+ if (!container || !contained) {
+ return false;
+ }
+ return container.contains(contained);
+};
+
+
+// EVENT CREATION AND TRACKING
+/**
+ * Creates a new Event of type `inType`, based on the information in
+ * `data`.
+ *
+ * @param {string} inType A string representing the type of event to create.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ * @return {ol.pointer.PointerEvent} A PointerEvent of type `inType`.
+ */
+ol.pointer.PointerEventHandler.prototype.makeEvent = function(inType, data, event) {
+ return new ol.pointer.PointerEvent(inType, event, data);
+};
+
+
+/**
+ * Make and dispatch an event in one call.
+ * @param {string} inType A string representing the type of event.
+ * @param {Object} data Pointer event data.
+ * @param {Event} event The event.
+ */
+ol.pointer.PointerEventHandler.prototype.fireEvent = function(inType, data, event) {
+ var e = this.makeEvent(inType, data, event);
+ this.dispatchEvent(e);
+};
+
+
+/**
+ * Creates a pointer event from a native pointer event
+ * and dispatches this event.
+ * @param {Event} event A platform event with a target.
+ */
+ol.pointer.PointerEventHandler.prototype.fireNativeEvent = function(event) {
+ var e = this.makeEvent(event.type, event, event);
+ this.dispatchEvent(e);
+};
+
+
+/**
+ * Wrap a native mouse event into a pointer event.
+ * This proxy method is required for the legacy IE support.
+ * @param {string} eventType The pointer event type.
+ * @param {Event} event The event.
+ * @return {ol.pointer.PointerEvent} The wrapped event.
+ */
+ol.pointer.PointerEventHandler.prototype.wrapMouseEvent = function(eventType, event) {
+ var pointerEvent = this.makeEvent(
+ eventType, ol.pointer.MouseSource.prepareEvent(event, this), event);
+ return pointerEvent;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.pointer.PointerEventHandler.prototype.disposeInternal = function() {
+ this.unregister_();
+ ol.events.EventTarget.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * Properties to copy when cloning an event, with default values.
+ * @type {Array.<Array>}
+ */
+ol.pointer.PointerEventHandler.CLONE_PROPS = [
+ // MouseEvent
+ ['bubbles', false],
+ ['cancelable', false],
+ ['view', null],
+ ['detail', null],
+ ['screenX', 0],
+ ['screenY', 0],
+ ['clientX', 0],
+ ['clientY', 0],
+ ['ctrlKey', false],
+ ['altKey', false],
+ ['shiftKey', false],
+ ['metaKey', false],
+ ['button', 0],
+ ['relatedTarget', null],
+ // DOM Level 3
+ ['buttons', 0],
+ // PointerEvent
+ ['pointerId', 0],
+ ['width', 0],
+ ['height', 0],
+ ['pressure', 0],
+ ['tiltX', 0],
+ ['tiltY', 0],
+ ['pointerType', ''],
+ ['hwTimestamp', 0],
+ ['isPrimary', false],
+ // event instance
+ ['type', ''],
+ ['target', null],
+ ['currentTarget', null],
+ ['which', 0]
+];
+
+goog.provide('ol.MapBrowserEventHandler');
+
+goog.require('ol');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.MapBrowserPointerEvent');
+goog.require('ol.events');
+goog.require('ol.events.EventTarget');
+goog.require('ol.pointer.EventType');
+goog.require('ol.pointer.PointerEventHandler');
+
+
+/**
+ * @param {ol.Map} map The map with the viewport to listen to events on.
+ * @constructor
+ * @extends {ol.events.EventTarget}
+ */
+ol.MapBrowserEventHandler = function(map) {
+
+ ol.events.EventTarget.call(this);
+
+ /**
+ * This is the element that we will listen to the real events on.
+ * @type {ol.Map}
+ * @private
+ */
+ this.map_ = map;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.clickTimeoutId_ = 0;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.dragging_ = false;
+
+ /**
+ * @type {!Array.<ol.EventsKey>}
+ * @private
+ */
+ this.dragListenerKeys_ = [];
+
+ /**
+ * The most recent "down" type event (or null if none have occurred).
+ * Set on pointerdown.
+ * @type {ol.pointer.PointerEvent}
+ * @private
+ */
+ this.down_ = null;
+
+ var element = this.map_.getViewport();
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.activePointers_ = 0;
+
+ /**
+ * @type {!Object.<number, boolean>}
+ * @private
+ */
+ this.trackedTouches_ = {};
+
+ /**
+ * Event handler which generates pointer events for
+ * the viewport element.
+ *
+ * @type {ol.pointer.PointerEventHandler}
+ * @private
+ */
+ this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element);
+
+ /**
+ * Event handler which generates pointer events for
+ * the document (used when dragging).
+ *
+ * @type {ol.pointer.PointerEventHandler}
+ * @private
+ */
+ this.documentPointerEventHandler_ = null;
+
+ /**
+ * @type {?ol.EventsKey}
+ * @private
+ */
+ this.pointerdownListenerKey_ = ol.events.listen(this.pointerEventHandler_,
+ ol.pointer.EventType.POINTERDOWN,
+ this.handlePointerDown_, this);
+
+ /**
+ * @type {?ol.EventsKey}
+ * @private
+ */
+ this.relayedListenerKey_ = ol.events.listen(this.pointerEventHandler_,
+ ol.pointer.EventType.POINTERMOVE,
+ this.relayEvent_, this);
+
+};
+ol.inherits(ol.MapBrowserEventHandler, ol.events.EventTarget);
+
+
+/**
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) {
+ var newEvent = new ol.MapBrowserPointerEvent(
+ ol.MapBrowserEvent.EventType.CLICK, this.map_, pointerEvent);
+ this.dispatchEvent(newEvent);
+ if (this.clickTimeoutId_ !== 0) {
+ // double-click
+ clearTimeout(this.clickTimeoutId_);
+ this.clickTimeoutId_ = 0;
+ newEvent = new ol.MapBrowserPointerEvent(
+ ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, pointerEvent);
+ this.dispatchEvent(newEvent);
+ } else {
+ // click
+ this.clickTimeoutId_ = setTimeout(function() {
+ this.clickTimeoutId_ = 0;
+ var newEvent = new ol.MapBrowserPointerEvent(
+ ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, pointerEvent);
+ this.dispatchEvent(newEvent);
+ }.bind(this), 250);
+ }
+};
+
+
+/**
+ * Keeps track on how many pointers are currently active.
+ *
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.updateActivePointers_ = function(pointerEvent) {
+ var event = pointerEvent;
+
+ if (event.type == ol.MapBrowserEvent.EventType.POINTERUP ||
+ event.type == ol.MapBrowserEvent.EventType.POINTERCANCEL) {
+ delete this.trackedTouches_[event.pointerId];
+ } else if (event.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
+ this.trackedTouches_[event.pointerId] = true;
+ }
+ this.activePointers_ = Object.keys(this.trackedTouches_).length;
+};
+
+
+/**
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) {
+ this.updateActivePointers_(pointerEvent);
+ var newEvent = new ol.MapBrowserPointerEvent(
+ ol.MapBrowserEvent.EventType.POINTERUP, this.map_, pointerEvent);
+ this.dispatchEvent(newEvent);
+
+ // We emulate click events on left mouse button click, touch contact, and pen
+ // contact. isMouseActionButton returns true in these cases (evt.button is set
+ // to 0).
+ // See http://www.w3.org/TR/pointerevents/#button-states
+ if (!this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
+ ol.DEBUG && console.assert(this.down_, 'this.down_ must be truthy');
+ this.emulateClick_(this.down_);
+ }
+
+ ol.DEBUG && console.assert(this.activePointers_ >= 0,
+ 'this.activePointers_ should be equal to or larger than 0');
+ if (this.activePointers_ === 0) {
+ this.dragListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.dragListenerKeys_.length = 0;
+ this.dragging_ = false;
+ this.down_ = null;
+ this.documentPointerEventHandler_.dispose();
+ this.documentPointerEventHandler_ = null;
+ }
+};
+
+
+/**
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @return {boolean} If the left mouse button was pressed.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.isMouseActionButton_ = function(pointerEvent) {
+ return pointerEvent.button === 0;
+};
+
+
+/**
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.handlePointerDown_ = function(pointerEvent) {
+ this.updateActivePointers_(pointerEvent);
+ var newEvent = new ol.MapBrowserPointerEvent(
+ ol.MapBrowserEvent.EventType.POINTERDOWN, this.map_, pointerEvent);
+ this.dispatchEvent(newEvent);
+
+ this.down_ = pointerEvent;
+
+ if (this.dragListenerKeys_.length === 0) {
+ /* Set up a pointer event handler on the `document`,
+ * which is required when the pointer is moved outside
+ * the viewport when dragging.
+ */
+ this.documentPointerEventHandler_ =
+ new ol.pointer.PointerEventHandler(document);
+
+ this.dragListenerKeys_.push(
+ ol.events.listen(this.documentPointerEventHandler_,
+ ol.MapBrowserEvent.EventType.POINTERMOVE,
+ this.handlePointerMove_, this),
+ ol.events.listen(this.documentPointerEventHandler_,
+ ol.MapBrowserEvent.EventType.POINTERUP,
+ this.handlePointerUp_, this),
+ /* Note that the listener for `pointercancel is set up on
+ * `pointerEventHandler_` and not `documentPointerEventHandler_` like
+ * the `pointerup` and `pointermove` listeners.
+ *
+ * The reason for this is the following: `TouchSource.vacuumTouches_()`
+ * issues `pointercancel` events, when there was no `touchend` for a
+ * `touchstart`. Now, let's say a first `touchstart` is registered on
+ * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
+ * But `documentPointerEventHandler_` doesn't know about the first
+ * `touchstart`. If there is no `touchend` for the `touchstart`, we can
+ * only receive a `touchcancel` from `pointerEventHandler_`, because it is
+ * only registered there.
+ */
+ ol.events.listen(this.pointerEventHandler_,
+ ol.MapBrowserEvent.EventType.POINTERCANCEL,
+ this.handlePointerUp_, this)
+ );
+ }
+};
+
+
+/**
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.handlePointerMove_ = function(pointerEvent) {
+ // Fix IE10 on windows Surface : When you tap the tablet, it triggers
+ // multiple pointermove events between pointerdown and pointerup with
+ // the exact same coordinates of the pointerdown event. To avoid a
+ // 'false' touchmove event to be dispatched , we test if the pointer
+ // effectively moved.
+ if (this.isMoving_(pointerEvent)) {
+ this.dragging_ = true;
+ var newEvent = new ol.MapBrowserPointerEvent(
+ ol.MapBrowserEvent.EventType.POINTERDRAG, this.map_, pointerEvent,
+ this.dragging_);
+ this.dispatchEvent(newEvent);
+ }
+
+ // Some native android browser triggers mousemove events during small period
+ // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
+ // https://code.google.com/p/android/issues/detail?id=19827
+ // ex: Galaxy Tab P3110 + Android 4.1.1
+ pointerEvent.preventDefault();
+};
+
+
+/**
+ * Wrap and relay a pointer event. Note that this requires that the type
+ * string for the MapBrowserPointerEvent matches the PointerEvent type.
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) {
+ var dragging = !!(this.down_ && this.isMoving_(pointerEvent));
+ this.dispatchEvent(new ol.MapBrowserPointerEvent(
+ pointerEvent.type, this.map_, pointerEvent, dragging));
+};
+
+
+/**
+ * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
+ * @return {boolean} Is moving.
+ * @private
+ */
+ol.MapBrowserEventHandler.prototype.isMoving_ = function(pointerEvent) {
+ return pointerEvent.clientX != this.down_.clientX ||
+ pointerEvent.clientY != this.down_.clientY;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
+ if (this.relayedListenerKey_) {
+ ol.events.unlistenByKey(this.relayedListenerKey_);
+ this.relayedListenerKey_ = null;
+ }
+ if (this.pointerdownListenerKey_) {
+ ol.events.unlistenByKey(this.pointerdownListenerKey_);
+ this.pointerdownListenerKey_ = null;
+ }
+
+ this.dragListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.dragListenerKeys_.length = 0;
+
+ if (this.documentPointerEventHandler_) {
+ this.documentPointerEventHandler_.dispose();
+ this.documentPointerEventHandler_ = null;
+ }
+ if (this.pointerEventHandler_) {
+ this.pointerEventHandler_.dispose();
+ this.pointerEventHandler_ = null;
+ }
+ ol.events.EventTarget.prototype.disposeInternal.call(this);
+};
+
+goog.provide('ol.Tile');
+
+goog.require('ol');
+goog.require('ol.events.EventTarget');
+goog.require('ol.events.EventType');
+
+
+/**
+ * @classdesc
+ * Base class for tiles.
+ *
+ * @constructor
+ * @extends {ol.events.EventTarget}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Tile.State} state State.
+ */
+ol.Tile = function(tileCoord, state) {
+
+ ol.events.EventTarget.call(this);
+
+ /**
+ * @type {ol.TileCoord}
+ */
+ this.tileCoord = tileCoord;
+
+ /**
+ * @protected
+ * @type {ol.Tile.State}
+ */
+ this.state = state;
+
+ /**
+ * An "interim" tile for this tile. The interim tile may be used while this
+ * one is loading, for "smooth" transitions when changing params/dimensions
+ * on the source.
+ * @type {ol.Tile}
+ */
+ this.interimTile = null;
+
+ /**
+ * A key assigned to the tile. This is used by the tile source to determine
+ * if this tile can effectively be used, or if a new tile should be created
+ * and this one be used as an interim tile for this new tile.
+ * @type {string}
+ */
+ this.key = '';
+
+};
+ol.inherits(ol.Tile, ol.events.EventTarget);
+
+
+/**
+ * @protected
+ */
+ol.Tile.prototype.changed = function() {
+ this.dispatchEvent(ol.events.EventType.CHANGE);
+};
+
+
+/**
+ * Get the HTML image element for this tile (may be a Canvas, Image, or Video).
+ * @abstract
+ * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
+ */
+ol.Tile.prototype.getImage = function() {};
+
+
+/**
+ * @return {string} Key.
+ */
+ol.Tile.prototype.getKey = function() {
+ return this.key + '/' + this.tileCoord;
+};
+
+/**
+ * Get the interim tile most suitable for rendering using the chain of interim
+ * tiles. This corresponds to the most recent tile that has been loaded, if no
+ * such tile exists, the original tile is returned.
+ * @return {!ol.Tile} Best tile for rendering.
+ */
+ol.Tile.prototype.getInterimTile = function() {
+ if (!this.interimTile) {
+ //empty chain
+ return this;
+ }
+ var tile = this.interimTile;
+
+ // find the first loaded tile and return it. Since the chain is sorted in
+ // decreasing order of creation time, there is no need to search the remainder
+ // of the list (all those tiles correspond to older requests and will be
+ // cleaned up by refreshInterimChain)
+ do {
+ if (tile.getState() == ol.Tile.State.LOADED) {
+ return tile;
+ }
+ tile = tile.interimTile;
+ } while (tile);
+
+ // we can not find a better tile
+ return this;
+};
+
+/**
+ * Goes through the chain of interim tiles and discards sections of the chain
+ * that are no longer relevant.
+ */
+ol.Tile.prototype.refreshInterimChain = function() {
+ if (!this.interimTile) {
+ return;
+ }
+
+ var tile = this.interimTile;
+ var prev = this;
+
+ do {
+ if (tile.getState() == ol.Tile.State.LOADED) {
+ //we have a loaded tile, we can discard the rest of the list
+ //we would could abort any LOADING tile request
+ //older than this tile (i.e. any LOADING tile following this entry in the chain)
+ tile.interimTile = null;
+ break;
+ } else if (tile.getState() == ol.Tile.State.LOADING) {
+ //keep this LOADING tile any loaded tiles later in the chain are
+ //older than this tile, so we're still interested in the request
+ prev = tile;
+ } else if (tile.getState() == ol.Tile.State.IDLE) {
+ //the head of the list is the most current tile, we don't need
+ //to start any other requests for this chain
+ prev.interimTile = tile.interimTile;
+ } else {
+ prev = tile;
+ }
+ tile = prev.interimTile;
+ } while (tile);
+};
+
+/**
+ * Get the tile coordinate for this tile.
+ * @return {ol.TileCoord} The tile coordinate.
+ * @api
+ */
+ol.Tile.prototype.getTileCoord = function() {
+ return this.tileCoord;
+};
+
+
+/**
+ * @return {ol.Tile.State} State.
+ */
+ol.Tile.prototype.getState = function() {
+ return this.state;
+};
+
+
+/**
+ * Load the image or retry if loading previously failed.
+ * Loading is taken care of by the tile queue, and calling this method is
+ * only needed for preloading or for reloading in case of an error.
+ * @abstract
+ * @api
+ */
+ol.Tile.prototype.load = function() {};
+
+
+/**
+ * @enum {number}
+ */
+ol.Tile.State = {
+ IDLE: 0,
+ LOADING: 1,
+ LOADED: 2,
+ ERROR: 3,
+ EMPTY: 4,
+ ABORT: 5
+};
+
+goog.provide('ol.structs.PriorityQueue');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.obj');
+
+
+/**
+ * Priority queue.
+ *
+ * The implementation is inspired from the Closure Library's Heap class and
+ * Python's heapq module.
+ *
+ * @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html
+ * @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py
+ *
+ * @constructor
+ * @param {function(T): number} priorityFunction Priority function.
+ * @param {function(T): string} keyFunction Key function.
+ * @struct
+ * @template T
+ */
+ol.structs.PriorityQueue = function(priorityFunction, keyFunction) {
+
+ /**
+ * @type {function(T): number}
+ * @private
+ */
+ this.priorityFunction_ = priorityFunction;
+
+ /**
+ * @type {function(T): string}
+ * @private
+ */
+ this.keyFunction_ = keyFunction;
+
+ /**
+ * @type {Array.<T>}
+ * @private
+ */
+ this.elements_ = [];
+
+ /**
+ * @type {Array.<number>}
+ * @private
+ */
+ this.priorities_ = [];
+
+ /**
+ * @type {Object.<string, boolean>}
+ * @private
+ */
+ this.queuedElements_ = {};
+
+};
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.structs.PriorityQueue.DROP = Infinity;
+
+
+if (ol.DEBUG) {
+ /**
+ * FIXME empty description for jsdoc
+ */
+ ol.structs.PriorityQueue.prototype.assertValid = function() {
+ var elements = this.elements_;
+ var priorities = this.priorities_;
+ var n = elements.length;
+ console.assert(priorities.length == n);
+ var i, priority;
+ for (i = 0; i < (n >> 1) - 1; ++i) {
+ priority = priorities[i];
+ console.assert(priority <= priorities[this.getLeftChildIndex_(i)],
+ 'priority smaller than or equal to priority of left child (%s <= %s)',
+ priority, priorities[this.getLeftChildIndex_(i)]);
+ console.assert(priority <= priorities[this.getRightChildIndex_(i)],
+ 'priority smaller than or equal to priority of right child (%s <= %s)',
+ priority, priorities[this.getRightChildIndex_(i)]);
+ }
+ };
+}
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.structs.PriorityQueue.prototype.clear = function() {
+ this.elements_.length = 0;
+ this.priorities_.length = 0;
+ ol.obj.clear(this.queuedElements_);
+};
+
+
+/**
+ * Remove and return the highest-priority element. O(log N).
+ * @return {T} Element.
+ */
+ol.structs.PriorityQueue.prototype.dequeue = function() {
+ var elements = this.elements_;
+ ol.DEBUG && console.assert(elements.length > 0,
+ 'must have elements in order to be able to dequeue');
+ var priorities = this.priorities_;
+ var element = elements[0];
+ if (elements.length == 1) {
+ elements.length = 0;
+ priorities.length = 0;
+ } else {
+ elements[0] = elements.pop();
+ priorities[0] = priorities.pop();
+ this.siftUp_(0);
+ }
+ var elementKey = this.keyFunction_(element);
+ ol.DEBUG && console.assert(elementKey in this.queuedElements_,
+ 'key %s is not listed as queued', elementKey);
+ delete this.queuedElements_[elementKey];
+ return element;
+};
+
+
+/**
+ * Enqueue an element. O(log N).
+ * @param {T} element Element.
+ * @return {boolean} The element was added to the queue.
+ */
+ol.structs.PriorityQueue.prototype.enqueue = function(element) {
+ ol.asserts.assert(!(this.keyFunction_(element) in this.queuedElements_),
+ 31); // Tried to enqueue an `element` that was already added to the queue
+ var priority = this.priorityFunction_(element);
+ if (priority != ol.structs.PriorityQueue.DROP) {
+ this.elements_.push(element);
+ this.priorities_.push(priority);
+ this.queuedElements_[this.keyFunction_(element)] = true;
+ this.siftDown_(0, this.elements_.length - 1);
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * @return {number} Count.
+ */
+ol.structs.PriorityQueue.prototype.getCount = function() {
+ return this.elements_.length;
+};
+
+
+/**
+ * Gets the index of the left child of the node at the given index.
+ * @param {number} index The index of the node to get the left child for.
+ * @return {number} The index of the left child.
+ * @private
+ */
+ol.structs.PriorityQueue.prototype.getLeftChildIndex_ = function(index) {
+ return index * 2 + 1;
+};
+
+
+/**
+ * Gets the index of the right child of the node at the given index.
+ * @param {number} index The index of the node to get the right child for.
+ * @return {number} The index of the right child.
+ * @private
+ */
+ol.structs.PriorityQueue.prototype.getRightChildIndex_ = function(index) {
+ return index * 2 + 2;
+};
+
+
+/**
+ * Gets the index of the parent of the node at the given index.
+ * @param {number} index The index of the node to get the parent for.
+ * @return {number} The index of the parent.
+ * @private
+ */
+ol.structs.PriorityQueue.prototype.getParentIndex_ = function(index) {
+ return (index - 1) >> 1;
+};
+
+
+/**
+ * Make this a heap. O(N).
+ * @private
+ */
+ol.structs.PriorityQueue.prototype.heapify_ = function() {
+ var i;
+ for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {
+ this.siftUp_(i);
+ }
+};
+
+
+/**
+ * @return {boolean} Is empty.
+ */
+ol.structs.PriorityQueue.prototype.isEmpty = function() {
+ return this.elements_.length === 0;
+};
+
+
+/**
+ * @param {string} key Key.
+ * @return {boolean} Is key queued.
+ */
+ol.structs.PriorityQueue.prototype.isKeyQueued = function(key) {
+ return key in this.queuedElements_;
+};
+
+
+/**
+ * @param {T} element Element.
+ * @return {boolean} Is queued.
+ */
+ol.structs.PriorityQueue.prototype.isQueued = function(element) {
+ return this.isKeyQueued(this.keyFunction_(element));
+};
+
+
+/**
+ * @param {number} index The index of the node to move down.
+ * @private
+ */
+ol.structs.PriorityQueue.prototype.siftUp_ = function(index) {
+ var elements = this.elements_;
+ var priorities = this.priorities_;
+ var count = elements.length;
+ var element = elements[index];
+ var priority = priorities[index];
+ var startIndex = index;
+
+ while (index < (count >> 1)) {
+ var lIndex = this.getLeftChildIndex_(index);
+ var rIndex = this.getRightChildIndex_(index);
+
+ var smallerChildIndex = rIndex < count &&
+ priorities[rIndex] < priorities[lIndex] ?
+ rIndex : lIndex;
+
+ elements[index] = elements[smallerChildIndex];
+ priorities[index] = priorities[smallerChildIndex];
+ index = smallerChildIndex;
+ }
+
+ elements[index] = element;
+ priorities[index] = priority;
+ this.siftDown_(startIndex, index);
+};
+
+
+/**
+ * @param {number} startIndex The index of the root.
+ * @param {number} index The index of the node to move up.
+ * @private
+ */
+ol.structs.PriorityQueue.prototype.siftDown_ = function(startIndex, index) {
+ var elements = this.elements_;
+ var priorities = this.priorities_;
+ var element = elements[index];
+ var priority = priorities[index];
+
+ while (index > startIndex) {
+ var parentIndex = this.getParentIndex_(index);
+ if (priorities[parentIndex] > priority) {
+ elements[index] = elements[parentIndex];
+ priorities[index] = priorities[parentIndex];
+ index = parentIndex;
+ } else {
+ break;
+ }
+ }
+ elements[index] = element;
+ priorities[index] = priority;
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.structs.PriorityQueue.prototype.reprioritize = function() {
+ var priorityFunction = this.priorityFunction_;
+ var elements = this.elements_;
+ var priorities = this.priorities_;
+ var index = 0;
+ var n = elements.length;
+ var element, i, priority;
+ for (i = 0; i < n; ++i) {
+ element = elements[i];
+ priority = priorityFunction(element);
+ if (priority == ol.structs.PriorityQueue.DROP) {
+ delete this.queuedElements_[this.keyFunction_(element)];
+ } else {
+ priorities[index] = priority;
+ elements[index++] = element;
+ }
+ }
+ elements.length = index;
+ priorities.length = index;
+ this.heapify_();
+};
+
+goog.provide('ol.TileQueue');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.structs.PriorityQueue');
+
+
+/**
+ * @constructor
+ * @extends {ol.structs.PriorityQueue.<Array>}
+ * @param {ol.TilePriorityFunction} tilePriorityFunction
+ * Tile priority function.
+ * @param {function(): ?} tileChangeCallback
+ * Function called on each tile change event.
+ * @struct
+ */
+ol.TileQueue = function(tilePriorityFunction, tileChangeCallback) {
+
+ ol.structs.PriorityQueue.call(
+ this,
+ /**
+ * @param {Array} element Element.
+ * @return {number} Priority.
+ */
+ function(element) {
+ return tilePriorityFunction.apply(null, element);
+ },
+ /**
+ * @param {Array} element Element.
+ * @return {string} Key.
+ */
+ function(element) {
+ return /** @type {ol.Tile} */ (element[0]).getKey();
+ });
+
+ /**
+ * @private
+ * @type {function(): ?}
+ */
+ this.tileChangeCallback_ = tileChangeCallback;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.tilesLoading_ = 0;
+
+ /**
+ * @private
+ * @type {!Object.<string,boolean>}
+ */
+ this.tilesLoadingKeys_ = {};
+
+};
+ol.inherits(ol.TileQueue, ol.structs.PriorityQueue);
+
+
+/**
+ * @inheritDoc
+ */
+ol.TileQueue.prototype.enqueue = function(element) {
+ var added = ol.structs.PriorityQueue.prototype.enqueue.call(this, element);
+ if (added) {
+ var tile = element[0];
+ ol.events.listen(tile, ol.events.EventType.CHANGE,
+ this.handleTileChange, this);
+ }
+ return added;
+};
+
+
+/**
+ * @return {number} Number of tiles loading.
+ */
+ol.TileQueue.prototype.getTilesLoading = function() {
+ return this.tilesLoading_;
+};
+
+
+/**
+ * @param {ol.events.Event} event Event.
+ * @protected
+ */
+ol.TileQueue.prototype.handleTileChange = function(event) {
+ var tile = /** @type {ol.Tile} */ (event.target);
+ var state = tile.getState();
+ if (state === ol.Tile.State.LOADED || state === ol.Tile.State.ERROR ||
+ state === ol.Tile.State.EMPTY || state === ol.Tile.State.ABORT) {
+ ol.events.unlisten(tile, ol.events.EventType.CHANGE,
+ this.handleTileChange, this);
+ var tileKey = tile.getKey();
+ if (tileKey in this.tilesLoadingKeys_) {
+ delete this.tilesLoadingKeys_[tileKey];
+ --this.tilesLoading_;
+ }
+ this.tileChangeCallback_();
+ }
+ ol.DEBUG && console.assert(Object.keys(this.tilesLoadingKeys_).length === this.tilesLoading_);
+};
+
+
+/**
+ * @param {number} maxTotalLoading Maximum number tiles to load simultaneously.
+ * @param {number} maxNewLoads Maximum number of new tiles to load.
+ */
+ol.TileQueue.prototype.loadMoreTiles = function(maxTotalLoading, maxNewLoads) {
+ var newLoads = 0;
+ var tile, tileKey;
+ while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads &&
+ this.getCount() > 0) {
+ tile = /** @type {ol.Tile} */ (this.dequeue()[0]);
+ tileKey = tile.getKey();
+ if (tile.getState() === ol.Tile.State.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
+ this.tilesLoadingKeys_[tileKey] = true;
+ ++this.tilesLoading_;
+ ++newLoads;
+ tile.load();
+ }
+ ol.DEBUG && console.assert(Object.keys(this.tilesLoadingKeys_).length === this.tilesLoading_);
+ }
+};
+
+goog.provide('ol.Kinetic');
+
+
+/**
+ * @classdesc
+ * Implementation of inertial deceleration for map movement.
+ *
+ * @constructor
+ * @param {number} decay Rate of decay (must be negative).
+ * @param {number} minVelocity Minimum velocity (pixels/millisecond).
+ * @param {number} delay Delay to consider to calculate the kinetic
+ * initial values (milliseconds).
+ * @struct
+ * @api
+ */
+ol.Kinetic = function(decay, minVelocity, delay) {
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.decay_ = decay;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.minVelocity_ = minVelocity;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.delay_ = delay;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.points_ = [];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.angle_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.initialVelocity_ = 0;
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.Kinetic.prototype.begin = function() {
+ this.points_.length = 0;
+ this.angle_ = 0;
+ this.initialVelocity_ = 0;
+};
+
+
+/**
+ * @param {number} x X.
+ * @param {number} y Y.
+ */
+ol.Kinetic.prototype.update = function(x, y) {
+ this.points_.push(x, y, Date.now());
+};
+
+
+/**
+ * @return {boolean} Whether we should do kinetic animation.
+ */
+ol.Kinetic.prototype.end = function() {
+ if (this.points_.length < 6) {
+ // at least 2 points are required (i.e. there must be at least 6 elements
+ // in the array)
+ return false;
+ }
+ var delay = Date.now() - this.delay_;
+ var lastIndex = this.points_.length - 3;
+ if (this.points_[lastIndex + 2] < delay) {
+ // the last tracked point is too old, which means that the user stopped
+ // panning before releasing the map
+ return false;
+ }
+
+ // get the first point which still falls into the delay time
+ var firstIndex = lastIndex - 3;
+ while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) {
+ firstIndex -= 3;
+ }
+ var duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2];
+ var dx = this.points_[lastIndex] - this.points_[firstIndex];
+ var dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1];
+ this.angle_ = Math.atan2(dy, dx);
+ this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration;
+ return this.initialVelocity_ > this.minVelocity_;
+};
+
+
+/**
+ * @private
+ * @return {number} Duration of animation (milliseconds).
+ */
+ol.Kinetic.prototype.getDuration_ = function() {
+ return Math.log(this.minVelocity_ / this.initialVelocity_) / this.decay_;
+};
+
+
+/**
+ * @return {number} Total distance travelled (pixels).
+ */
+ol.Kinetic.prototype.getDistance = function() {
+ return (this.minVelocity_ - this.initialVelocity_) / this.decay_;
+};
+
+
+/**
+ * @return {number} Angle of the kinetic panning animation (radians).
+ */
+ol.Kinetic.prototype.getAngle = function() {
+ return this.angle_;
+};
+
+// FIXME factor out key precondition (shift et. al)
+
+goog.provide('ol.interaction.Interaction');
+
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.easing');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * User actions that change the state of the map. Some are similar to controls,
+ * but are not associated with a DOM element.
+ * For example, {@link ol.interaction.KeyboardZoom} is functionally the same as
+ * {@link ol.control.Zoom}, but triggered by a keyboard event not a button
+ * element event.
+ * Although interactions do not have a DOM element, some of them do render
+ * vectors and so are visible on the screen.
+ *
+ * @constructor
+ * @param {olx.interaction.InteractionOptions} options Options.
+ * @extends {ol.Object}
+ * @api
+ */
+ol.interaction.Interaction = function(options) {
+
+ ol.Object.call(this);
+
+ /**
+ * @private
+ * @type {ol.Map}
+ */
+ this.map_ = null;
+
+ this.setActive(true);
+
+ /**
+ * @type {function(ol.MapBrowserEvent):boolean}
+ */
+ this.handleEvent = options.handleEvent;
+
+};
+ol.inherits(ol.interaction.Interaction, ol.Object);
+
+
+/**
+ * Return whether the interaction is currently active.
+ * @return {boolean} `true` if the interaction is active, `false` otherwise.
+ * @observable
+ * @api
+ */
+ol.interaction.Interaction.prototype.getActive = function() {
+ return /** @type {boolean} */ (
+ this.get(ol.interaction.Interaction.Property.ACTIVE));
+};
+
+
+/**
+ * Get the map associated with this interaction.
+ * @return {ol.Map} Map.
+ * @api
+ */
+ol.interaction.Interaction.prototype.getMap = function() {
+ return this.map_;
+};
+
+
+/**
+ * Activate or deactivate the interaction.
+ * @param {boolean} active Active.
+ * @observable
+ * @api
+ */
+ol.interaction.Interaction.prototype.setActive = function(active) {
+ this.set(ol.interaction.Interaction.Property.ACTIVE, active);
+};
+
+
+/**
+ * Remove the interaction from its current map and attach it to the new map.
+ * Subclasses may set up event handlers to get notified about changes to
+ * the map here.
+ * @param {ol.Map} map Map.
+ */
+ol.interaction.Interaction.prototype.setMap = function(map) {
+ this.map_ = map;
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {ol.View} view View.
+ * @param {ol.Coordinate} delta Delta.
+ * @param {number=} opt_duration Duration.
+ */
+ol.interaction.Interaction.pan = function(map, view, delta, opt_duration) {
+ var currentCenter = view.getCenter();
+ if (currentCenter) {
+ var center = view.constrainCenter(
+ [currentCenter[0] + delta[0], currentCenter[1] + delta[1]]);
+ if (opt_duration) {
+ view.animate({
+ duration: opt_duration,
+ easing: ol.easing.linear,
+ center: center
+ });
+ } else {
+ view.setCenter(center);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {ol.View} view View.
+ * @param {number|undefined} rotation Rotation.
+ * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
+ * @param {number=} opt_duration Duration.
+ */
+ol.interaction.Interaction.rotate = function(map, view, rotation, opt_anchor, opt_duration) {
+ rotation = view.constrainRotation(rotation, 0);
+ ol.interaction.Interaction.rotateWithoutConstraints(
+ map, view, rotation, opt_anchor, opt_duration);
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {ol.View} view View.
+ * @param {number|undefined} rotation Rotation.
+ * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
+ * @param {number=} opt_duration Duration.
+ */
+ol.interaction.Interaction.rotateWithoutConstraints = function(map, view, rotation, opt_anchor, opt_duration) {
+ if (rotation !== undefined) {
+ var currentRotation = view.getRotation();
+ var currentCenter = view.getCenter();
+ if (currentRotation !== undefined && currentCenter && opt_duration > 0) {
+ view.animate({
+ rotation: rotation,
+ anchor: opt_anchor,
+ duration: opt_duration,
+ easing: ol.easing.easeOut
+ });
+ } else {
+ view.rotate(rotation, opt_anchor);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {ol.View} view View.
+ * @param {number|undefined} resolution Resolution to go to.
+ * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
+ * @param {number=} opt_duration Duration.
+ * @param {number=} opt_direction Zooming direction; > 0 indicates
+ * zooming out, in which case the constraints system will select
+ * the largest nearest resolution; < 0 indicates zooming in, in
+ * which case the constraints system will select the smallest
+ * nearest resolution; == 0 indicates that the zooming direction
+ * is unknown/not relevant, in which case the constraints system
+ * will select the nearest resolution. If not defined 0 is
+ * assumed.
+ */
+ol.interaction.Interaction.zoom = function(map, view, resolution, opt_anchor, opt_duration, opt_direction) {
+ resolution = view.constrainResolution(resolution, 0, opt_direction);
+ ol.interaction.Interaction.zoomWithoutConstraints(
+ map, view, resolution, opt_anchor, opt_duration);
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {ol.View} view View.
+ * @param {number} delta Delta from previous zoom level.
+ * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
+ * @param {number=} opt_duration Duration.
+ */
+ol.interaction.Interaction.zoomByDelta = function(map, view, delta, opt_anchor, opt_duration) {
+ var currentResolution = view.getResolution();
+ var resolution = view.constrainResolution(currentResolution, delta, 0);
+ ol.interaction.Interaction.zoomWithoutConstraints(
+ map, view, resolution, opt_anchor, opt_duration);
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {ol.View} view View.
+ * @param {number|undefined} resolution Resolution to go to.
+ * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
+ * @param {number=} opt_duration Duration.
+ */
+ol.interaction.Interaction.zoomWithoutConstraints = function(map, view, resolution, opt_anchor, opt_duration) {
+ if (resolution) {
+ var currentResolution = view.getResolution();
+ var currentCenter = view.getCenter();
+ if (currentResolution !== undefined && currentCenter &&
+ resolution !== currentResolution && opt_duration) {
+ view.animate({
+ resolution: resolution,
+ anchor: opt_anchor,
+ duration: opt_duration,
+ easing: ol.easing.easeOut
+ });
+ } else {
+ if (opt_anchor) {
+ var center = view.calculateCenterZoom(resolution, opt_anchor);
+ view.setCenter(center);
+ }
+ view.setResolution(resolution);
+ }
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.Interaction.Property = {
+ ACTIVE: 'active'
+};
+
+goog.provide('ol.interaction.DoubleClickZoom');
+
+goog.require('ol');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.interaction.Interaction');
+
+
+/**
+ * @classdesc
+ * Allows the user to zoom by double-clicking on the map.
+ *
+ * @constructor
+ * @extends {ol.interaction.Interaction}
+ * @param {olx.interaction.DoubleClickZoomOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DoubleClickZoom = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.delta_ = options.delta ? options.delta : 1;
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: ol.interaction.DoubleClickZoom.handleEvent
+ });
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 250;
+
+};
+ol.inherits(ol.interaction.DoubleClickZoom, ol.interaction.Interaction);
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} (if it was a
+ * doubleclick) and eventually zooms the map.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.DoubleClickZoom}
+ * @api
+ */
+ol.interaction.DoubleClickZoom.handleEvent = function(mapBrowserEvent) {
+ var stopEvent = false;
+ var browserEvent = mapBrowserEvent.originalEvent;
+ if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK) {
+ var map = mapBrowserEvent.map;
+ var anchor = mapBrowserEvent.coordinate;
+ var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_;
+ var view = map.getView();
+ ol.interaction.Interaction.zoomByDelta(
+ map, view, delta, anchor, this.duration_);
+ mapBrowserEvent.preventDefault();
+ stopEvent = true;
+ }
+ return !stopEvent;
+};
+
+goog.provide('ol.events.condition');
+
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.asserts');
+goog.require('ol.functions');
+goog.require('ol.has');
+
+
+/**
+ * Return `true` if only the alt-key is pressed, `false` otherwise (e.g. when
+ * additionally the shift-key is pressed).
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if only the alt key is pressed.
+ * @api stable
+ */
+ol.events.condition.altKeyOnly = function(mapBrowserEvent) {
+ var originalEvent = mapBrowserEvent.originalEvent;
+ return (
+ originalEvent.altKey &&
+ !(originalEvent.metaKey || originalEvent.ctrlKey) &&
+ !originalEvent.shiftKey);
+};
+
+
+/**
+ * Return `true` if only the alt-key and shift-key is pressed, `false` otherwise
+ * (e.g. when additionally the platform-modifier-key is pressed).
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if only the alt and shift keys are pressed.
+ * @api stable
+ */
+ol.events.condition.altShiftKeysOnly = function(mapBrowserEvent) {
+ var originalEvent = mapBrowserEvent.originalEvent;
+ return (
+ originalEvent.altKey &&
+ !(originalEvent.metaKey || originalEvent.ctrlKey) &&
+ originalEvent.shiftKey);
+};
+
+
+/**
+ * Return always true.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True.
+ * @function
+ * @api stable
+ */
+ol.events.condition.always = ol.functions.TRUE;
+
+
+/**
+ * Return `true` if the event is a `click` event, `false` otherwise.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if the event is a map `click` event.
+ * @api stable
+ */
+ol.events.condition.click = function(mapBrowserEvent) {
+ return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.CLICK;
+};
+
+
+/**
+ * Return `true` if the event has an "action"-producing mouse button.
+ *
+ * By definition, this includes left-click on windows/linux, and left-click
+ * without the ctrl key on Macs.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} The result.
+ */
+ol.events.condition.mouseActionButton = function(mapBrowserEvent) {
+ var originalEvent = mapBrowserEvent.originalEvent;
+ return originalEvent.button == 0 &&
+ !(ol.has.WEBKIT && ol.has.MAC && originalEvent.ctrlKey);
+};
+
+
+/**
+ * Return always false.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} False.
+ * @function
+ * @api stable
+ */
+ol.events.condition.never = ol.functions.FALSE;
+
+
+/**
+ * Return `true` if the browser event is a `pointermove` event, `false`
+ * otherwise.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if the browser event is a `pointermove` event.
+ * @api
+ */
+ol.events.condition.pointerMove = function(mapBrowserEvent) {
+ return mapBrowserEvent.type == 'pointermove';
+};
+
+
+/**
+ * Return `true` if the event is a map `singleclick` event, `false` otherwise.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if the event is a map `singleclick` event.
+ * @api stable
+ */
+ol.events.condition.singleClick = function(mapBrowserEvent) {
+ return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.SINGLECLICK;
+};
+
+
+/**
+ * Return `true` if the event is a map `dblclick` event, `false` otherwise.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if the event is a map `dblclick` event.
+ * @api stable
+ */
+ol.events.condition.doubleClick = function(mapBrowserEvent) {
+ return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK;
+};
+
+
+/**
+ * Return `true` if no modifier key (alt-, shift- or platform-modifier-key) is
+ * pressed.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True only if there no modifier keys are pressed.
+ * @api stable
+ */
+ol.events.condition.noModifierKeys = function(mapBrowserEvent) {
+ var originalEvent = mapBrowserEvent.originalEvent;
+ return (
+ !originalEvent.altKey &&
+ !(originalEvent.metaKey || originalEvent.ctrlKey) &&
+ !originalEvent.shiftKey);
+};
+
+
+/**
+ * Return `true` if only the platform-modifier-key (the meta-key on Mac,
+ * ctrl-key otherwise) is pressed, `false` otherwise (e.g. when additionally
+ * the shift-key is pressed).
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if only the platform modifier key is pressed.
+ * @api stable
+ */
+ol.events.condition.platformModifierKeyOnly = function(mapBrowserEvent) {
+ var originalEvent = mapBrowserEvent.originalEvent;
+ return (
+ !originalEvent.altKey &&
+ (ol.has.MAC ? originalEvent.metaKey : originalEvent.ctrlKey) &&
+ !originalEvent.shiftKey);
+};
+
+
+/**
+ * Return `true` if only the shift-key is pressed, `false` otherwise (e.g. when
+ * additionally the alt-key is pressed).
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if only the shift key is pressed.
+ * @api stable
+ */
+ol.events.condition.shiftKeyOnly = function(mapBrowserEvent) {
+ var originalEvent = mapBrowserEvent.originalEvent;
+ return (
+ !originalEvent.altKey &&
+ !(originalEvent.metaKey || originalEvent.ctrlKey) &&
+ originalEvent.shiftKey);
+};
+
+
+/**
+ * Return `true` if the target element is not editable, i.e. not a `<input>`-,
+ * `<select>`- or `<textarea>`-element, `false` otherwise.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True only if the target element is not editable.
+ * @api
+ */
+ol.events.condition.targetNotEditable = function(mapBrowserEvent) {
+ var target = mapBrowserEvent.originalEvent.target;
+ var tagName = target.tagName;
+ return (
+ tagName !== 'INPUT' &&
+ tagName !== 'SELECT' &&
+ tagName !== 'TEXTAREA');
+};
+
+
+/**
+ * Return `true` if the event originates from a mouse device.
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if the event originates from a mouse device.
+ * @api stable
+ */
+ol.events.condition.mouseOnly = function(mapBrowserEvent) {
+ ol.asserts.assert(mapBrowserEvent.pointerEvent, 56); // mapBrowserEvent must originate from a pointer event
+ // see http://www.w3.org/TR/pointerevents/#widl-PointerEvent-pointerType
+ return /** @type {ol.MapBrowserEvent} */ (mapBrowserEvent).pointerEvent.pointerType == 'mouse';
+};
+
+
+/**
+ * Return `true` if the event originates from a primary pointer in
+ * contact with the surface or if the left mouse button is pressed.
+ * @see http://www.w3.org/TR/pointerevents/#button-states
+ *
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} True if the event originates from a primary pointer.
+ * @api
+ */
+ol.events.condition.primaryAction = function(mapBrowserEvent) {
+ var pointerEvent = mapBrowserEvent.pointerEvent;
+ return pointerEvent.isPrimary && pointerEvent.button === 0;
+};
+
+goog.provide('ol.interaction.Pointer');
+
+goog.require('ol');
+goog.require('ol.functions');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.MapBrowserPointerEvent');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * Base class that calls user-defined functions on `down`, `move` and `up`
+ * events. This class also manages "drag sequences".
+ *
+ * When the `handleDownEvent` user function returns `true` a drag sequence is
+ * started. During a drag sequence the `handleDragEvent` user function is
+ * called on `move` events. The drag sequence ends when the `handleUpEvent`
+ * user function is called and returns `false`.
+ *
+ * @constructor
+ * @param {olx.interaction.PointerOptions=} opt_options Options.
+ * @extends {ol.interaction.Interaction}
+ * @api
+ */
+ol.interaction.Pointer = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var handleEvent = options.handleEvent ?
+ options.handleEvent : ol.interaction.Pointer.handleEvent;
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: handleEvent
+ });
+
+ /**
+ * @type {function(ol.MapBrowserPointerEvent):boolean}
+ * @private
+ */
+ this.handleDownEvent_ = options.handleDownEvent ?
+ options.handleDownEvent : ol.interaction.Pointer.handleDownEvent;
+
+ /**
+ * @type {function(ol.MapBrowserPointerEvent)}
+ * @private
+ */
+ this.handleDragEvent_ = options.handleDragEvent ?
+ options.handleDragEvent : ol.interaction.Pointer.handleDragEvent;
+
+ /**
+ * @type {function(ol.MapBrowserPointerEvent)}
+ * @private
+ */
+ this.handleMoveEvent_ = options.handleMoveEvent ?
+ options.handleMoveEvent : ol.interaction.Pointer.handleMoveEvent;
+
+ /**
+ * @type {function(ol.MapBrowserPointerEvent):boolean}
+ * @private
+ */
+ this.handleUpEvent_ = options.handleUpEvent ?
+ options.handleUpEvent : ol.interaction.Pointer.handleUpEvent;
+
+ /**
+ * @type {boolean}
+ * @protected
+ */
+ this.handlingDownUpSequence = false;
+
+ /**
+ * @type {Object.<number, ol.pointer.PointerEvent>}
+ * @private
+ */
+ this.trackedPointers_ = {};
+
+ /**
+ * @type {Array.<ol.pointer.PointerEvent>}
+ * @protected
+ */
+ this.targetPointers = [];
+
+};
+ol.inherits(ol.interaction.Pointer, ol.interaction.Interaction);
+
+
+/**
+ * @param {Array.<ol.pointer.PointerEvent>} pointerEvents List of events.
+ * @return {ol.Pixel} Centroid pixel.
+ */
+ol.interaction.Pointer.centroid = function(pointerEvents) {
+ var length = pointerEvents.length;
+ var clientX = 0;
+ var clientY = 0;
+ for (var i = 0; i < length; i++) {
+ clientX += pointerEvents[i].clientX;
+ clientY += pointerEvents[i].clientY;
+ }
+ return [clientX / length, clientY / length];
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Whether the event is a pointerdown, pointerdrag
+ * or pointerup event.
+ * @private
+ */
+ol.interaction.Pointer.prototype.isPointerDraggingEvent_ = function(mapBrowserEvent) {
+ var type = mapBrowserEvent.type;
+ return (
+ type === ol.MapBrowserEvent.EventType.POINTERDOWN ||
+ type === ol.MapBrowserEvent.EventType.POINTERDRAG ||
+ type === ol.MapBrowserEvent.EventType.POINTERUP);
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @private
+ */
+ol.interaction.Pointer.prototype.updateTrackedPointers_ = function(mapBrowserEvent) {
+ if (this.isPointerDraggingEvent_(mapBrowserEvent)) {
+ var event = mapBrowserEvent.pointerEvent;
+
+ if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERUP) {
+ delete this.trackedPointers_[event.pointerId];
+ } else if (mapBrowserEvent.type ==
+ ol.MapBrowserEvent.EventType.POINTERDOWN) {
+ this.trackedPointers_[event.pointerId] = event;
+ } else if (event.pointerId in this.trackedPointers_) {
+ // update only when there was a pointerdown event for this pointer
+ this.trackedPointers_[event.pointerId] = event;
+ }
+ this.targetPointers = ol.obj.getValues(this.trackedPointers_);
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.Pointer}
+ */
+ol.interaction.Pointer.handleDragEvent = ol.nullFunction;
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Capture dragging.
+ * @this {ol.interaction.Pointer}
+ */
+ol.interaction.Pointer.handleUpEvent = ol.functions.FALSE;
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Capture dragging.
+ * @this {ol.interaction.Pointer}
+ */
+ol.interaction.Pointer.handleDownEvent = ol.functions.FALSE;
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.Pointer}
+ */
+ol.interaction.Pointer.handleMoveEvent = ol.nullFunction;
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} and may call into
+ * other functions, if event sequences like e.g. 'drag' or 'down-up' etc. are
+ * detected.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.Pointer}
+ * @api
+ */
+ol.interaction.Pointer.handleEvent = function(mapBrowserEvent) {
+ if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) {
+ return true;
+ }
+
+ var stopEvent = false;
+ this.updateTrackedPointers_(mapBrowserEvent);
+ if (this.handlingDownUpSequence) {
+ if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERDRAG) {
+ this.handleDragEvent_(mapBrowserEvent);
+ } else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERUP) {
+ this.handlingDownUpSequence = this.handleUpEvent_(mapBrowserEvent);
+ }
+ }
+ if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
+ var handled = this.handleDownEvent_(mapBrowserEvent);
+ this.handlingDownUpSequence = handled;
+ stopEvent = this.shouldStopEvent(handled);
+ } else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE) {
+ this.handleMoveEvent_(mapBrowserEvent);
+ }
+ return !stopEvent;
+};
+
+
+/**
+ * This method is used to determine if "down" events should be propagated to
+ * other interactions or should be stopped.
+ *
+ * The method receives the return code of the "handleDownEvent" function.
+ *
+ * By default this function is the "identity" function. It's overidden in
+ * child classes.
+ *
+ * @param {boolean} handled Was the event handled by the interaction?
+ * @return {boolean} Should the event be stopped?
+ * @protected
+ */
+ol.interaction.Pointer.prototype.shouldStopEvent = function(handled) {
+ return handled;
+};
+
+goog.provide('ol.interaction.DragPan');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.coordinate');
+goog.require('ol.easing');
+goog.require('ol.events.condition');
+goog.require('ol.functions');
+goog.require('ol.interaction.Pointer');
+
+
+/**
+ * @classdesc
+ * Allows the user to pan the map by dragging the map.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.DragPanOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DragPan = function(opt_options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.DragPan.handleDownEvent_,
+ handleDragEvent: ol.interaction.DragPan.handleDragEvent_,
+ handleUpEvent: ol.interaction.DragPan.handleUpEvent_
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {ol.Kinetic|undefined}
+ */
+ this.kinetic_ = options.kinetic;
+
+ /**
+ * @type {ol.Pixel}
+ */
+ this.lastCentroid = null;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.noModifierKeys;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.noKinetic_ = false;
+
+};
+ol.inherits(ol.interaction.DragPan, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.DragPan}
+ * @private
+ */
+ol.interaction.DragPan.handleDragEvent_ = function(mapBrowserEvent) {
+ ol.DEBUG && console.assert(this.targetPointers.length >= 1,
+ 'the length of this.targetPointers should be more than 1');
+ var centroid =
+ ol.interaction.Pointer.centroid(this.targetPointers);
+ if (this.kinetic_) {
+ this.kinetic_.update(centroid[0], centroid[1]);
+ }
+ if (this.lastCentroid) {
+ var deltaX = this.lastCentroid[0] - centroid[0];
+ var deltaY = centroid[1] - this.lastCentroid[1];
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ var viewState = view.getState();
+ var center = [deltaX, deltaY];
+ ol.coordinate.scale(center, viewState.resolution);
+ ol.coordinate.rotate(center, viewState.rotation);
+ ol.coordinate.add(center, viewState.center);
+ center = view.constrainCenter(center);
+ view.setCenter(center);
+ }
+ this.lastCentroid = centroid;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.DragPan}
+ * @private
+ */
+ol.interaction.DragPan.handleUpEvent_ = function(mapBrowserEvent) {
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ if (this.targetPointers.length === 0) {
+ if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) {
+ var distance = this.kinetic_.getDistance();
+ var angle = this.kinetic_.getAngle();
+ var center = /** @type {!ol.Coordinate} */ (view.getCenter());
+ var centerpx = map.getPixelFromCoordinate(center);
+ var dest = map.getCoordinateFromPixel([
+ centerpx[0] - distance * Math.cos(angle),
+ centerpx[1] - distance * Math.sin(angle)
+ ]);
+ view.animate({
+ center: view.constrainCenter(dest),
+ duration: 500,
+ easing: ol.easing.easeOut
+ });
+ }
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+ return false;
+ } else {
+ this.lastCentroid = null;
+ return true;
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.DragPan}
+ * @private
+ */
+ol.interaction.DragPan.handleDownEvent_ = function(mapBrowserEvent) {
+ if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ this.lastCentroid = null;
+ if (!this.handlingDownUpSequence) {
+ view.setHint(ol.View.Hint.INTERACTING, 1);
+ }
+ // stop any current animation
+ view.setCenter(mapBrowserEvent.frameState.viewState.center);
+ if (this.kinetic_) {
+ this.kinetic_.begin();
+ }
+ // No kinetic as soon as more than one pointer on the screen is
+ // detected. This is to prevent nasty pans after pinch.
+ this.noKinetic_ = this.targetPointers.length > 1;
+ return true;
+ } else {
+ return false;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.DragPan.prototype.shouldStopEvent = ol.functions.FALSE;
+
+goog.provide('ol.interaction.DragRotate');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.events.condition');
+goog.require('ol.functions');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.interaction.Pointer');
+
+
+/**
+ * @classdesc
+ * Allows the user to rotate the map by clicking and dragging on the map,
+ * normally combined with an {@link ol.events.condition} that limits
+ * it to when the alt and shift keys are held down.
+ *
+ * This interaction is only supported for mouse devices.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.DragRotateOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DragRotate = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.DragRotate.handleDownEvent_,
+ handleDragEvent: ol.interaction.DragRotate.handleDragEvent_,
+ handleUpEvent: ol.interaction.DragRotate.handleUpEvent_
+ });
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.altShiftKeysOnly;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.lastAngle_ = undefined;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 250;
+};
+ol.inherits(ol.interaction.DragRotate, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.DragRotate}
+ * @private
+ */
+ol.interaction.DragRotate.handleDragEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return;
+ }
+
+ var map = mapBrowserEvent.map;
+ var size = map.getSize();
+ var offset = mapBrowserEvent.pixel;
+ var theta =
+ Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2);
+ if (this.lastAngle_ !== undefined) {
+ var delta = theta - this.lastAngle_;
+ var view = map.getView();
+ var rotation = view.getRotation();
+ ol.interaction.Interaction.rotateWithoutConstraints(
+ map, view, rotation - delta);
+ }
+ this.lastAngle_ = theta;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.DragRotate}
+ * @private
+ */
+ol.interaction.DragRotate.handleUpEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return true;
+ }
+
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+ var rotation = view.getRotation();
+ ol.interaction.Interaction.rotate(map, view, rotation,
+ undefined, this.duration_);
+ return false;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.DragRotate}
+ * @private
+ */
+ol.interaction.DragRotate.handleDownEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return false;
+ }
+
+ if (ol.events.condition.mouseActionButton(mapBrowserEvent) &&
+ this.condition_(mapBrowserEvent)) {
+ var map = mapBrowserEvent.map;
+ map.getView().setHint(ol.View.Hint.INTERACTING, 1);
+ this.lastAngle_ = undefined;
+ return true;
+ } else {
+ return false;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.DragRotate.prototype.shouldStopEvent = ol.functions.FALSE;
+
+// FIXME add rotation
+
+goog.provide('ol.render.Box');
+
+goog.require('ol');
+goog.require('ol.Disposable');
+goog.require('ol.geom.Polygon');
+
+
+/**
+ * @constructor
+ * @extends {ol.Disposable}
+ * @param {string} className CSS class name.
+ */
+ol.render.Box = function(className) {
+
+ /**
+ * @type {ol.geom.Polygon}
+ * @private
+ */
+ this.geometry_ = null;
+
+ /**
+ * @type {HTMLDivElement}
+ * @private
+ */
+ this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div'));
+ this.element_.style.position = 'absolute';
+ this.element_.className = 'ol-box ' + className;
+
+ /**
+ * @private
+ * @type {ol.Map}
+ */
+ this.map_ = null;
+
+ /**
+ * @private
+ * @type {ol.Pixel}
+ */
+ this.startPixel_ = null;
+
+ /**
+ * @private
+ * @type {ol.Pixel}
+ */
+ this.endPixel_ = null;
+
+};
+ol.inherits(ol.render.Box, ol.Disposable);
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.Box.prototype.disposeInternal = function() {
+ this.setMap(null);
+};
+
+
+/**
+ * @private
+ */
+ol.render.Box.prototype.render_ = function() {
+ var startPixel = this.startPixel_;
+ var endPixel = this.endPixel_;
+ var px = 'px';
+ var style = this.element_.style;
+ style.left = Math.min(startPixel[0], endPixel[0]) + px;
+ style.top = Math.min(startPixel[1], endPixel[1]) + px;
+ style.width = Math.abs(endPixel[0] - startPixel[0]) + px;
+ style.height = Math.abs(endPixel[1] - startPixel[1]) + px;
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ */
+ol.render.Box.prototype.setMap = function(map) {
+ if (this.map_) {
+ this.map_.getOverlayContainer().removeChild(this.element_);
+ var style = this.element_.style;
+ style.left = style.top = style.width = style.height = 'inherit';
+ }
+ this.map_ = map;
+ if (this.map_) {
+ this.map_.getOverlayContainer().appendChild(this.element_);
+ }
+};
+
+
+/**
+ * @param {ol.Pixel} startPixel Start pixel.
+ * @param {ol.Pixel} endPixel End pixel.
+ */
+ol.render.Box.prototype.setPixels = function(startPixel, endPixel) {
+ this.startPixel_ = startPixel;
+ this.endPixel_ = endPixel;
+ this.createOrUpdateGeometry();
+ this.render_();
+};
+
+
+/**
+ * Creates or updates the cached geometry.
+ */
+ol.render.Box.prototype.createOrUpdateGeometry = function() {
+ var startPixel = this.startPixel_;
+ var endPixel = this.endPixel_;
+ var pixels = [
+ startPixel,
+ [startPixel[0], endPixel[1]],
+ endPixel,
+ [endPixel[0], startPixel[1]]
+ ];
+ var coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_);
+ // close the polygon
+ coordinates[4] = coordinates[0].slice();
+ if (!this.geometry_) {
+ this.geometry_ = new ol.geom.Polygon([coordinates]);
+ } else {
+ this.geometry_.setCoordinates([coordinates]);
+ }
+};
+
+
+/**
+ * @return {ol.geom.Polygon} Geometry.
+ */
+ol.render.Box.prototype.getGeometry = function() {
+ return this.geometry_;
+};
+
+// FIXME draw drag box
+goog.provide('ol.interaction.DragBox');
+
+goog.require('ol.events.Event');
+goog.require('ol');
+goog.require('ol.events.condition');
+goog.require('ol.interaction.Pointer');
+goog.require('ol.render.Box');
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED =
+ ol.DRAG_BOX_HYSTERESIS_PIXELS *
+ ol.DRAG_BOX_HYSTERESIS_PIXELS;
+
+
+/**
+ * @classdesc
+ * Allows the user to draw a vector box by clicking and dragging on the map,
+ * normally combined with an {@link ol.events.condition} that limits
+ * it to when the shift or other key is held down. This is used, for example,
+ * for zooming to a specific area of the map
+ * (see {@link ol.interaction.DragZoom} and
+ * {@link ol.interaction.DragRotateAndZoom}).
+ *
+ * This interaction is only supported for mouse devices.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @fires ol.interaction.DragBox.Event
+ * @param {olx.interaction.DragBoxOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DragBox = function(opt_options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.DragBox.handleDownEvent_,
+ handleDragEvent: ol.interaction.DragBox.handleDragEvent_,
+ handleUpEvent: ol.interaction.DragBox.handleUpEvent_
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @type {ol.render.Box}
+ * @private
+ */
+ this.box_ = new ol.render.Box(options.className || 'ol-dragbox');
+
+ /**
+ * @type {ol.Pixel}
+ * @private
+ */
+ this.startPixel_ = null;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.always;
+
+ /**
+ * @private
+ * @type {ol.DragBoxEndConditionType}
+ */
+ this.boxEndCondition_ = options.boxEndCondition ?
+ options.boxEndCondition : ol.interaction.DragBox.defaultBoxEndCondition;
+};
+ol.inherits(ol.interaction.DragBox, ol.interaction.Pointer);
+
+
+/**
+ * The default condition for determining whether the boxend event
+ * should fire.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent The originating MapBrowserEvent
+ * leading to the box end.
+ * @param {ol.Pixel} startPixel The starting pixel of the box.
+ * @param {ol.Pixel} endPixel The end pixel of the box.
+ * @return {boolean} Whether or not the boxend condition should be fired.
+ */
+ol.interaction.DragBox.defaultBoxEndCondition = function(mapBrowserEvent,
+ startPixel, endPixel) {
+ var width = endPixel[0] - startPixel[0];
+ var height = endPixel[1] - startPixel[1];
+ return width * width + height * height >=
+ ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.DragBox}
+ * @private
+ */
+ol.interaction.DragBox.handleDragEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return;
+ }
+
+ this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
+
+ this.dispatchEvent(new ol.interaction.DragBox.Event(ol.interaction.DragBox.EventType.BOXDRAG,
+ mapBrowserEvent.coordinate, mapBrowserEvent));
+};
+
+
+/**
+ * Returns geometry of last drawn box.
+ * @return {ol.geom.Polygon} Geometry.
+ * @api stable
+ */
+ol.interaction.DragBox.prototype.getGeometry = function() {
+ return this.box_.getGeometry();
+};
+
+
+/**
+ * To be overriden by child classes.
+ * FIXME: use constructor option instead of relying on overridding.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @protected
+ */
+ol.interaction.DragBox.prototype.onBoxEnd = ol.nullFunction;
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.DragBox}
+ * @private
+ */
+ol.interaction.DragBox.handleUpEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return true;
+ }
+
+ this.box_.setMap(null);
+
+ if (this.boxEndCondition_(mapBrowserEvent,
+ this.startPixel_, mapBrowserEvent.pixel)) {
+ this.onBoxEnd(mapBrowserEvent);
+ this.dispatchEvent(new ol.interaction.DragBox.Event(ol.interaction.DragBox.EventType.BOXEND,
+ mapBrowserEvent.coordinate, mapBrowserEvent));
+ }
+ return false;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.DragBox}
+ * @private
+ */
+ol.interaction.DragBox.handleDownEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return false;
+ }
+
+ if (ol.events.condition.mouseActionButton(mapBrowserEvent) &&
+ this.condition_(mapBrowserEvent)) {
+ this.startPixel_ = mapBrowserEvent.pixel;
+ this.box_.setMap(mapBrowserEvent.map);
+ this.box_.setPixels(this.startPixel_, this.startPixel_);
+ this.dispatchEvent(new ol.interaction.DragBox.Event(ol.interaction.DragBox.EventType.BOXSTART,
+ mapBrowserEvent.coordinate, mapBrowserEvent));
+ return true;
+ } else {
+ return false;
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.DragBox.EventType = {
+ /**
+ * Triggered upon drag box start.
+ * @event ol.interaction.DragBox.Event#boxstart
+ * @api stable
+ */
+ BOXSTART: 'boxstart',
+
+ /**
+ * Triggered on drag when box is active.
+ * @event ol.interaction.DragBox.Event#boxdrag
+ * @api
+ */
+ BOXDRAG: 'boxdrag',
+
+ /**
+ * Triggered upon drag box end.
+ * @event ol.interaction.DragBox.Event#boxend
+ * @api stable
+ */
+ BOXEND: 'boxend'
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.DragBox} instances are instances of
+ * this type.
+ *
+ * @param {string} type The event type.
+ * @param {ol.Coordinate} coordinate The event coordinate.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Originating event.
+ * @extends {ol.events.Event}
+ * @constructor
+ * @implements {oli.DragBoxEvent}
+ */
+ol.interaction.DragBox.Event = function(type, coordinate, mapBrowserEvent) {
+ ol.events.Event.call(this, type);
+
+ /**
+ * The coordinate of the drag event.
+ * @const
+ * @type {ol.Coordinate}
+ * @api stable
+ */
+ this.coordinate = coordinate;
+
+ /**
+ * @const
+ * @type {ol.MapBrowserEvent}
+ * @api
+ */
+ this.mapBrowserEvent = mapBrowserEvent;
+
+};
+ol.inherits(ol.interaction.DragBox.Event, ol.events.Event);
+
+goog.provide('ol.interaction.DragZoom');
+
+goog.require('ol');
+goog.require('ol.easing');
+goog.require('ol.events.condition');
+goog.require('ol.extent');
+goog.require('ol.interaction.DragBox');
+
+
+/**
+ * @classdesc
+ * Allows the user to zoom the map by clicking and dragging on the map,
+ * normally combined with an {@link ol.events.condition} that limits
+ * it to when a key, shift by default, is held down.
+ *
+ * To change the style of the box, use CSS and the `.ol-dragzoom` selector, or
+ * your custom one configured with `className`.
+ *
+ * @constructor
+ * @extends {ol.interaction.DragBox}
+ * @param {olx.interaction.DragZoomOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DragZoom = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+
+ var condition = options.condition ?
+ options.condition : ol.events.condition.shiftKeyOnly;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 200;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.out_ = options.out !== undefined ? options.out : false;
+
+ ol.interaction.DragBox.call(this, {
+ condition: condition,
+ className: options.className || 'ol-dragzoom'
+ });
+
+};
+ol.inherits(ol.interaction.DragZoom, ol.interaction.DragBox);
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.DragZoom.prototype.onBoxEnd = function() {
+ var map = this.getMap();
+
+ var view = /** @type {!ol.View} */ (map.getView());
+
+ var size = /** @type {!ol.Size} */ (map.getSize());
+
+ var extent = this.getGeometry().getExtent();
+
+ if (this.out_) {
+ var mapExtent = view.calculateExtent(size);
+ var boxPixelExtent = ol.extent.createOrUpdateFromCoordinates([
+ map.getPixelFromCoordinate(ol.extent.getBottomLeft(extent)),
+ map.getPixelFromCoordinate(ol.extent.getTopRight(extent))]);
+ var factor = view.getResolutionForExtent(boxPixelExtent, size);
+
+ ol.extent.scaleFromCenter(mapExtent, 1 / factor);
+ extent = mapExtent;
+ }
+
+ var resolution = view.constrainResolution(
+ view.getResolutionForExtent(extent, size));
+
+ view.animate({
+ resolution: resolution,
+ center: ol.extent.getCenter(extent),
+ duration: this.duration_,
+ easing: ol.easing.easeOut
+ });
+
+};
+
+goog.provide('ol.events.KeyCode');
+
+/**
+ * @enum {number}
+ * @const
+ */
+ol.events.KeyCode = {
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40
+};
+
+goog.provide('ol.interaction.KeyboardPan');
+
+goog.require('ol');
+goog.require('ol.coordinate');
+goog.require('ol.events.EventType');
+goog.require('ol.events.KeyCode');
+goog.require('ol.events.condition');
+goog.require('ol.interaction.Interaction');
+
+
+/**
+ * @classdesc
+ * Allows the user to pan the map using keyboard arrows.
+ * Note that, although this interaction is by default included in maps,
+ * the keys can only be used when browser focus is on the element to which
+ * the keyboard events are attached. By default, this is the map div,
+ * though you can change this with the `keyboardEventTarget` in
+ * {@link ol.Map}. `document` never loses focus but, for any other element,
+ * focus will have to be on, and returned to, this element if the keys are to
+ * function.
+ * See also {@link ol.interaction.KeyboardZoom}.
+ *
+ * @constructor
+ * @extends {ol.interaction.Interaction}
+ * @param {olx.interaction.KeyboardPanOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.KeyboardPan = function(opt_options) {
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: ol.interaction.KeyboardPan.handleEvent
+ });
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Browser event.
+ * @return {boolean} Combined condition result.
+ */
+ this.defaultCondition_ = function(mapBrowserEvent) {
+ return ol.events.condition.noModifierKeys(mapBrowserEvent) &&
+ ol.events.condition.targetNotEditable(mapBrowserEvent);
+ };
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition !== undefined ?
+ options.condition : this.defaultCondition_;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 100;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelDelta_ = options.pixelDelta !== undefined ?
+ options.pixelDelta : 128;
+
+};
+ol.inherits(ol.interaction.KeyboardPan, ol.interaction.Interaction);
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} if it was a
+ * `KeyEvent`, and decides the direction to pan to (if an arrow key was
+ * pressed).
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.KeyboardPan}
+ * @api
+ */
+ol.interaction.KeyboardPan.handleEvent = function(mapBrowserEvent) {
+ var stopEvent = false;
+ if (mapBrowserEvent.type == ol.events.EventType.KEYDOWN) {
+ var keyEvent = mapBrowserEvent.originalEvent;
+ var keyCode = keyEvent.keyCode;
+ if (this.condition_(mapBrowserEvent) &&
+ (keyCode == ol.events.KeyCode.DOWN ||
+ keyCode == ol.events.KeyCode.LEFT ||
+ keyCode == ol.events.KeyCode.RIGHT ||
+ keyCode == ol.events.KeyCode.UP)) {
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ var mapUnitsDelta = view.getResolution() * this.pixelDelta_;
+ var deltaX = 0, deltaY = 0;
+ if (keyCode == ol.events.KeyCode.DOWN) {
+ deltaY = -mapUnitsDelta;
+ } else if (keyCode == ol.events.KeyCode.LEFT) {
+ deltaX = -mapUnitsDelta;
+ } else if (keyCode == ol.events.KeyCode.RIGHT) {
+ deltaX = mapUnitsDelta;
+ } else {
+ deltaY = mapUnitsDelta;
+ }
+ var delta = [deltaX, deltaY];
+ ol.coordinate.rotate(delta, view.getRotation());
+ ol.interaction.Interaction.pan(map, view, delta, this.duration_);
+ mapBrowserEvent.preventDefault();
+ stopEvent = true;
+ }
+ }
+ return !stopEvent;
+};
+
+goog.provide('ol.interaction.KeyboardZoom');
+
+goog.require('ol');
+goog.require('ol.events.EventType');
+goog.require('ol.events.condition');
+goog.require('ol.interaction.Interaction');
+
+
+/**
+ * @classdesc
+ * Allows the user to zoom the map using keyboard + and -.
+ * Note that, although this interaction is by default included in maps,
+ * the keys can only be used when browser focus is on the element to which
+ * the keyboard events are attached. By default, this is the map div,
+ * though you can change this with the `keyboardEventTarget` in
+ * {@link ol.Map}. `document` never loses focus but, for any other element,
+ * focus will have to be on, and returned to, this element if the keys are to
+ * function.
+ * See also {@link ol.interaction.KeyboardPan}.
+ *
+ * @constructor
+ * @param {olx.interaction.KeyboardZoomOptions=} opt_options Options.
+ * @extends {ol.interaction.Interaction}
+ * @api stable
+ */
+ol.interaction.KeyboardZoom = function(opt_options) {
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: ol.interaction.KeyboardZoom.handleEvent
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ? options.condition :
+ ol.events.condition.targetNotEditable;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.delta_ = options.delta ? options.delta : 1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 100;
+
+};
+ol.inherits(ol.interaction.KeyboardZoom, ol.interaction.Interaction);
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} if it was a
+ * `KeyEvent`, and decides whether to zoom in or out (depending on whether the
+ * key pressed was '+' or '-').
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.KeyboardZoom}
+ * @api
+ */
+ol.interaction.KeyboardZoom.handleEvent = function(mapBrowserEvent) {
+ var stopEvent = false;
+ if (mapBrowserEvent.type == ol.events.EventType.KEYDOWN ||
+ mapBrowserEvent.type == ol.events.EventType.KEYPRESS) {
+ var keyEvent = mapBrowserEvent.originalEvent;
+ var charCode = keyEvent.charCode;
+ if (this.condition_(mapBrowserEvent) &&
+ (charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0))) {
+ var map = mapBrowserEvent.map;
+ var delta = (charCode == '+'.charCodeAt(0)) ? this.delta_ : -this.delta_;
+ var view = map.getView();
+ ol.interaction.Interaction.zoomByDelta(
+ map, view, delta, undefined, this.duration_);
+ mapBrowserEvent.preventDefault();
+ stopEvent = true;
+ }
+ }
+ return !stopEvent;
+};
+
+goog.provide('ol.interaction.MouseWheelZoom');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.easing');
+goog.require('ol.events.EventType');
+goog.require('ol.has');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.math');
+
+
+/**
+ * @classdesc
+ * Allows the user to zoom the map by scrolling the mouse wheel.
+ *
+ * @constructor
+ * @extends {ol.interaction.Interaction}
+ * @param {olx.interaction.MouseWheelZoomOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.MouseWheelZoom = function(opt_options) {
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: ol.interaction.MouseWheelZoom.handleEvent
+ });
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.delta_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 250;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.timeout_ = options.timeout !== undefined ? options.timeout : 80;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true;
+
+ /**
+ * @private
+ * @type {?ol.Coordinate}
+ */
+ this.lastAnchor_ = null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.startTime_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.timeoutId_ = undefined;
+
+ /**
+ * @private
+ * @type {ol.interaction.MouseWheelZoom.Mode|undefined}
+ */
+ this.mode_ = undefined;
+
+ /**
+ * Trackpad events separated by this delay will be considered separate
+ * interactions.
+ * @type {number}
+ */
+ this.trackpadEventGap_ = 400;
+
+ /**
+ * @type {number|undefined}
+ */
+ this.trackpadTimeoutId_ = undefined;
+
+ /**
+ * The number of delta values per zoom level
+ * @private
+ * @type {number}
+ */
+ this.trackpadDeltaPerZoom_ = 300;
+
+ /**
+ * The zoom factor by which scroll zooming is allowed to exceed the limits.
+ * @private
+ * @type {number}
+ */
+ this.trackpadZoomBuffer_ = 1.5;
+
+};
+ol.inherits(ol.interaction.MouseWheelZoom, ol.interaction.Interaction);
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} (if it was a
+ * mousewheel-event) and eventually zooms the map.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} Allow event propagation.
+ * @this {ol.interaction.MouseWheelZoom}
+ * @api
+ */
+ol.interaction.MouseWheelZoom.handleEvent = function(mapBrowserEvent) {
+ var type = mapBrowserEvent.type;
+ if (type !== ol.events.EventType.WHEEL && type !== ol.events.EventType.MOUSEWHEEL) {
+ return true;
+ }
+
+ mapBrowserEvent.preventDefault();
+
+ var map = mapBrowserEvent.map;
+ var wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent);
+
+ if (this.useAnchor_) {
+ this.lastAnchor_ = mapBrowserEvent.coordinate;
+ }
+
+ // Delta normalisation inspired by
+ // https://github.com/mapbox/mapbox-gl-js/blob/001c7b9/js/ui/handler/scroll_zoom.js
+ var delta;
+ if (mapBrowserEvent.type == ol.events.EventType.WHEEL) {
+ delta = wheelEvent.deltaY;
+ if (ol.has.FIREFOX &&
+ wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
+ delta /= ol.has.DEVICE_PIXEL_RATIO;
+ }
+ if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
+ delta *= 40;
+ }
+ } else if (mapBrowserEvent.type == ol.events.EventType.MOUSEWHEEL) {
+ delta = -wheelEvent.wheelDeltaY;
+ if (ol.has.SAFARI) {
+ delta /= 3;
+ }
+ }
+
+ if (delta === 0) {
+ return false;
+ }
+
+ var now = Date.now();
+
+ if (this.startTime_ === undefined) {
+ this.startTime_ = now;
+ }
+
+ if (!this.mode_ || now - this.startTime_ > this.trackpadEventGap_) {
+ this.mode_ = Math.abs(delta) < 4 ?
+ ol.interaction.MouseWheelZoom.Mode.TRACKPAD :
+ ol.interaction.MouseWheelZoom.Mode.WHEEL;
+ }
+
+ if (this.mode_ === ol.interaction.MouseWheelZoom.Mode.TRACKPAD) {
+ var view = map.getView();
+ if (this.trackpadTimeoutId_) {
+ clearTimeout(this.trackpadTimeoutId_);
+ } else {
+ view.setHint(ol.View.Hint.INTERACTING, 1);
+ }
+ this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_);
+ var resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_);
+ var minResolution = view.getMinResolution();
+ var maxResolution = view.getMaxResolution();
+ var rebound = 0;
+ if (resolution < minResolution) {
+ resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_);
+ rebound = 1;
+ } else if (resolution > maxResolution) {
+ resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_);
+ rebound = -1;
+ }
+ if (this.lastAnchor_) {
+ var center = view.calculateCenterZoom(resolution, this.lastAnchor_);
+ view.setCenter(center);
+ }
+ view.setResolution(resolution);
+ if (rebound > 0) {
+ view.animate({
+ resolution: minResolution,
+ easing: ol.easing.easeOut,
+ anchor: this.lastAnchor_,
+ duration: 500
+ });
+ } else if (rebound < 0) {
+ view.animate({
+ resolution: maxResolution,
+ easing: ol.easing.easeOut,
+ anchor: this.lastAnchor_,
+ duration: 500
+ });
+ }
+ this.startTime_ = now;
+ return false;
+ }
+
+ this.delta_ += delta;
+
+ var timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
+
+ clearTimeout(this.timeoutId_);
+ this.timeoutId_ = setTimeout(this.handleWheelZoom_.bind(this, map), timeLeft);
+
+ return false;
+};
+
+
+/**
+ * @private
+ */
+ol.interaction.MouseWheelZoom.prototype.decrementInteractingHint_ = function() {
+ this.trackpadTimeoutId_ = undefined;
+ var view = this.getMap().getView();
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+};
+
+
+/**
+ * @private
+ * @param {ol.Map} map Map.
+ */
+ol.interaction.MouseWheelZoom.prototype.handleWheelZoom_ = function(map) {
+ var view = map.getView();
+ if (view.getAnimating()) {
+ view.cancelAnimations();
+ }
+ var maxDelta = ol.MOUSEWHEELZOOM_MAXDELTA;
+ var delta = ol.math.clamp(this.delta_, -maxDelta, maxDelta);
+ ol.interaction.Interaction.zoomByDelta(map, view, -delta, this.lastAnchor_,
+ this.duration_);
+ this.mode_ = undefined;
+ this.delta_ = 0;
+ this.lastAnchor_ = null;
+ this.startTime_ = undefined;
+ this.timeoutId_ = undefined;
+};
+
+
+/**
+ * Enable or disable using the mouse's location as an anchor when zooming
+ * @param {boolean} useAnchor true to zoom to the mouse's location, false
+ * to zoom to the center of the map
+ * @api
+ */
+ol.interaction.MouseWheelZoom.prototype.setMouseAnchor = function(useAnchor) {
+ this.useAnchor_ = useAnchor;
+ if (!useAnchor) {
+ this.lastAnchor_ = null;
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.MouseWheelZoom.Mode = {
+ TRACKPAD: 'trackpad',
+ WHEEL: 'wheel'
+};
+
+goog.provide('ol.interaction.PinchRotate');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.functions');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.interaction.Pointer');
+
+
+/**
+ * @classdesc
+ * Allows the user to rotate the map by twisting with two fingers
+ * on a touch screen.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.PinchRotateOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.PinchRotate = function(opt_options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.PinchRotate.handleDownEvent_,
+ handleDragEvent: ol.interaction.PinchRotate.handleDragEvent_,
+ handleUpEvent: ol.interaction.PinchRotate.handleUpEvent_
+ });
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.anchor_ = null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.lastAngle_ = undefined;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.rotating_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.rotationDelta_ = 0.0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.threshold_ = options.threshold !== undefined ? options.threshold : 0.3;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 250;
+
+};
+ol.inherits(ol.interaction.PinchRotate, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.PinchRotate}
+ * @private
+ */
+ol.interaction.PinchRotate.handleDragEvent_ = function(mapBrowserEvent) {
+ ol.DEBUG && console.assert(this.targetPointers.length >= 2,
+ 'length of this.targetPointers should be greater than or equal to 2');
+ var rotationDelta = 0.0;
+
+ var touch0 = this.targetPointers[0];
+ var touch1 = this.targetPointers[1];
+
+ // angle between touches
+ var angle = Math.atan2(
+ touch1.clientY - touch0.clientY,
+ touch1.clientX - touch0.clientX);
+
+ if (this.lastAngle_ !== undefined) {
+ var delta = angle - this.lastAngle_;
+ this.rotationDelta_ += delta;
+ if (!this.rotating_ &&
+ Math.abs(this.rotationDelta_) > this.threshold_) {
+ this.rotating_ = true;
+ }
+ rotationDelta = delta;
+ }
+ this.lastAngle_ = angle;
+
+ var map = mapBrowserEvent.map;
+
+ // rotate anchor point.
+ // FIXME: should be the intersection point between the lines:
+ // touch0,touch1 and previousTouch0,previousTouch1
+ var viewportPosition = map.getViewport().getBoundingClientRect();
+ var centroid = ol.interaction.Pointer.centroid(this.targetPointers);
+ centroid[0] -= viewportPosition.left;
+ centroid[1] -= viewportPosition.top;
+ this.anchor_ = map.getCoordinateFromPixel(centroid);
+
+ // rotate
+ if (this.rotating_) {
+ var view = map.getView();
+ var rotation = view.getRotation();
+ map.render();
+ ol.interaction.Interaction.rotateWithoutConstraints(map, view,
+ rotation + rotationDelta, this.anchor_);
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.PinchRotate}
+ * @private
+ */
+ol.interaction.PinchRotate.handleUpEvent_ = function(mapBrowserEvent) {
+ if (this.targetPointers.length < 2) {
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+ if (this.rotating_) {
+ var rotation = view.getRotation();
+ ol.interaction.Interaction.rotate(
+ map, view, rotation, this.anchor_, this.duration_);
+ }
+ return false;
+ } else {
+ return true;
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.PinchRotate}
+ * @private
+ */
+ol.interaction.PinchRotate.handleDownEvent_ = function(mapBrowserEvent) {
+ if (this.targetPointers.length >= 2) {
+ var map = mapBrowserEvent.map;
+ this.anchor_ = null;
+ this.lastAngle_ = undefined;
+ this.rotating_ = false;
+ this.rotationDelta_ = 0.0;
+ if (!this.handlingDownUpSequence) {
+ map.getView().setHint(ol.View.Hint.INTERACTING, 1);
+ }
+ return true;
+ } else {
+ return false;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.PinchRotate.prototype.shouldStopEvent = ol.functions.FALSE;
+
+goog.provide('ol.interaction.PinchZoom');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.functions');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.interaction.Pointer');
+
+
+/**
+ * @classdesc
+ * Allows the user to zoom the map by pinching with two fingers
+ * on a touch screen.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.PinchZoomOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.PinchZoom = function(opt_options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.PinchZoom.handleDownEvent_,
+ handleDragEvent: ol.interaction.PinchZoom.handleDragEvent_,
+ handleUpEvent: ol.interaction.PinchZoom.handleUpEvent_
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.constrainResolution_ = options.constrainResolution || false;
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.anchor_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 400;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.lastDistance_ = undefined;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.lastScaleDelta_ = 1;
+
+};
+ol.inherits(ol.interaction.PinchZoom, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.PinchZoom}
+ * @private
+ */
+ol.interaction.PinchZoom.handleDragEvent_ = function(mapBrowserEvent) {
+ ol.DEBUG && console.assert(this.targetPointers.length >= 2,
+ 'length of this.targetPointers should be 2 or more');
+ var scaleDelta = 1.0;
+
+ var touch0 = this.targetPointers[0];
+ var touch1 = this.targetPointers[1];
+ var dx = touch0.clientX - touch1.clientX;
+ var dy = touch0.clientY - touch1.clientY;
+
+ // distance between touches
+ var distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (this.lastDistance_ !== undefined) {
+ scaleDelta = this.lastDistance_ / distance;
+ }
+ this.lastDistance_ = distance;
+ if (scaleDelta != 1.0) {
+ this.lastScaleDelta_ = scaleDelta;
+ }
+
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ var resolution = view.getResolution();
+
+ // scale anchor point.
+ var viewportPosition = map.getViewport().getBoundingClientRect();
+ var centroid = ol.interaction.Pointer.centroid(this.targetPointers);
+ centroid[0] -= viewportPosition.left;
+ centroid[1] -= viewportPosition.top;
+ this.anchor_ = map.getCoordinateFromPixel(centroid);
+
+ // scale, bypass the resolution constraint
+ map.render();
+ ol.interaction.Interaction.zoomWithoutConstraints(
+ map, view, resolution * scaleDelta, this.anchor_);
+
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.PinchZoom}
+ * @private
+ */
+ol.interaction.PinchZoom.handleUpEvent_ = function(mapBrowserEvent) {
+ if (this.targetPointers.length < 2) {
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+ if (this.constrainResolution_) {
+ var resolution = view.getResolution();
+ // Zoom to final resolution, with an animation, and provide a
+ // direction not to zoom out/in if user was pinching in/out.
+ // Direction is > 0 if pinching out, and < 0 if pinching in.
+ var direction = this.lastScaleDelta_ - 1;
+ ol.interaction.Interaction.zoom(map, view, resolution,
+ this.anchor_, this.duration_, direction);
+ }
+ return false;
+ } else {
+ return true;
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.PinchZoom}
+ * @private
+ */
+ol.interaction.PinchZoom.handleDownEvent_ = function(mapBrowserEvent) {
+ if (this.targetPointers.length >= 2) {
+ var map = mapBrowserEvent.map;
+ this.anchor_ = null;
+ this.lastDistance_ = undefined;
+ this.lastScaleDelta_ = 1;
+ if (!this.handlingDownUpSequence) {
+ map.getView().setHint(ol.View.Hint.INTERACTING, 1);
+ }
+ return true;
+ } else {
+ return false;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.PinchZoom.prototype.shouldStopEvent = ol.functions.FALSE;
+
+goog.provide('ol.interaction');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.Kinetic');
+goog.require('ol.interaction.DoubleClickZoom');
+goog.require('ol.interaction.DragPan');
+goog.require('ol.interaction.DragRotate');
+goog.require('ol.interaction.DragZoom');
+goog.require('ol.interaction.KeyboardPan');
+goog.require('ol.interaction.KeyboardZoom');
+goog.require('ol.interaction.MouseWheelZoom');
+goog.require('ol.interaction.PinchRotate');
+goog.require('ol.interaction.PinchZoom');
+
+
+/**
+ * Set of interactions included in maps by default. Specific interactions can be
+ * excluded by setting the appropriate option to false in the constructor
+ * options, but the order of the interactions is fixed. If you want to specify
+ * a different order for interactions, you will need to create your own
+ * {@link ol.interaction.Interaction} instances and insert them into a
+ * {@link ol.Collection} in the order you want before creating your
+ * {@link ol.Map} instance. The default set of interactions, in sequence, is:
+ * * {@link ol.interaction.DragRotate}
+ * * {@link ol.interaction.DoubleClickZoom}
+ * * {@link ol.interaction.DragPan}
+ * * {@link ol.interaction.PinchRotate}
+ * * {@link ol.interaction.PinchZoom}
+ * * {@link ol.interaction.KeyboardPan}
+ * * {@link ol.interaction.KeyboardZoom}
+ * * {@link ol.interaction.MouseWheelZoom}
+ * * {@link ol.interaction.DragZoom}
+ *
+ * @param {olx.interaction.DefaultsOptions=} opt_options Defaults options.
+ * @return {ol.Collection.<ol.interaction.Interaction>} A collection of
+ * interactions to be used with the ol.Map constructor's interactions option.
+ * @api stable
+ */
+ol.interaction.defaults = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var interactions = new ol.Collection();
+
+ var kinetic = new ol.Kinetic(-0.005, 0.05, 100);
+
+ var altShiftDragRotate = options.altShiftDragRotate !== undefined ?
+ options.altShiftDragRotate : true;
+ if (altShiftDragRotate) {
+ interactions.push(new ol.interaction.DragRotate());
+ }
+
+ var doubleClickZoom = options.doubleClickZoom !== undefined ?
+ options.doubleClickZoom : true;
+ if (doubleClickZoom) {
+ interactions.push(new ol.interaction.DoubleClickZoom({
+ delta: options.zoomDelta,
+ duration: options.zoomDuration
+ }));
+ }
+
+ var dragPan = options.dragPan !== undefined ? options.dragPan : true;
+ if (dragPan) {
+ interactions.push(new ol.interaction.DragPan({
+ kinetic: kinetic
+ }));
+ }
+
+ var pinchRotate = options.pinchRotate !== undefined ? options.pinchRotate :
+ true;
+ if (pinchRotate) {
+ interactions.push(new ol.interaction.PinchRotate());
+ }
+
+ var pinchZoom = options.pinchZoom !== undefined ? options.pinchZoom : true;
+ if (pinchZoom) {
+ interactions.push(new ol.interaction.PinchZoom({
+ duration: options.zoomDuration
+ }));
+ }
+
+ var keyboard = options.keyboard !== undefined ? options.keyboard : true;
+ if (keyboard) {
+ interactions.push(new ol.interaction.KeyboardPan());
+ interactions.push(new ol.interaction.KeyboardZoom({
+ delta: options.zoomDelta,
+ duration: options.zoomDuration
+ }));
+ }
+
+ var mouseWheelZoom = options.mouseWheelZoom !== undefined ?
+ options.mouseWheelZoom : true;
+ if (mouseWheelZoom) {
+ interactions.push(new ol.interaction.MouseWheelZoom({
+ duration: options.zoomDuration
+ }));
+ }
+
+ var shiftDragZoom = options.shiftDragZoom !== undefined ?
+ options.shiftDragZoom : true;
+ if (shiftDragZoom) {
+ interactions.push(new ol.interaction.DragZoom({
+ duration: options.zoomDuration
+ }));
+ }
+
+ return interactions;
+
+};
+
+goog.provide('ol.layer.Base');
+
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.math');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Note that with `ol.layer.Base` and all its subclasses, any property set in
+ * the options is set as a {@link ol.Object} property on the layer object, so
+ * is observable, and has get/set accessors.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {olx.layer.BaseOptions} options Layer options.
+ * @api stable
+ */
+ol.layer.Base = function(options) {
+
+ ol.Object.call(this);
+
+ /**
+ * @type {Object.<string, *>}
+ */
+ var properties = ol.obj.assign({}, options);
+ properties[ol.layer.Base.Property.OPACITY] =
+ options.opacity !== undefined ? options.opacity : 1;
+ properties[ol.layer.Base.Property.VISIBLE] =
+ options.visible !== undefined ? options.visible : true;
+ properties[ol.layer.Base.Property.Z_INDEX] =
+ options.zIndex !== undefined ? options.zIndex : 0;
+ properties[ol.layer.Base.Property.MAX_RESOLUTION] =
+ options.maxResolution !== undefined ? options.maxResolution : Infinity;
+ properties[ol.layer.Base.Property.MIN_RESOLUTION] =
+ options.minResolution !== undefined ? options.minResolution : 0;
+
+ this.setProperties(properties);
+
+ /**
+ * @type {ol.LayerState}
+ * @private
+ */
+ this.state_ = /** @type {ol.LayerState} */ ({
+ layer: /** @type {ol.layer.Layer} */ (this),
+ managed: true
+ });
+
+};
+ol.inherits(ol.layer.Base, ol.Object);
+
+
+/**
+ * @return {ol.LayerState} Layer state.
+ */
+ol.layer.Base.prototype.getLayerState = function() {
+ this.state_.opacity = ol.math.clamp(this.getOpacity(), 0, 1);
+ this.state_.sourceState = this.getSourceState();
+ this.state_.visible = this.getVisible();
+ this.state_.extent = this.getExtent();
+ this.state_.zIndex = this.getZIndex();
+ this.state_.maxResolution = this.getMaxResolution();
+ this.state_.minResolution = Math.max(this.getMinResolution(), 0);
+
+ return this.state_;
+};
+
+
+/**
+ * @abstract
+ * @param {Array.<ol.layer.Layer>=} opt_array Array of layers (to be
+ * modified in place).
+ * @return {Array.<ol.layer.Layer>} Array of layers.
+ */
+ol.layer.Base.prototype.getLayersArray = function(opt_array) {};
+
+
+/**
+ * @abstract
+ * @param {Array.<ol.LayerState>=} opt_states Optional list of layer
+ * states (to be modified in place).
+ * @return {Array.<ol.LayerState>} List of layer states.
+ */
+ol.layer.Base.prototype.getLayerStatesArray = function(opt_states) {};
+
+
+/**
+ * Return the {@link ol.Extent extent} of the layer or `undefined` if it
+ * will be visible regardless of extent.
+ * @return {ol.Extent|undefined} The layer extent.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.getExtent = function() {
+ return /** @type {ol.Extent|undefined} */ (
+ this.get(ol.layer.Base.Property.EXTENT));
+};
+
+
+/**
+ * Return the maximum resolution of the layer.
+ * @return {number} The maximum resolution of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.getMaxResolution = function() {
+ return /** @type {number} */ (
+ this.get(ol.layer.Base.Property.MAX_RESOLUTION));
+};
+
+
+/**
+ * Return the minimum resolution of the layer.
+ * @return {number} The minimum resolution of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.getMinResolution = function() {
+ return /** @type {number} */ (
+ this.get(ol.layer.Base.Property.MIN_RESOLUTION));
+};
+
+
+/**
+ * Return the opacity of the layer (between 0 and 1).
+ * @return {number} The opacity of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.getOpacity = function() {
+ return /** @type {number} */ (this.get(ol.layer.Base.Property.OPACITY));
+};
+
+
+/**
+ * @abstract
+ * @return {ol.source.State} Source state.
+ */
+ol.layer.Base.prototype.getSourceState = function() {};
+
+
+/**
+ * Return the visibility of the layer (`true` or `false`).
+ * @return {boolean} The visibility of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.getVisible = function() {
+ return /** @type {boolean} */ (this.get(ol.layer.Base.Property.VISIBLE));
+};
+
+
+/**
+ * Return the Z-index of the layer, which is used to order layers before
+ * rendering. The default Z-index is 0.
+ * @return {number} The Z-index of the layer.
+ * @observable
+ * @api
+ */
+ol.layer.Base.prototype.getZIndex = function() {
+ return /** @type {number} */ (this.get(ol.layer.Base.Property.Z_INDEX));
+};
+
+
+/**
+ * Set the extent at which the layer is visible. If `undefined`, the layer
+ * will be visible at all extents.
+ * @param {ol.Extent|undefined} extent The extent of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.setExtent = function(extent) {
+ this.set(ol.layer.Base.Property.EXTENT, extent);
+};
+
+
+/**
+ * Set the maximum resolution at which the layer is visible.
+ * @param {number} maxResolution The maximum resolution of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.setMaxResolution = function(maxResolution) {
+ this.set(ol.layer.Base.Property.MAX_RESOLUTION, maxResolution);
+};
+
+
+/**
+ * Set the minimum resolution at which the layer is visible.
+ * @param {number} minResolution The minimum resolution of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.setMinResolution = function(minResolution) {
+ this.set(ol.layer.Base.Property.MIN_RESOLUTION, minResolution);
+};
+
+
+/**
+ * Set the opacity of the layer, allowed values range from 0 to 1.
+ * @param {number} opacity The opacity of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.setOpacity = function(opacity) {
+ this.set(ol.layer.Base.Property.OPACITY, opacity);
+};
+
+
+/**
+ * Set the visibility of the layer (`true` or `false`).
+ * @param {boolean} visible The visibility of the layer.
+ * @observable
+ * @api stable
+ */
+ol.layer.Base.prototype.setVisible = function(visible) {
+ this.set(ol.layer.Base.Property.VISIBLE, visible);
+};
+
+
+/**
+ * Set Z-index of the layer, which is used to order layers before rendering.
+ * The default Z-index is 0.
+ * @param {number} zindex The z-index of the layer.
+ * @observable
+ * @api
+ */
+ol.layer.Base.prototype.setZIndex = function(zindex) {
+ this.set(ol.layer.Base.Property.Z_INDEX, zindex);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.layer.Base.Property = {
+ OPACITY: 'opacity',
+ VISIBLE: 'visible',
+ EXTENT: 'extent',
+ Z_INDEX: 'zIndex',
+ MAX_RESOLUTION: 'maxResolution',
+ MIN_RESOLUTION: 'minResolution',
+ SOURCE: 'source'
+};
+
+goog.provide('ol.source.State');
+
+
+/**
+ * State of the source, one of 'undefined', 'loading', 'ready' or 'error'.
+ * @enum {string}
+ */
+ol.source.State = {
+ UNDEFINED: 'undefined',
+ LOADING: 'loading',
+ READY: 'ready',
+ ERROR: 'error'
+};
+
+
+goog.provide('ol.layer.Group');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.Collection');
+goog.require('ol.Object');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.layer.Base');
+goog.require('ol.obj');
+goog.require('ol.source.State');
+
+
+/**
+ * @classdesc
+ * A {@link ol.Collection} of layers that are handled together.
+ *
+ * A generic `change` event is triggered when the group/Collection changes.
+ *
+ * @constructor
+ * @extends {ol.layer.Base}
+ * @param {olx.layer.GroupOptions=} opt_options Layer options.
+ * @api stable
+ */
+ol.layer.Group = function(opt_options) {
+
+ var options = opt_options || {};
+ var baseOptions = /** @type {olx.layer.GroupOptions} */
+ (ol.obj.assign({}, options));
+ delete baseOptions.layers;
+
+ var layers = options.layers;
+
+ ol.layer.Base.call(this, baseOptions);
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.layersListenerKeys_ = [];
+
+ /**
+ * @private
+ * @type {Object.<string, Array.<ol.EventsKey>>}
+ */
+ this.listenerKeys_ = {};
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.layer.Group.Property.LAYERS),
+ this.handleLayersChanged_, this);
+
+ if (layers) {
+ if (Array.isArray(layers)) {
+ layers = new ol.Collection(layers.slice());
+ } else {
+ ol.asserts.assert(layers instanceof ol.Collection,
+ 43); // Expected `layers` to be an array or an `ol.Collection`
+ layers = layers;
+ }
+ } else {
+ layers = new ol.Collection();
+ }
+
+ this.setLayers(layers);
+
+};
+ol.inherits(ol.layer.Group, ol.layer.Base);
+
+
+/**
+ * @private
+ */
+ol.layer.Group.prototype.handleLayerChange_ = function() {
+ if (this.getVisible()) {
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.events.Event} event Event.
+ * @private
+ */
+ol.layer.Group.prototype.handleLayersChanged_ = function(event) {
+ this.layersListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.layersListenerKeys_.length = 0;
+
+ var layers = this.getLayers();
+ this.layersListenerKeys_.push(
+ ol.events.listen(layers, ol.Collection.EventType.ADD,
+ this.handleLayersAdd_, this),
+ ol.events.listen(layers, ol.Collection.EventType.REMOVE,
+ this.handleLayersRemove_, this));
+
+ for (var id in this.listenerKeys_) {
+ this.listenerKeys_[id].forEach(ol.events.unlistenByKey);
+ }
+ ol.obj.clear(this.listenerKeys_);
+
+ var layersArray = layers.getArray();
+ var i, ii, layer;
+ for (i = 0, ii = layersArray.length; i < ii; i++) {
+ layer = layersArray[i];
+ this.listenerKeys_[ol.getUid(layer).toString()] = [
+ ol.events.listen(layer, ol.Object.EventType.PROPERTYCHANGE,
+ this.handleLayerChange_, this),
+ ol.events.listen(layer, ol.events.EventType.CHANGE,
+ this.handleLayerChange_, this)
+ ];
+ }
+
+ this.changed();
+};
+
+
+/**
+ * @param {ol.Collection.Event} collectionEvent Collection event.
+ * @private
+ */
+ol.layer.Group.prototype.handleLayersAdd_ = function(collectionEvent) {
+ var layer = /** @type {ol.layer.Base} */ (collectionEvent.element);
+ var key = ol.getUid(layer).toString();
+ ol.DEBUG && console.assert(!(key in this.listenerKeys_),
+ 'listeners already registered');
+ this.listenerKeys_[key] = [
+ ol.events.listen(layer, ol.Object.EventType.PROPERTYCHANGE,
+ this.handleLayerChange_, this),
+ ol.events.listen(layer, ol.events.EventType.CHANGE,
+ this.handleLayerChange_, this)
+ ];
+ this.changed();
+};
+
+
+/**
+ * @param {ol.Collection.Event} collectionEvent Collection event.
+ * @private
+ */
+ol.layer.Group.prototype.handleLayersRemove_ = function(collectionEvent) {
+ var layer = /** @type {ol.layer.Base} */ (collectionEvent.element);
+ var key = ol.getUid(layer).toString();
+ ol.DEBUG && console.assert(key in this.listenerKeys_, 'no listeners to unregister');
+ this.listenerKeys_[key].forEach(ol.events.unlistenByKey);
+ delete this.listenerKeys_[key];
+ this.changed();
+};
+
+
+/**
+ * Returns the {@link ol.Collection collection} of {@link ol.layer.Layer layers}
+ * in this group.
+ * @return {!ol.Collection.<ol.layer.Base>} Collection of
+ * {@link ol.layer.Base layers} that are part of this group.
+ * @observable
+ * @api stable
+ */
+ol.layer.Group.prototype.getLayers = function() {
+ return /** @type {!ol.Collection.<ol.layer.Base>} */ (this.get(
+ ol.layer.Group.Property.LAYERS));
+};
+
+
+/**
+ * Set the {@link ol.Collection collection} of {@link ol.layer.Layer layers}
+ * in this group.
+ * @param {!ol.Collection.<ol.layer.Base>} layers Collection of
+ * {@link ol.layer.Base layers} that are part of this group.
+ * @observable
+ * @api stable
+ */
+ol.layer.Group.prototype.setLayers = function(layers) {
+ this.set(ol.layer.Group.Property.LAYERS, layers);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.layer.Group.prototype.getLayersArray = function(opt_array) {
+ var array = opt_array !== undefined ? opt_array : [];
+ this.getLayers().forEach(function(layer) {
+ layer.getLayersArray(array);
+ });
+ return array;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.layer.Group.prototype.getLayerStatesArray = function(opt_states) {
+ var states = opt_states !== undefined ? opt_states : [];
+
+ var pos = states.length;
+
+ this.getLayers().forEach(function(layer) {
+ layer.getLayerStatesArray(states);
+ });
+
+ var ownLayerState = this.getLayerState();
+ var i, ii, layerState;
+ for (i = pos, ii = states.length; i < ii; i++) {
+ layerState = states[i];
+ layerState.opacity *= ownLayerState.opacity;
+ layerState.visible = layerState.visible && ownLayerState.visible;
+ layerState.maxResolution = Math.min(
+ layerState.maxResolution, ownLayerState.maxResolution);
+ layerState.minResolution = Math.max(
+ layerState.minResolution, ownLayerState.minResolution);
+ if (ownLayerState.extent !== undefined) {
+ if (layerState.extent !== undefined) {
+ layerState.extent = ol.extent.getIntersection(
+ layerState.extent, ownLayerState.extent);
+ } else {
+ layerState.extent = ownLayerState.extent;
+ }
+ }
+ }
+
+ return states;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.layer.Group.prototype.getSourceState = function() {
+ return ol.source.State.READY;
+};
+
+/**
+ * @enum {string}
+ */
+ol.layer.Group.Property = {
+ LAYERS: 'layers'
+};
+
+goog.provide('ol.proj.EPSG3857');
+
+goog.require('ol');
+goog.require('ol.math');
+goog.require('ol.proj');
+goog.require('ol.proj.Projection');
+goog.require('ol.proj.Units');
+
+
+/**
+ * @classdesc
+ * Projection object for web/spherical Mercator (EPSG:3857).
+ *
+ * @constructor
+ * @extends {ol.proj.Projection}
+ * @param {string} code Code.
+ * @private
+ */
+ol.proj.EPSG3857_ = function(code) {
+ ol.proj.Projection.call(this, {
+ code: code,
+ units: ol.proj.Units.METERS,
+ extent: ol.proj.EPSG3857.EXTENT,
+ global: true,
+ worldExtent: ol.proj.EPSG3857.WORLD_EXTENT,
+ getPointResolution: function(resolution, point) {
+ return resolution / ol.math.cosh(point[1] / ol.proj.EPSG3857.RADIUS);
+ }
+ });
+};
+ol.inherits(ol.proj.EPSG3857_, ol.proj.Projection);
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.proj.EPSG3857.RADIUS = 6378137;
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.proj.EPSG3857.HALF_SIZE = Math.PI * ol.proj.EPSG3857.RADIUS;
+
+
+/**
+ * @const
+ * @type {ol.Extent}
+ */
+ol.proj.EPSG3857.EXTENT = [
+ -ol.proj.EPSG3857.HALF_SIZE, -ol.proj.EPSG3857.HALF_SIZE,
+ ol.proj.EPSG3857.HALF_SIZE, ol.proj.EPSG3857.HALF_SIZE
+];
+
+
+/**
+ * @const
+ * @type {ol.Extent}
+ */
+ol.proj.EPSG3857.WORLD_EXTENT = [-180, -85, 180, 85];
+
+
+/**
+ * Lists several projection codes with the same meaning as EPSG:3857.
+ *
+ * @type {Array.<string>}
+ */
+ol.proj.EPSG3857.CODES = [
+ 'EPSG:3857',
+ 'EPSG:102100',
+ 'EPSG:102113',
+ 'EPSG:900913',
+ 'urn:ogc:def:crs:EPSG:6.18:3:3857',
+ 'urn:ogc:def:crs:EPSG::3857',
+ 'http://www.opengis.net/gml/srs/epsg.xml#3857'
+];
+
+
+/**
+ * Projections equal to EPSG:3857.
+ *
+ * @const
+ * @type {Array.<ol.proj.Projection>}
+ */
+ol.proj.EPSG3857.PROJECTIONS = ol.proj.EPSG3857.CODES.map(function(code) {
+ return new ol.proj.EPSG3857_(code);
+});
+
+
+/**
+ * Transformation from EPSG:4326 to EPSG:3857.
+ *
+ * @param {Array.<number>} input Input array of coordinate values.
+ * @param {Array.<number>=} opt_output Output array of coordinate values.
+ * @param {number=} opt_dimension Dimension (default is `2`).
+ * @return {Array.<number>} Output array of coordinate values.
+ */
+ol.proj.EPSG3857.fromEPSG4326 = function(input, opt_output, opt_dimension) {
+ var length = input.length,
+ dimension = opt_dimension > 1 ? opt_dimension : 2,
+ output = opt_output;
+ if (output === undefined) {
+ if (dimension > 2) {
+ // preserve values beyond second dimension
+ output = input.slice();
+ } else {
+ output = new Array(length);
+ }
+ }
+ ol.DEBUG && console.assert(output.length % dimension === 0,
+ 'modulus of output.length with dimension should be 0');
+ var halfSize = ol.proj.EPSG3857.HALF_SIZE;
+ for (var i = 0; i < length; i += dimension) {
+ output[i] = halfSize * input[i] / 180;
+ var y = ol.proj.EPSG3857.RADIUS *
+ Math.log(Math.tan(Math.PI * (input[i + 1] + 90) / 360));
+ if (y > halfSize) {
+ y = halfSize;
+ } else if (y < -halfSize) {
+ y = -halfSize;
+ }
+ output[i + 1] = y;
+ }
+ return output;
+};
+
+
+/**
+ * Transformation from EPSG:3857 to EPSG:4326.
+ *
+ * @param {Array.<number>} input Input array of coordinate values.
+ * @param {Array.<number>=} opt_output Output array of coordinate values.
+ * @param {number=} opt_dimension Dimension (default is `2`).
+ * @return {Array.<number>} Output array of coordinate values.
+ */
+ol.proj.EPSG3857.toEPSG4326 = function(input, opt_output, opt_dimension) {
+ var length = input.length,
+ dimension = opt_dimension > 1 ? opt_dimension : 2,
+ output = opt_output;
+ if (output === undefined) {
+ if (dimension > 2) {
+ // preserve values beyond second dimension
+ output = input.slice();
+ } else {
+ output = new Array(length);
+ }
+ }
+ ol.DEBUG && console.assert(output.length % dimension === 0,
+ 'modulus of output.length with dimension should be 0');
+ for (var i = 0; i < length; i += dimension) {
+ output[i] = 180 * input[i] / ol.proj.EPSG3857.HALF_SIZE;
+ output[i + 1] = 360 * Math.atan(
+ Math.exp(input[i + 1] / ol.proj.EPSG3857.RADIUS)) / Math.PI - 90;
+ }
+ return output;
+};
+
+goog.provide('ol.sphere.WGS84');
+
+goog.require('ol.Sphere');
+
+
+/**
+ * A sphere with radius equal to the semi-major axis of the WGS84 ellipsoid.
+ * @const
+ * @type {ol.Sphere}
+ */
+ol.sphere.WGS84 = new ol.Sphere(6378137);
+
+goog.provide('ol.proj.EPSG4326');
+
+goog.require('ol');
+goog.require('ol.proj');
+goog.require('ol.proj.Projection');
+goog.require('ol.proj.Units');
+goog.require('ol.sphere.WGS84');
+
+
+/**
+ * @classdesc
+ * Projection object for WGS84 geographic coordinates (EPSG:4326).
+ *
+ * Note that OpenLayers does not strictly comply with the EPSG definition.
+ * The EPSG registry defines 4326 as a CRS for Latitude,Longitude (y,x).
+ * OpenLayers treats EPSG:4326 as a pseudo-projection, with x,y coordinates.
+ *
+ * @constructor
+ * @extends {ol.proj.Projection}
+ * @param {string} code Code.
+ * @param {string=} opt_axisOrientation Axis orientation.
+ * @private
+ */
+ol.proj.EPSG4326_ = function(code, opt_axisOrientation) {
+ ol.proj.Projection.call(this, {
+ code: code,
+ units: ol.proj.Units.DEGREES,
+ extent: ol.proj.EPSG4326.EXTENT,
+ axisOrientation: opt_axisOrientation,
+ global: true,
+ metersPerUnit: ol.proj.EPSG4326.METERS_PER_UNIT,
+ worldExtent: ol.proj.EPSG4326.EXTENT
+ });
+};
+ol.inherits(ol.proj.EPSG4326_, ol.proj.Projection);
+
+
+/**
+ * Extent of the EPSG:4326 projection which is the whole world.
+ *
+ * @const
+ * @type {ol.Extent}
+ */
+ol.proj.EPSG4326.EXTENT = [-180, -90, 180, 90];
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.proj.EPSG4326.METERS_PER_UNIT = Math.PI * ol.sphere.WGS84.radius / 180;
+
+
+/**
+ * Projections equal to EPSG:4326.
+ *
+ * @const
+ * @type {Array.<ol.proj.Projection>}
+ */
+ol.proj.EPSG4326.PROJECTIONS = [
+ new ol.proj.EPSG4326_('CRS:84'),
+ new ol.proj.EPSG4326_('EPSG:4326', 'neu'),
+ new ol.proj.EPSG4326_('urn:ogc:def:crs:EPSG::4326', 'neu'),
+ new ol.proj.EPSG4326_('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'),
+ new ol.proj.EPSG4326_('urn:ogc:def:crs:OGC:1.3:CRS84'),
+ new ol.proj.EPSG4326_('urn:ogc:def:crs:OGC:2:84'),
+ new ol.proj.EPSG4326_('http://www.opengis.net/gml/srs/epsg.xml#4326', 'neu'),
+ new ol.proj.EPSG4326_('urn:x-ogc:def:crs:EPSG:4326', 'neu')
+];
+
+goog.provide('ol.proj.common');
+
+goog.require('ol.proj');
+goog.require('ol.proj.EPSG3857');
+goog.require('ol.proj.EPSG4326');
+
+
+/**
+ * FIXME empty description for jsdoc
+ * @api
+ */
+ol.proj.common.add = function() {
+ // Add transformations that don't alter coordinates to convert within set of
+ // projections with equal meaning.
+ ol.proj.addEquivalentProjections(ol.proj.EPSG3857.PROJECTIONS);
+ ol.proj.addEquivalentProjections(ol.proj.EPSG4326.PROJECTIONS);
+ // Add transformations to convert EPSG:4326 like coordinates to EPSG:3857 like
+ // coordinates and back.
+ ol.proj.addEquivalentTransforms(
+ ol.proj.EPSG4326.PROJECTIONS,
+ ol.proj.EPSG3857.PROJECTIONS,
+ ol.proj.EPSG3857.fromEPSG4326,
+ ol.proj.EPSG3857.toEPSG4326);
+};
+
+goog.provide('ol.renderer.Type');
+
+
+/**
+ * Available renderers: `'canvas'` or `'webgl'`.
+ * @enum {string}
+ */
+ol.renderer.Type = {
+ CANVAS: 'canvas',
+ WEBGL: 'webgl'
+};
+
+goog.provide('ol.render.Event');
+
+goog.require('ol');
+goog.require('ol.events.Event');
+
+
+/**
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.render.Event}
+ * @param {ol.render.Event.Type} type Type.
+ * @param {ol.render.VectorContext=} opt_vectorContext Vector context.
+ * @param {olx.FrameState=} opt_frameState Frame state.
+ * @param {?CanvasRenderingContext2D=} opt_context Context.
+ * @param {?ol.webgl.Context=} opt_glContext WebGL Context.
+ */
+ol.render.Event = function(
+ type, opt_vectorContext, opt_frameState, opt_context,
+ opt_glContext) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * For canvas, this is an instance of {@link ol.render.canvas.Immediate}.
+ * @type {ol.render.VectorContext|undefined}
+ * @api
+ */
+ this.vectorContext = opt_vectorContext;
+
+ /**
+ * An object representing the current render frame state.
+ * @type {olx.FrameState|undefined}
+ * @api
+ */
+ this.frameState = opt_frameState;
+
+ /**
+ * Canvas context. Only available when a Canvas renderer is used, null
+ * otherwise.
+ * @type {CanvasRenderingContext2D|null|undefined}
+ * @api
+ */
+ this.context = opt_context;
+
+ /**
+ * WebGL context. Only available when a WebGL renderer is used, null
+ * otherwise.
+ * @type {ol.webgl.Context|null|undefined}
+ * @api
+ */
+ this.glContext = opt_glContext;
+
+};
+ol.inherits(ol.render.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.render.Event.Type = {
+ /**
+ * @event ol.render.Event#postcompose
+ * @api
+ */
+ POSTCOMPOSE: 'postcompose',
+ /**
+ * @event ol.render.Event#precompose
+ * @api
+ */
+ PRECOMPOSE: 'precompose',
+ /**
+ * @event ol.render.Event#render
+ * @api
+ */
+ RENDER: 'render'
+};
+
+goog.provide('ol.layer.Layer');
+
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.layer.Base');
+goog.require('ol.obj');
+goog.require('ol.render.Event');
+goog.require('ol.source.State');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * A visual representation of raster or vector map data.
+ * Layers group together those properties that pertain to how the data is to be
+ * displayed, irrespective of the source of that data.
+ *
+ * Layers are usually added to a map with {@link ol.Map#addLayer}. Components
+ * like {@link ol.interaction.Select} use unmanaged layers internally. These
+ * unmanaged layers are associated with the map using
+ * {@link ol.layer.Layer#setMap} instead.
+ *
+ * A generic `change` event is fired when the state of the source changes.
+ *
+ * @constructor
+ * @extends {ol.layer.Base}
+ * @fires ol.render.Event
+ * @param {olx.layer.LayerOptions} options Layer options.
+ * @api stable
+ */
+ol.layer.Layer = function(options) {
+
+ var baseOptions = ol.obj.assign({}, options);
+ delete baseOptions.source;
+
+ ol.layer.Base.call(this, /** @type {olx.layer.BaseOptions} */ (baseOptions));
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.mapPrecomposeKey_ = null;
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.mapRenderKey_ = null;
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.sourceChangeKey_ = null;
+
+ if (options.map) {
+ this.setMap(options.map);
+ }
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.layer.Base.Property.SOURCE),
+ this.handleSourcePropertyChange_, this);
+
+ var source = options.source ? options.source : null;
+ this.setSource(source);
+};
+ol.inherits(ol.layer.Layer, ol.layer.Base);
+
+
+/**
+ * Return `true` if the layer is visible, and if the passed resolution is
+ * between the layer's minResolution and maxResolution. The comparison is
+ * inclusive for `minResolution` and exclusive for `maxResolution`.
+ * @param {ol.LayerState} layerState Layer state.
+ * @param {number} resolution Resolution.
+ * @return {boolean} The layer is visible at the given resolution.
+ */
+ol.layer.Layer.visibleAtResolution = function(layerState, resolution) {
+ return layerState.visible && resolution >= layerState.minResolution &&
+ resolution < layerState.maxResolution;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.layer.Layer.prototype.getLayersArray = function(opt_array) {
+ var array = opt_array ? opt_array : [];
+ array.push(this);
+ return array;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.layer.Layer.prototype.getLayerStatesArray = function(opt_states) {
+ var states = opt_states ? opt_states : [];
+ states.push(this.getLayerState());
+ return states;
+};
+
+
+/**
+ * Get the layer source.
+ * @return {ol.source.Source} The layer source (or `null` if not yet set).
+ * @observable
+ * @api stable
+ */
+ol.layer.Layer.prototype.getSource = function() {
+ var source = this.get(ol.layer.Base.Property.SOURCE);
+ return /** @type {ol.source.Source} */ (source) || null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.layer.Layer.prototype.getSourceState = function() {
+ var source = this.getSource();
+ return !source ? ol.source.State.UNDEFINED : source.getState();
+};
+
+
+/**
+ * @private
+ */
+ol.layer.Layer.prototype.handleSourceChange_ = function() {
+ this.changed();
+};
+
+
+/**
+ * @private
+ */
+ol.layer.Layer.prototype.handleSourcePropertyChange_ = function() {
+ if (this.sourceChangeKey_) {
+ ol.events.unlistenByKey(this.sourceChangeKey_);
+ this.sourceChangeKey_ = null;
+ }
+ var source = this.getSource();
+ if (source) {
+ this.sourceChangeKey_ = ol.events.listen(source,
+ ol.events.EventType.CHANGE, this.handleSourceChange_, this);
+ }
+ this.changed();
+};
+
+
+/**
+ * Sets the layer to be rendered on top of other layers on a map. The map will
+ * not manage this layer in its layers collection, and the callback in
+ * {@link ol.Map#forEachLayerAtPixel} will receive `null` as layer. This
+ * is useful for temporary layers. To remove an unmanaged layer from the map,
+ * use `#setMap(null)`.
+ *
+ * To add the layer to a map and have it managed by the map, use
+ * {@link ol.Map#addLayer} instead.
+ * @param {ol.Map} map Map.
+ * @api
+ */
+ol.layer.Layer.prototype.setMap = function(map) {
+ if (this.mapPrecomposeKey_) {
+ ol.events.unlistenByKey(this.mapPrecomposeKey_);
+ this.mapPrecomposeKey_ = null;
+ }
+ if (!map) {
+ this.changed();
+ }
+ if (this.mapRenderKey_) {
+ ol.events.unlistenByKey(this.mapRenderKey_);
+ this.mapRenderKey_ = null;
+ }
+ if (map) {
+ this.mapPrecomposeKey_ = ol.events.listen(
+ map, ol.render.Event.Type.PRECOMPOSE, function(evt) {
+ var layerState = this.getLayerState();
+ layerState.managed = false;
+ layerState.zIndex = Infinity;
+ evt.frameState.layerStatesArray.push(layerState);
+ evt.frameState.layerStates[ol.getUid(this)] = layerState;
+ }, this);
+ this.mapRenderKey_ = ol.events.listen(
+ this, ol.events.EventType.CHANGE, map.render, map);
+ this.changed();
+ }
+};
+
+
+/**
+ * Set the layer source.
+ * @param {ol.source.Source} source The layer source.
+ * @observable
+ * @api stable
+ */
+ol.layer.Layer.prototype.setSource = function(source) {
+ this.set(ol.layer.Base.Property.SOURCE, source);
+};
+
+goog.provide('ol.style.IconImageCache');
+
+goog.require('ol');
+goog.require('ol.color');
+
+
+/**
+ * @constructor
+ */
+ol.style.IconImageCache = function() {
+
+ /**
+ * @type {Object.<string, ol.style.IconImage>}
+ * @private
+ */
+ this.cache_ = {};
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.cacheSize_ = 0;
+
+ /**
+ * @const
+ * @type {number}
+ * @private
+ */
+ this.maxCacheSize_ = 32;
+};
+
+
+/**
+ * @param {string} src Src.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.Color} color Color.
+ * @return {string} Cache key.
+ */
+ol.style.IconImageCache.getKey = function(src, crossOrigin, color) {
+ ol.DEBUG && console.assert(crossOrigin !== undefined,
+ 'argument crossOrigin must be defined');
+ var colorString = color ? ol.color.asString(color) : 'null';
+ return crossOrigin + ':' + src + ':' + colorString;
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.style.IconImageCache.prototype.clear = function() {
+ this.cache_ = {};
+ this.cacheSize_ = 0;
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.style.IconImageCache.prototype.expire = function() {
+ if (this.cacheSize_ > this.maxCacheSize_) {
+ var i = 0;
+ var key, iconImage;
+ for (key in this.cache_) {
+ iconImage = this.cache_[key];
+ if ((i++ & 3) === 0 && !iconImage.hasListener()) {
+ delete this.cache_[key];
+ --this.cacheSize_;
+ }
+ }
+ }
+};
+
+
+/**
+ * @param {string} src Src.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.Color} color Color.
+ * @return {ol.style.IconImage} Icon image.
+ */
+ol.style.IconImageCache.prototype.get = function(src, crossOrigin, color) {
+ var key = ol.style.IconImageCache.getKey(src, crossOrigin, color);
+ return key in this.cache_ ? this.cache_[key] : null;
+};
+
+
+/**
+ * @param {string} src Src.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.Color} color Color.
+ * @param {ol.style.IconImage} iconImage Icon image.
+ */
+ol.style.IconImageCache.prototype.set = function(src, crossOrigin, color,
+ iconImage) {
+ var key = ol.style.IconImageCache.getKey(src, crossOrigin, color);
+ this.cache_[key] = iconImage;
+ ++this.cacheSize_;
+};
+
+goog.provide('ol.style');
+
+goog.require('ol.style.IconImageCache');
+
+ol.style.iconImageCache = new ol.style.IconImageCache();
+
+goog.provide('ol.transform');
+
+goog.require('ol.asserts');
+
+
+/**
+ * Collection of affine 2d transformation functions. The functions work on an
+ * array of 6 elements. The element order is compatible with the [SVGMatrix
+ * interface](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix) and is
+ * a subset (elements a to f) of a 3x3 martrix:
+ * ```
+ * [ a c e ]
+ * [ b d f ]
+ * [ 0 0 1 ]
+ * ```
+ */
+
+
+/**
+ * @private
+ * @type {ol.Transform}
+ */
+ol.transform.tmp_ = new Array(6);
+
+
+/**
+ * Create an identity transform.
+ * @return {!ol.Transform} Identity transform.
+ */
+ol.transform.create = function() {
+ return [1, 0, 0, 1, 0, 0];
+};
+
+
+/**
+ * Resets the given transform to an identity transform.
+ * @param {!ol.Transform} transform Transform.
+ * @return {!ol.Transform} Transform.
+ */
+ol.transform.reset = function(transform) {
+ return ol.transform.set(transform, 1, 0, 0, 1, 0, 0);
+};
+
+
+/**
+ * Multiply the underlying matrices of two transforms and return the result in
+ * the first transform.
+ * @param {!ol.Transform} transform1 Transform parameters of matrix 1.
+ * @param {!ol.Transform} transform2 Transform parameters of matrix 2.
+ * @return {!ol.Transform} transform1 multiplied with transform2.
+ */
+ol.transform.multiply = function(transform1, transform2) {
+ var a1 = transform1[0];
+ var b1 = transform1[1];
+ var c1 = transform1[2];
+ var d1 = transform1[3];
+ var e1 = transform1[4];
+ var f1 = transform1[5];
+ var a2 = transform2[0];
+ var b2 = transform2[1];
+ var c2 = transform2[2];
+ var d2 = transform2[3];
+ var e2 = transform2[4];
+ var f2 = transform2[5];
+
+ transform1[0] = a1 * a2 + c1 * b2;
+ transform1[1] = b1 * a2 + d1 * b2;
+ transform1[2] = a1 * c2 + c1 * d2;
+ transform1[3] = b1 * c2 + d1 * d2;
+ transform1[4] = a1 * e2 + c1 * f2 + e1;
+ transform1[5] = b1 * e2 + d1 * f2 + f1;
+
+ return transform1;
+};
+
+/**
+ * Set the transform components a-f on a given transform.
+ * @param {!ol.Transform} transform Transform.
+ * @param {number} a The a component of the transform.
+ * @param {number} b The b component of the transform.
+ * @param {number} c The c component of the transform.
+ * @param {number} d The d component of the transform.
+ * @param {number} e The e component of the transform.
+ * @param {number} f The f component of the transform.
+ * @return {!ol.Transform} Matrix with transform applied.
+ */
+ol.transform.set = function(transform, a, b, c, d, e, f) {
+ transform[0] = a;
+ transform[1] = b;
+ transform[2] = c;
+ transform[3] = d;
+ transform[4] = e;
+ transform[5] = f;
+ return transform;
+};
+
+
+/**
+ * Set transform on one matrix from another matrix.
+ * @param {!ol.Transform} transform1 Matrix to set transform to.
+ * @param {!ol.Transform} transform2 Matrix to set transform from.
+ * @return {!ol.Transform} transform1 with transform from transform2 applied.
+ */
+ol.transform.setFromArray = function(transform1, transform2) {
+ transform1[0] = transform2[0];
+ transform1[1] = transform2[1];
+ transform1[2] = transform2[2];
+ transform1[3] = transform2[3];
+ transform1[4] = transform2[4];
+ transform1[5] = transform2[5];
+ return transform1;
+};
+
+
+/**
+ * Transforms the given coordinate with the given transform returning the
+ * resulting, transformed coordinate. The coordinate will be modified in-place.
+ *
+ * @param {ol.Transform} transform The transformation.
+ * @param {ol.Coordinate|ol.Pixel} coordinate The coordinate to transform.
+ * @return {ol.Coordinate|ol.Pixel} return coordinate so that operations can be
+ * chained together.
+ */
+ol.transform.apply = function(transform, coordinate) {
+ var x = coordinate[0], y = coordinate[1];
+ coordinate[0] = transform[0] * x + transform[2] * y + transform[4];
+ coordinate[1] = transform[1] * x + transform[3] * y + transform[5];
+ return coordinate;
+};
+
+
+/**
+ * Applies rotation to the given transform.
+ * @param {!ol.Transform} transform Transform.
+ * @param {number} angle Angle in radians.
+ * @return {!ol.Transform} The rotated transform.
+ */
+ol.transform.rotate = function(transform, angle) {
+ var cos = Math.cos(angle);
+ var sin = Math.sin(angle);
+ return ol.transform.multiply(transform,
+ ol.transform.set(ol.transform.tmp_, cos, sin, -sin, cos, 0, 0));
+};
+
+
+/**
+ * Applies scale to a given transform.
+ * @param {!ol.Transform} transform Transform.
+ * @param {number} x Scale factor x.
+ * @param {number} y Scale factor y.
+ * @return {!ol.Transform} The scaled transform.
+ */
+ol.transform.scale = function(transform, x, y) {
+ return ol.transform.multiply(transform,
+ ol.transform.set(ol.transform.tmp_, x, 0, 0, y, 0, 0));
+};
+
+
+/**
+ * Applies translation to the given transform.
+ * @param {!ol.Transform} transform Transform.
+ * @param {number} dx Translation x.
+ * @param {number} dy Translation y.
+ * @return {!ol.Transform} The translated transform.
+ */
+ol.transform.translate = function(transform, dx, dy) {
+ return ol.transform.multiply(transform,
+ ol.transform.set(ol.transform.tmp_, 1, 0, 0, 1, dx, dy));
+};
+
+
+/**
+ * Creates a composite transform given an initial translation, scale, rotation, and
+ * final translation (in that order only, not commutative).
+ * @param {!ol.Transform} transform The transform (will be modified in place).
+ * @param {number} dx1 Initial translation x.
+ * @param {number} dy1 Initial translation y.
+ * @param {number} sx Scale factor x.
+ * @param {number} sy Scale factor y.
+ * @param {number} angle Rotation (in counter-clockwise radians).
+ * @param {number} dx2 Final translation x.
+ * @param {number} dy2 Final translation y.
+ * @return {!ol.Transform} The composite transform.
+ */
+ol.transform.compose = function(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
+ var sin = Math.sin(angle);
+ var cos = Math.cos(angle);
+ transform[0] = sx * cos;
+ transform[1] = sy * sin;
+ transform[2] = -sx * sin;
+ transform[3] = sy * cos;
+ transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;
+ transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;
+ return transform;
+};
+
+
+/**
+ * Invert the given transform.
+ * @param {!ol.Transform} transform Transform.
+ * @return {!ol.Transform} Inverse of the transform.
+ */
+ol.transform.invert = function(transform) {
+ var det = ol.transform.determinant(transform);
+ ol.asserts.assert(det !== 0, 32); // Transformation matrix cannot be inverted
+
+ var a = transform[0];
+ var b = transform[1];
+ var c = transform[2];
+ var d = transform[3];
+ var e = transform[4];
+ var f = transform[5];
+
+ transform[0] = d / det;
+ transform[1] = -b / det;
+ transform[2] = -c / det;
+ transform[3] = a / det;
+ transform[4] = (c * f - d * e) / det;
+ transform[5] = -(a * f - b * e) / det;
+
+ return transform;
+};
+
+
+/**
+ * Returns the determinant of the given matrix.
+ * @param {!ol.Transform} mat Matrix.
+ * @return {number} Determinant.
+ */
+ol.transform.determinant = function(mat) {
+ return mat[0] * mat[3] - mat[1] * mat[2];
+};
+
+goog.provide('ol.renderer.Map');
+
+goog.require('ol');
+goog.require('ol.Disposable');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.functions');
+goog.require('ol.layer.Layer');
+goog.require('ol.style');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.Disposable}
+ * @param {Element} container Container.
+ * @param {ol.Map} map Map.
+ * @struct
+ */
+ol.renderer.Map = function(container, map) {
+
+ ol.Disposable.call(this);
+
+
+ /**
+ * @private
+ * @type {ol.Map}
+ */
+ this.map_ = map;
+
+ /**
+ * @private
+ * @type {Object.<string, ol.renderer.Layer>}
+ */
+ this.layerRenderers_ = {};
+
+ /**
+ * @private
+ * @type {Object.<string, ol.EventsKey>}
+ */
+ this.layerRendererListeners_ = {};
+
+};
+ol.inherits(ol.renderer.Map, ol.Disposable);
+
+
+/**
+ * @param {olx.FrameState} frameState FrameState.
+ * @protected
+ */
+ol.renderer.Map.prototype.calculateMatrices2D = function(frameState) {
+ var viewState = frameState.viewState;
+ var coordinateToPixelTransform = frameState.coordinateToPixelTransform;
+ var pixelToCoordinateTransform = frameState.pixelToCoordinateTransform;
+ ol.DEBUG && console.assert(coordinateToPixelTransform,
+ 'frameState has a coordinateToPixelTransform');
+
+ ol.transform.compose(coordinateToPixelTransform,
+ frameState.size[0] / 2, frameState.size[1] / 2,
+ 1 / viewState.resolution, -1 / viewState.resolution,
+ -viewState.rotation,
+ -viewState.center[0], -viewState.center[1]);
+
+ ol.transform.invert(
+ ol.transform.setFromArray(pixelToCoordinateTransform, coordinateToPixelTransform));
+};
+
+
+/**
+ * @abstract
+ * @param {ol.layer.Layer} layer Layer.
+ * @protected
+ * @return {ol.renderer.Layer} layerRenderer Layer renderer.
+ */
+ol.renderer.Map.prototype.createLayerRenderer = function(layer) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.Map.prototype.disposeInternal = function() {
+ for (var id in this.layerRenderers_) {
+ this.layerRenderers_[id].dispose();
+ }
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.Map.expireIconCache_ = function(map, frameState) {
+ var cache = ol.style.iconImageCache;
+ cache.expire();
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {olx.FrameState} frameState FrameState.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @param {function(this: S, (ol.Feature|ol.render.Feature),
+ * ol.layer.Layer): T} callback Feature callback.
+ * @param {S} thisArg Value to use as `this` when executing `callback`.
+ * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter
+ * function, only layers which are visible and for which this function
+ * returns `true` will be tested for features. By default, all visible
+ * layers will be tested.
+ * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
+ * @return {T|undefined} Callback result.
+ * @template S,T,U
+ */
+ol.renderer.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg,
+ layerFilter, thisArg2) {
+ var result;
+ var viewState = frameState.viewState;
+ var viewResolution = viewState.resolution;
+
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {ol.layer.Layer} layer Layer.
+ * @return {?} Callback result.
+ */
+ function forEachFeatureAtCoordinate(feature, layer) {
+ var key = ol.getUid(feature).toString();
+ var managed = frameState.layerStates[ol.getUid(layer)].managed;
+ if (!(key in frameState.skippedFeatureUids && !managed)) {
+ return callback.call(thisArg, feature, managed ? layer : null);
+ }
+ }
+
+ var projection = viewState.projection;
+
+ var translatedCoordinate = coordinate;
+ if (projection.canWrapX()) {
+ var projectionExtent = projection.getExtent();
+ var worldWidth = ol.extent.getWidth(projectionExtent);
+ var x = coordinate[0];
+ if (x < projectionExtent[0] || x > projectionExtent[2]) {
+ var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth);
+ translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]];
+ }
+ }
+
+ var layerStates = frameState.layerStatesArray;
+ var numLayers = layerStates.length;
+ var i;
+ for (i = numLayers - 1; i >= 0; --i) {
+ var layerState = layerStates[i];
+ var layer = layerState.layer;
+ if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
+ layerFilter.call(thisArg2, layer)) {
+ var layerRenderer = this.getLayerRenderer(layer);
+ if (layer.getSource()) {
+ result = layerRenderer.forEachFeatureAtCoordinate(
+ layer.getSource().getWrapX() ? translatedCoordinate : coordinate,
+ frameState, hitTolerance, forEachFeatureAtCoordinate, thisArg);
+ }
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @abstract
+ * @param {ol.Pixel} pixel Pixel.
+ * @param {olx.FrameState} frameState FrameState.
+ * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer
+ * callback.
+ * @param {S} thisArg Value to use as `this` when executing `callback`.
+ * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter
+ * function, only layers which are visible and for which this function
+ * returns `true` will be tested for features. By default, all visible
+ * layers will be tested.
+ * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
+ * @return {T|undefined} Callback result.
+ * @template S,T,U
+ */
+ol.renderer.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg,
+ layerFilter, thisArg2) {};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {olx.FrameState} frameState FrameState.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter
+ * function, only layers which are visible and for which this function
+ * returns `true` will be tested for features. By default, all visible
+ * layers will be tested.
+ * @param {U} thisArg Value to use as `this` when executing `layerFilter`.
+ * @return {boolean} Is there a feature at the given coordinate?
+ * @template U
+ */
+ol.renderer.Map.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, layerFilter, thisArg) {
+ var hasFeature = this.forEachFeatureAtCoordinate(
+ coordinate, frameState, hitTolerance, ol.functions.TRUE, this, layerFilter, thisArg);
+
+ return hasFeature !== undefined;
+};
+
+
+/**
+ * @param {ol.layer.Layer} layer Layer.
+ * @protected
+ * @return {ol.renderer.Layer} Layer renderer.
+ */
+ol.renderer.Map.prototype.getLayerRenderer = function(layer) {
+ var layerKey = ol.getUid(layer).toString();
+ if (layerKey in this.layerRenderers_) {
+ return this.layerRenderers_[layerKey];
+ } else {
+ var layerRenderer = this.createLayerRenderer(layer);
+ this.layerRenderers_[layerKey] = layerRenderer;
+ this.layerRendererListeners_[layerKey] = ol.events.listen(layerRenderer,
+ ol.events.EventType.CHANGE, this.handleLayerRendererChange_, this);
+
+ return layerRenderer;
+ }
+};
+
+
+/**
+ * @param {string} layerKey Layer key.
+ * @protected
+ * @return {ol.renderer.Layer} Layer renderer.
+ */
+ol.renderer.Map.prototype.getLayerRendererByKey = function(layerKey) {
+ ol.DEBUG && console.assert(layerKey in this.layerRenderers_,
+ 'given layerKey (%s) exists in layerRenderers', layerKey);
+ return this.layerRenderers_[layerKey];
+};
+
+
+/**
+ * @protected
+ * @return {Object.<string, ol.renderer.Layer>} Layer renderers.
+ */
+ol.renderer.Map.prototype.getLayerRenderers = function() {
+ return this.layerRenderers_;
+};
+
+
+/**
+ * @return {ol.Map} Map.
+ */
+ol.renderer.Map.prototype.getMap = function() {
+ return this.map_;
+};
+
+
+/**
+ * @abstract
+ * @return {string} Type
+ */
+ol.renderer.Map.prototype.getType = function() {};
+
+
+/**
+ * Handle changes in a layer renderer.
+ * @private
+ */
+ol.renderer.Map.prototype.handleLayerRendererChange_ = function() {
+ this.map_.render();
+};
+
+
+/**
+ * @param {string} layerKey Layer key.
+ * @return {ol.renderer.Layer} Layer renderer.
+ * @private
+ */
+ol.renderer.Map.prototype.removeLayerRendererByKey_ = function(layerKey) {
+ ol.DEBUG && console.assert(layerKey in this.layerRenderers_,
+ 'given layerKey (%s) exists in layerRenderers', layerKey);
+ var layerRenderer = this.layerRenderers_[layerKey];
+ delete this.layerRenderers_[layerKey];
+
+ ol.DEBUG && console.assert(layerKey in this.layerRendererListeners_,
+ 'given layerKey (%s) exists in layerRendererListeners', layerKey);
+ ol.events.unlistenByKey(this.layerRendererListeners_[layerKey]);
+ delete this.layerRendererListeners_[layerKey];
+
+ return layerRenderer;
+};
+
+
+/**
+ * Render.
+ * @param {?olx.FrameState} frameState Frame state.
+ */
+ol.renderer.Map.prototype.renderFrame = ol.nullFunction;
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.Map.prototype.removeUnusedLayerRenderers_ = function(map, frameState) {
+ var layerKey;
+ for (layerKey in this.layerRenderers_) {
+ if (!frameState || !(layerKey in frameState.layerStates)) {
+ this.removeLayerRendererByKey_(layerKey).dispose();
+ }
+ }
+};
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @protected
+ */
+ol.renderer.Map.prototype.scheduleExpireIconCache = function(frameState) {
+ frameState.postRenderFunctions.push(
+ /** @type {ol.PostRenderFunction} */ (ol.renderer.Map.expireIconCache_)
+ );
+};
+
+
+/**
+ * @param {!olx.FrameState} frameState Frame state.
+ * @protected
+ */
+ol.renderer.Map.prototype.scheduleRemoveUnusedLayerRenderers = function(frameState) {
+ var layerKey;
+ for (layerKey in this.layerRenderers_) {
+ if (!(layerKey in frameState.layerStates)) {
+ frameState.postRenderFunctions.push(
+ /** @type {ol.PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this))
+ );
+ return;
+ }
+ }
+};
+
+
+/**
+ * @param {ol.LayerState} state1 First layer state.
+ * @param {ol.LayerState} state2 Second layer state.
+ * @return {number} The zIndex difference.
+ */
+ol.renderer.Map.sortByZIndex = function(state1, state2) {
+ return state1.zIndex - state2.zIndex;
+};
+
+goog.provide('ol.layer.Image');
+
+goog.require('ol');
+goog.require('ol.layer.Layer');
+
+
+/**
+ * @classdesc
+ * Server-rendered images that are available for arbitrary extents and
+ * resolutions.
+ * Note that any property set in the options is set as a {@link ol.Object}
+ * property on the layer object; for example, setting `title: 'My Title'` in the
+ * options means that `title` is observable, and has get/set accessors.
+ *
+ * @constructor
+ * @extends {ol.layer.Layer}
+ * @fires ol.render.Event
+ * @param {olx.layer.ImageOptions=} opt_options Layer options.
+ * @api stable
+ */
+ol.layer.Image = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+ ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (options));
+};
+ol.inherits(ol.layer.Image, ol.layer.Layer);
+
+
+/**
+ * Return the associated {@link ol.source.Image source} of the image layer.
+ * @function
+ * @return {ol.source.Image} Source.
+ * @api stable
+ */
+ol.layer.Image.prototype.getSource;
+
+goog.provide('ol.layer.Tile');
+
+goog.require('ol');
+goog.require('ol.layer.Layer');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * For layer sources that provide pre-rendered, tiled images in grids that are
+ * organized by zoom levels for specific resolutions.
+ * Note that any property set in the options is set as a {@link ol.Object}
+ * property on the layer object; for example, setting `title: 'My Title'` in the
+ * options means that `title` is observable, and has get/set accessors.
+ *
+ * @constructor
+ * @extends {ol.layer.Layer}
+ * @fires ol.render.Event
+ * @param {olx.layer.TileOptions=} opt_options Tile layer options.
+ * @api stable
+ */
+ol.layer.Tile = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+
+ var baseOptions = ol.obj.assign({}, options);
+
+ delete baseOptions.preload;
+ delete baseOptions.useInterimTilesOnError;
+ ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (baseOptions));
+
+ this.setPreload(options.preload !== undefined ? options.preload : 0);
+ this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ?
+ options.useInterimTilesOnError : true);
+};
+ol.inherits(ol.layer.Tile, ol.layer.Layer);
+
+
+/**
+ * Return the level as number to which we will preload tiles up to.
+ * @return {number} The level to preload tiles up to.
+ * @observable
+ * @api
+ */
+ol.layer.Tile.prototype.getPreload = function() {
+ return /** @type {number} */ (this.get(ol.layer.Tile.Property.PRELOAD));
+};
+
+
+/**
+ * Return the associated {@link ol.source.Tile tilesource} of the layer.
+ * @function
+ * @return {ol.source.Tile} Source.
+ * @api stable
+ */
+ol.layer.Tile.prototype.getSource;
+
+
+/**
+ * Set the level as number to which we will preload tiles up to.
+ * @param {number} preload The level to preload tiles up to.
+ * @observable
+ * @api
+ */
+ol.layer.Tile.prototype.setPreload = function(preload) {
+ this.set(ol.layer.Tile.Property.PRELOAD, preload);
+};
+
+
+/**
+ * Whether we use interim tiles on error.
+ * @return {boolean} Use interim tiles on error.
+ * @observable
+ * @api
+ */
+ol.layer.Tile.prototype.getUseInterimTilesOnError = function() {
+ return /** @type {boolean} */ (
+ this.get(ol.layer.Tile.Property.USE_INTERIM_TILES_ON_ERROR));
+};
+
+
+/**
+ * Set whether we use interim tiles on error.
+ * @param {boolean} useInterimTilesOnError Use interim tiles on error.
+ * @observable
+ * @api
+ */
+ol.layer.Tile.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) {
+ this.set(
+ ol.layer.Tile.Property.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.layer.Tile.Property = {
+ PRELOAD: 'preload',
+ USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError'
+};
+
+goog.provide('ol.ImageBase');
+
+goog.require('ol');
+goog.require('ol.events.EventTarget');
+goog.require('ol.events.EventType');
+
+
+/**
+ * @constructor
+ * @extends {ol.events.EventTarget}
+ * @param {ol.Extent} extent Extent.
+ * @param {number|undefined} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Image.State} state State.
+ * @param {Array.<ol.Attribution>} attributions Attributions.
+ */
+ol.ImageBase = function(extent, resolution, pixelRatio, state, attributions) {
+
+ ol.events.EventTarget.call(this);
+
+ /**
+ * @private
+ * @type {Array.<ol.Attribution>}
+ */
+ this.attributions_ = attributions;
+
+ /**
+ * @protected
+ * @type {ol.Extent}
+ */
+ this.extent = extent;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelRatio_ = pixelRatio;
+
+ /**
+ * @protected
+ * @type {number|undefined}
+ */
+ this.resolution = resolution;
+
+ /**
+ * @protected
+ * @type {ol.Image.State}
+ */
+ this.state = state;
+
+};
+ol.inherits(ol.ImageBase, ol.events.EventTarget);
+
+
+/**
+ * @protected
+ */
+ol.ImageBase.prototype.changed = function() {
+ this.dispatchEvent(ol.events.EventType.CHANGE);
+};
+
+
+/**
+ * @return {Array.<ol.Attribution>} Attributions.
+ */
+ol.ImageBase.prototype.getAttributions = function() {
+ return this.attributions_;
+};
+
+
+/**
+ * @return {ol.Extent} Extent.
+ */
+ol.ImageBase.prototype.getExtent = function() {
+ return this.extent;
+};
+
+
+/**
+ * @abstract
+ * @param {Object=} opt_context Object.
+ * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image.
+ */
+ol.ImageBase.prototype.getImage = function(opt_context) {};
+
+
+/**
+ * @return {number} PixelRatio.
+ */
+ol.ImageBase.prototype.getPixelRatio = function() {
+ return this.pixelRatio_;
+};
+
+
+/**
+ * @return {number} Resolution.
+ */
+ol.ImageBase.prototype.getResolution = function() {
+ ol.DEBUG && console.assert(this.resolution !== undefined, 'resolution not yet set');
+ return /** @type {number} */ (this.resolution);
+};
+
+
+/**
+ * @return {ol.Image.State} State.
+ */
+ol.ImageBase.prototype.getState = function() {
+ return this.state;
+};
+
+
+/**
+ * Load not yet loaded URI.
+ * @abstract
+ */
+ol.ImageBase.prototype.load = function() {};
+
+goog.provide('ol.Image');
+
+goog.require('ol');
+goog.require('ol.ImageBase');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.obj');
+
+
+/**
+ * @constructor
+ * @extends {ol.ImageBase}
+ * @param {ol.Extent} extent Extent.
+ * @param {number|undefined} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {Array.<ol.Attribution>} attributions Attributions.
+ * @param {string} src Image source URI.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
+ */
+ol.Image = function(extent, resolution, pixelRatio, attributions, src,
+ crossOrigin, imageLoadFunction) {
+
+ ol.ImageBase.call(this, extent, resolution, pixelRatio, ol.Image.State.IDLE,
+ attributions);
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement|Image|HTMLVideoElement}
+ */
+ this.image_ = new Image();
+ if (crossOrigin !== null) {
+ this.image_.crossOrigin = crossOrigin;
+ }
+
+ /**
+ * @private
+ * @type {Object.<number, (HTMLCanvasElement|Image|HTMLVideoElement)>}
+ */
+ this.imageByContext_ = {};
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.imageListenerKeys_ = null;
+
+ /**
+ * @protected
+ * @type {ol.Image.State}
+ */
+ this.state = ol.Image.State.IDLE;
+
+ /**
+ * @private
+ * @type {ol.ImageLoadFunctionType}
+ */
+ this.imageLoadFunction_ = imageLoadFunction;
+
+};
+ol.inherits(ol.Image, ol.ImageBase);
+
+
+/**
+ * Get the HTML image element (may be a Canvas, Image, or Video).
+ * @param {Object=} opt_context Object.
+ * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image.
+ * @api
+ */
+ol.Image.prototype.getImage = function(opt_context) {
+ if (opt_context !== undefined) {
+ var image;
+ var key = ol.getUid(opt_context);
+ if (key in this.imageByContext_) {
+ return this.imageByContext_[key];
+ } else if (ol.obj.isEmpty(this.imageByContext_)) {
+ image = this.image_;
+ } else {
+ image = /** @type {Image} */ (this.image_.cloneNode(false));
+ }
+ this.imageByContext_[key] = image;
+ return image;
+ } else {
+ return this.image_;
+ }
+};
+
+
+/**
+ * Tracks loading or read errors.
+ *
+ * @private
+ */
+ol.Image.prototype.handleImageError_ = function() {
+ this.state = ol.Image.State.ERROR;
+ this.unlistenImage_();
+ this.changed();
+};
+
+
+/**
+ * Tracks successful image load.
+ *
+ * @private
+ */
+ol.Image.prototype.handleImageLoad_ = function() {
+ if (this.resolution === undefined) {
+ this.resolution = ol.extent.getHeight(this.extent) / this.image_.height;
+ }
+ this.state = ol.Image.State.LOADED;
+ this.unlistenImage_();
+ this.changed();
+};
+
+
+/**
+ * Load the image or retry if loading previously failed.
+ * Loading is taken care of by the tile queue, and calling this method is
+ * only needed for preloading or for reloading in case of an error.
+ * @api
+ */
+ol.Image.prototype.load = function() {
+ if (this.state == ol.Image.State.IDLE || this.state == ol.Image.State.ERROR) {
+ this.state = ol.Image.State.LOADING;
+ this.changed();
+ ol.DEBUG && console.assert(!this.imageListenerKeys_,
+ 'this.imageListenerKeys_ should be null');
+ this.imageListenerKeys_ = [
+ ol.events.listenOnce(this.image_, ol.events.EventType.ERROR,
+ this.handleImageError_, this),
+ ol.events.listenOnce(this.image_, ol.events.EventType.LOAD,
+ this.handleImageLoad_, this)
+ ];
+ this.imageLoadFunction_(this, this.src_);
+ }
+};
+
+
+/**
+ * @param {HTMLCanvasElement|Image|HTMLVideoElement} image Image.
+ */
+ol.Image.prototype.setImage = function(image) {
+ this.image_ = image;
+};
+
+
+/**
+ * Discards event handlers which listen for load completion or errors.
+ *
+ * @private
+ */
+ol.Image.prototype.unlistenImage_ = function() {
+ this.imageListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.imageListenerKeys_ = null;
+};
+
+
+/**
+ * @enum {number}
+ */
+ol.Image.State = {
+ IDLE: 0,
+ LOADING: 1,
+ LOADED: 2,
+ ERROR: 3
+};
+
+goog.provide('ol.render.canvas');
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.canvas.defaultFont = '10px sans-serif';
+
+
+/**
+ * @const
+ * @type {ol.Color}
+ */
+ol.render.canvas.defaultFillStyle = [0, 0, 0, 1];
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.canvas.defaultLineCap = 'round';
+
+
+/**
+ * @const
+ * @type {Array.<number>}
+ */
+ol.render.canvas.defaultLineDash = [];
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.canvas.defaultLineJoin = 'round';
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.render.canvas.defaultMiterLimit = 10;
+
+
+/**
+ * @const
+ * @type {ol.Color}
+ */
+ol.render.canvas.defaultStrokeStyle = [0, 0, 0, 1];
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.canvas.defaultTextAlign = 'center';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.canvas.defaultTextBaseline = 'middle';
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.render.canvas.defaultLineWidth = 1;
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {number} rotation Rotation.
+ * @param {number} offsetX X offset.
+ * @param {number} offsetY Y offset.
+ */
+ol.render.canvas.rotateAtOffset = function(context, rotation, offsetX, offsetY) {
+ if (rotation !== 0) {
+ context.translate(offsetX, offsetY);
+ context.rotate(rotation);
+ context.translate(-offsetX, -offsetY);
+ }
+};
+
+goog.provide('ol.style.Image');
+
+
+/**
+ * @classdesc
+ * A base class used for creating subclasses and not instantiated in
+ * apps. Base class for {@link ol.style.Icon}, {@link ol.style.Circle} and
+ * {@link ol.style.RegularShape}.
+ *
+ * @constructor
+ * @param {ol.StyleImageOptions} options Options.
+ * @api
+ */
+ol.style.Image = function(options) {
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.opacity_ = options.opacity;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.rotateWithView_ = options.rotateWithView;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.rotation_ = options.rotation;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.scale_ = options.scale;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.snapToPixel_ = options.snapToPixel;
+
+};
+
+
+/**
+ * Get the symbolizer opacity.
+ * @return {number} Opacity.
+ * @api
+ */
+ol.style.Image.prototype.getOpacity = function() {
+ return this.opacity_;
+};
+
+
+/**
+ * Determine whether the symbolizer rotates with the map.
+ * @return {boolean} Rotate with map.
+ * @api
+ */
+ol.style.Image.prototype.getRotateWithView = function() {
+ return this.rotateWithView_;
+};
+
+
+/**
+ * Get the symoblizer rotation.
+ * @return {number} Rotation.
+ * @api
+ */
+ol.style.Image.prototype.getRotation = function() {
+ return this.rotation_;
+};
+
+
+/**
+ * Get the symbolizer scale.
+ * @return {number} Scale.
+ * @api
+ */
+ol.style.Image.prototype.getScale = function() {
+ return this.scale_;
+};
+
+
+/**
+ * Determine whether the symbolizer should be snapped to a pixel.
+ * @return {boolean} The symbolizer should snap to a pixel.
+ * @api
+ */
+ol.style.Image.prototype.getSnapToPixel = function() {
+ return this.snapToPixel_;
+};
+
+
+/**
+ * Get the anchor point in pixels. The anchor determines the center point for the
+ * symbolizer.
+ * @abstract
+ * @return {Array.<number>} Anchor.
+ */
+ol.style.Image.prototype.getAnchor = function() {};
+
+
+/**
+ * Get the image element for the symbolizer.
+ * @abstract
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element.
+ */
+ol.style.Image.prototype.getImage = function(pixelRatio) {};
+
+
+/**
+ * @abstract
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element.
+ */
+ol.style.Image.prototype.getHitDetectionImage = function(pixelRatio) {};
+
+
+/**
+ * @abstract
+ * @return {ol.Image.State} Image state.
+ */
+ol.style.Image.prototype.getImageState = function() {};
+
+
+/**
+ * @abstract
+ * @return {ol.Size} Image size.
+ */
+ol.style.Image.prototype.getImageSize = function() {};
+
+
+/**
+ * @abstract
+ * @return {ol.Size} Size of the hit-detection image.
+ */
+ol.style.Image.prototype.getHitDetectionImageSize = function() {};
+
+
+/**
+ * Get the origin of the symbolizer.
+ * @abstract
+ * @return {Array.<number>} Origin.
+ */
+ol.style.Image.prototype.getOrigin = function() {};
+
+
+/**
+ * Get the size of the symbolizer (in pixels).
+ * @abstract
+ * @return {ol.Size} Size.
+ */
+ol.style.Image.prototype.getSize = function() {};
+
+
+/**
+ * Set the opacity.
+ *
+ * @param {number} opacity Opacity.
+ * @api
+ */
+ol.style.Image.prototype.setOpacity = function(opacity) {
+ this.opacity_ = opacity;
+};
+
+
+/**
+ * Set whether to rotate the style with the view.
+ *
+ * @param {boolean} rotateWithView Rotate with map.
+ */
+ol.style.Image.prototype.setRotateWithView = function(rotateWithView) {
+ this.rotateWithView_ = rotateWithView;
+};
+
+
+/**
+ * Set the rotation.
+ *
+ * @param {number} rotation Rotation.
+ * @api
+ */
+ol.style.Image.prototype.setRotation = function(rotation) {
+ this.rotation_ = rotation;
+};
+
+
+/**
+ * Set the scale.
+ *
+ * @param {number} scale Scale.
+ * @api
+ */
+ol.style.Image.prototype.setScale = function(scale) {
+ this.scale_ = scale;
+};
+
+
+/**
+ * Set whether to snap the image to the closest pixel.
+ *
+ * @param {boolean} snapToPixel Snap to pixel?
+ */
+ol.style.Image.prototype.setSnapToPixel = function(snapToPixel) {
+ this.snapToPixel_ = snapToPixel;
+};
+
+
+/**
+ * @abstract
+ * @param {function(this: T, ol.events.Event)} listener Listener function.
+ * @param {T} thisArg Value to use as `this` when executing `listener`.
+ * @return {ol.EventsKey|undefined} Listener key.
+ * @template T
+ */
+ol.style.Image.prototype.listenImageChange = function(listener, thisArg) {};
+
+
+/**
+ * Load not yet loaded URI.
+ * @abstract
+ */
+ol.style.Image.prototype.load = function() {};
+
+
+/**
+ * @abstract
+ * @param {function(this: T, ol.events.Event)} listener Listener function.
+ * @param {T} thisArg Value to use as `this` when executing `listener`.
+ * @template T
+ */
+ol.style.Image.prototype.unlistenImageChange = function(listener, thisArg) {};
+
+goog.provide('ol.style.RegularShape');
+
+goog.require('ol');
+goog.require('ol.colorlike');
+goog.require('ol.dom');
+goog.require('ol.has');
+goog.require('ol.Image');
+goog.require('ol.render.canvas');
+goog.require('ol.style.Image');
+
+
+/**
+ * @classdesc
+ * Set regular shape style for vector features. The resulting shape will be
+ * a regular polygon when `radius` is provided, or a star when `radius1` and
+ * `radius2` are provided.
+ *
+ * @constructor
+ * @param {olx.style.RegularShapeOptions} options Options.
+ * @extends {ol.style.Image}
+ * @api
+ */
+ol.style.RegularShape = function(options) {
+
+ ol.DEBUG && console.assert(
+ options.radius !== undefined || options.radius1 !== undefined,
+ 'must provide either "radius" or "radius1"');
+
+ /**
+ * @private
+ * @type {Array.<string>}
+ */
+ this.checksums_ = null;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = null;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.hitDetectionCanvas_ = null;
+
+ /**
+ * @private
+ * @type {ol.style.Fill}
+ */
+ this.fill_ = options.fill !== undefined ? options.fill : null;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.origin_ = [0, 0];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.points_ = options.points;
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.radius_ = /** @type {number} */ (options.radius !== undefined ?
+ options.radius : options.radius1);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.radius2_ =
+ options.radius2 !== undefined ? options.radius2 : this.radius_;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.angle_ = options.angle !== undefined ? options.angle : 0;
+
+ /**
+ * @private
+ * @type {ol.style.Stroke}
+ */
+ this.stroke_ = options.stroke !== undefined ? options.stroke : null;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.anchor_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.size_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.imageSize_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.hitDetectionImageSize_ = null;
+
+ /**
+ * @protected
+ * @type {ol.style.AtlasManager|undefined}
+ */
+ this.atlasManager_ = options.atlasManager;
+
+ this.render_(this.atlasManager_);
+
+ /**
+ * @type {boolean}
+ */
+ var snapToPixel = options.snapToPixel !== undefined ?
+ options.snapToPixel : true;
+
+ /**
+ * @type {boolean}
+ */
+ var rotateWithView = options.rotateWithView !== undefined ?
+ options.rotateWithView : false;
+
+ ol.style.Image.call(this, {
+ opacity: 1,
+ rotateWithView: rotateWithView,
+ rotation: options.rotation !== undefined ? options.rotation : 0,
+ scale: 1,
+ snapToPixel: snapToPixel
+ });
+
+};
+ol.inherits(ol.style.RegularShape, ol.style.Image);
+
+
+/**
+ * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too.
+ * @return {ol.style.RegularShape} The cloned style.
+ * @api
+ */
+ol.style.RegularShape.prototype.clone = function() {
+ var style = new ol.style.RegularShape({
+ fill: this.getFill() ? this.getFill().clone() : undefined,
+ points: this.getRadius2() !== this.getRadius() ? this.getPoints() / 2 : this.getPoints(),
+ radius: this.getRadius(),
+ radius2: this.getRadius2(),
+ angle: this.getAngle(),
+ snapToPixel: this.getSnapToPixel(),
+ stroke: this.getStroke() ? this.getStroke().clone() : undefined,
+ rotation: this.getRotation(),
+ rotateWithView: this.getRotateWithView(),
+ atlasManager: this.atlasManager_
+ });
+ style.setOpacity(this.getOpacity());
+ style.setScale(this.getScale());
+ return style;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.RegularShape.prototype.getAnchor = function() {
+ return this.anchor_;
+};
+
+
+/**
+ * Get the angle used in generating the shape.
+ * @return {number} Shape's rotation in radians.
+ * @api
+ */
+ol.style.RegularShape.prototype.getAngle = function() {
+ return this.angle_;
+};
+
+
+/**
+ * Get the fill style for the shape.
+ * @return {ol.style.Fill} Fill style.
+ * @api
+ */
+ol.style.RegularShape.prototype.getFill = function() {
+ return this.fill_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.getHitDetectionImage = function(pixelRatio) {
+ return this.hitDetectionCanvas_;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.RegularShape.prototype.getImage = function(pixelRatio) {
+ return this.canvas_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.getImageSize = function() {
+ return this.imageSize_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.getHitDetectionImageSize = function() {
+ return this.hitDetectionImageSize_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.getImageState = function() {
+ return ol.Image.State.LOADED;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.RegularShape.prototype.getOrigin = function() {
+ return this.origin_;
+};
+
+
+/**
+ * Get the number of points for generating the shape.
+ * @return {number} Number of points for stars and regular polygons.
+ * @api
+ */
+ol.style.RegularShape.prototype.getPoints = function() {
+ return this.points_;
+};
+
+
+/**
+ * Get the (primary) radius for the shape.
+ * @return {number} Radius.
+ * @api
+ */
+ol.style.RegularShape.prototype.getRadius = function() {
+ return this.radius_;
+};
+
+
+/**
+ * Get the secondary radius for the shape.
+ * @return {number} Radius2.
+ * @api
+ */
+ol.style.RegularShape.prototype.getRadius2 = function() {
+ return this.radius2_;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.RegularShape.prototype.getSize = function() {
+ return this.size_;
+};
+
+
+/**
+ * Get the stroke style for the shape.
+ * @return {ol.style.Stroke} Stroke style.
+ * @api
+ */
+ol.style.RegularShape.prototype.getStroke = function() {
+ return this.stroke_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.listenImageChange = ol.nullFunction;
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.load = ol.nullFunction;
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.RegularShape.prototype.unlistenImageChange = ol.nullFunction;
+
+
+/**
+ * @protected
+ * @param {ol.style.AtlasManager|undefined} atlasManager An atlas manager.
+ */
+ol.style.RegularShape.prototype.render_ = function(atlasManager) {
+ var imageSize;
+ var lineCap = '';
+ var lineJoin = '';
+ var miterLimit = 0;
+ var lineDash = null;
+ var strokeStyle;
+ var strokeWidth = 0;
+
+ if (this.stroke_) {
+ strokeStyle = ol.colorlike.asColorLike(this.stroke_.getColor());
+ strokeWidth = this.stroke_.getWidth();
+ if (strokeWidth === undefined) {
+ strokeWidth = ol.render.canvas.defaultLineWidth;
+ }
+ lineDash = this.stroke_.getLineDash();
+ if (!ol.has.CANVAS_LINE_DASH) {
+ lineDash = null;
+ }
+ lineJoin = this.stroke_.getLineJoin();
+ if (lineJoin === undefined) {
+ lineJoin = ol.render.canvas.defaultLineJoin;
+ }
+ lineCap = this.stroke_.getLineCap();
+ if (lineCap === undefined) {
+ lineCap = ol.render.canvas.defaultLineCap;
+ }
+ miterLimit = this.stroke_.getMiterLimit();
+ if (miterLimit === undefined) {
+ miterLimit = ol.render.canvas.defaultMiterLimit;
+ }
+ }
+
+ var size = 2 * (this.radius_ + strokeWidth) + 1;
+
+ /** @type {ol.RegularShapeRenderOptions} */
+ var renderOptions = {
+ strokeStyle: strokeStyle,
+ strokeWidth: strokeWidth,
+ size: size,
+ lineCap: lineCap,
+ lineDash: lineDash,
+ lineJoin: lineJoin,
+ miterLimit: miterLimit
+ };
+
+ if (atlasManager === undefined) {
+ // no atlas manager is used, create a new canvas
+ var context = ol.dom.createCanvasContext2D(size, size);
+ this.canvas_ = context.canvas;
+
+ // canvas.width and height are rounded to the closest integer
+ size = this.canvas_.width;
+ imageSize = size;
+
+ this.draw_(renderOptions, context, 0, 0);
+
+ this.createHitDetectionCanvas_(renderOptions);
+ } else {
+ // an atlas manager is used, add the symbol to an atlas
+ size = Math.round(size);
+
+ var hasCustomHitDetectionImage = !this.fill_;
+ var renderHitDetectionCallback;
+ if (hasCustomHitDetectionImage) {
+ // render the hit-detection image into a separate atlas image
+ renderHitDetectionCallback =
+ this.drawHitDetectionCanvas_.bind(this, renderOptions);
+ }
+
+ var id = this.getChecksum();
+ var info = atlasManager.add(
+ id, size, size, this.draw_.bind(this, renderOptions),
+ renderHitDetectionCallback);
+ ol.DEBUG && console.assert(info, 'shape size is too large');
+
+ this.canvas_ = info.image;
+ this.origin_ = [info.offsetX, info.offsetY];
+ imageSize = info.image.width;
+
+ if (hasCustomHitDetectionImage) {
+ this.hitDetectionCanvas_ = info.hitImage;
+ this.hitDetectionImageSize_ =
+ [info.hitImage.width, info.hitImage.height];
+ } else {
+ this.hitDetectionCanvas_ = this.canvas_;
+ this.hitDetectionImageSize_ = [imageSize, imageSize];
+ }
+ }
+
+ this.anchor_ = [size / 2, size / 2];
+ this.size_ = [size, size];
+ this.imageSize_ = [imageSize, imageSize];
+};
+
+
+/**
+ * @private
+ * @param {ol.RegularShapeRenderOptions} renderOptions Render options.
+ * @param {CanvasRenderingContext2D} context The rendering context.
+ * @param {number} x The origin for the symbol (x).
+ * @param {number} y The origin for the symbol (y).
+ */
+ol.style.RegularShape.prototype.draw_ = function(renderOptions, context, x, y) {
+ var i, angle0, radiusC;
+ // reset transform
+ context.setTransform(1, 0, 0, 1, 0, 0);
+
+ // then move to (x, y)
+ context.translate(x, y);
+
+ context.beginPath();
+
+ if (this.points_ === Infinity) {
+ context.arc(
+ renderOptions.size / 2, renderOptions.size / 2,
+ this.radius_, 0, 2 * Math.PI, true);
+ } else {
+ if (this.radius2_ !== this.radius_) {
+ this.points_ = 2 * this.points_;
+ }
+ for (i = 0; i <= this.points_; i++) {
+ angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
+ radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
+ context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0),
+ renderOptions.size / 2 + radiusC * Math.sin(angle0));
+ }
+ }
+
+
+ if (this.fill_) {
+ context.fillStyle = ol.colorlike.asColorLike(this.fill_.getColor());
+ context.fill();
+ }
+ if (this.stroke_) {
+ context.strokeStyle = renderOptions.strokeStyle;
+ context.lineWidth = renderOptions.strokeWidth;
+ if (renderOptions.lineDash) {
+ context.setLineDash(renderOptions.lineDash);
+ }
+ context.lineCap = renderOptions.lineCap;
+ context.lineJoin = renderOptions.lineJoin;
+ context.miterLimit = renderOptions.miterLimit;
+ context.stroke();
+ }
+ context.closePath();
+};
+
+
+/**
+ * @private
+ * @param {ol.RegularShapeRenderOptions} renderOptions Render options.
+ */
+ol.style.RegularShape.prototype.createHitDetectionCanvas_ = function(renderOptions) {
+ this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
+ if (this.fill_) {
+ this.hitDetectionCanvas_ = this.canvas_;
+ return;
+ }
+
+ // if no fill style is set, create an extra hit-detection image with a
+ // default fill style
+ var context = ol.dom.createCanvasContext2D(renderOptions.size, renderOptions.size);
+ this.hitDetectionCanvas_ = context.canvas;
+
+ this.drawHitDetectionCanvas_(renderOptions, context, 0, 0);
+};
+
+
+/**
+ * @private
+ * @param {ol.RegularShapeRenderOptions} renderOptions Render options.
+ * @param {CanvasRenderingContext2D} context The context.
+ * @param {number} x The origin for the symbol (x).
+ * @param {number} y The origin for the symbol (y).
+ */
+ol.style.RegularShape.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) {
+ // reset transform
+ context.setTransform(1, 0, 0, 1, 0, 0);
+
+ // then move to (x, y)
+ context.translate(x, y);
+
+ context.beginPath();
+
+ if (this.points_ === Infinity) {
+ context.arc(
+ renderOptions.size / 2, renderOptions.size / 2,
+ this.radius_, 0, 2 * Math.PI, true);
+ } else {
+ if (this.radius2_ !== this.radius_) {
+ this.points_ = 2 * this.points_;
+ }
+ var i, radiusC, angle0;
+ for (i = 0; i <= this.points_; i++) {
+ angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
+ radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
+ context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0),
+ renderOptions.size / 2 + radiusC * Math.sin(angle0));
+ }
+ }
+
+ context.fillStyle = ol.render.canvas.defaultFillStyle;
+ context.fill();
+ if (this.stroke_) {
+ context.strokeStyle = renderOptions.strokeStyle;
+ context.lineWidth = renderOptions.strokeWidth;
+ if (renderOptions.lineDash) {
+ context.setLineDash(renderOptions.lineDash);
+ }
+ context.stroke();
+ }
+ context.closePath();
+};
+
+
+/**
+ * @return {string} The checksum.
+ */
+ol.style.RegularShape.prototype.getChecksum = function() {
+ var strokeChecksum = this.stroke_ ?
+ this.stroke_.getChecksum() : '-';
+ var fillChecksum = this.fill_ ?
+ this.fill_.getChecksum() : '-';
+
+ var recalculate = !this.checksums_ ||
+ (strokeChecksum != this.checksums_[1] ||
+ fillChecksum != this.checksums_[2] ||
+ this.radius_ != this.checksums_[3] ||
+ this.radius2_ != this.checksums_[4] ||
+ this.angle_ != this.checksums_[5] ||
+ this.points_ != this.checksums_[6]);
+
+ if (recalculate) {
+ var checksum = 'r' + strokeChecksum + fillChecksum +
+ (this.radius_ !== undefined ? this.radius_.toString() : '-') +
+ (this.radius2_ !== undefined ? this.radius2_.toString() : '-') +
+ (this.angle_ !== undefined ? this.angle_.toString() : '-') +
+ (this.points_ !== undefined ? this.points_.toString() : '-');
+ this.checksums_ = [checksum, strokeChecksum, fillChecksum,
+ this.radius_, this.radius2_, this.angle_, this.points_];
+ }
+
+ return this.checksums_[0];
+};
+
+goog.provide('ol.style.Circle');
+
+goog.require('ol');
+goog.require('ol.style.RegularShape');
+
+
+/**
+ * @classdesc
+ * Set circle style for vector features.
+ *
+ * @constructor
+ * @param {olx.style.CircleOptions=} opt_options Options.
+ * @extends {ol.style.RegularShape}
+ * @api
+ */
+ol.style.Circle = function(opt_options) {
+
+ var options = opt_options || {};
+
+ ol.style.RegularShape.call(this, {
+ points: Infinity,
+ fill: options.fill,
+ radius: options.radius,
+ snapToPixel: options.snapToPixel,
+ stroke: options.stroke,
+ atlasManager: options.atlasManager
+ });
+
+};
+ol.inherits(ol.style.Circle, ol.style.RegularShape);
+
+
+/**
+ * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too.
+ * @return {ol.style.Circle} The cloned style.
+ * @api
+ */
+ol.style.Circle.prototype.clone = function() {
+ var style = new ol.style.Circle({
+ fill: this.getFill() ? this.getFill().clone() : undefined,
+ stroke: this.getStroke() ? this.getStroke().clone() : undefined,
+ radius: this.getRadius(),
+ snapToPixel: this.getSnapToPixel(),
+ atlasManager: this.atlasManager_
+ });
+ style.setOpacity(this.getOpacity());
+ style.setScale(this.getScale());
+ return style;
+};
+
+
+/**
+ * Set the circle radius.
+ *
+ * @param {number} radius Circle radius.
+ * @api
+ */
+ol.style.Circle.prototype.setRadius = function(radius) {
+ this.radius_ = radius;
+ this.render_(this.atlasManager_);
+};
+
+goog.provide('ol.style.Fill');
+
+goog.require('ol');
+goog.require('ol.color');
+
+
+/**
+ * @classdesc
+ * Set fill style for vector features.
+ *
+ * @constructor
+ * @param {olx.style.FillOptions=} opt_options Options.
+ * @api
+ */
+ol.style.Fill = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {ol.Color|ol.ColorLike}
+ */
+ this.color_ = options.color !== undefined ? options.color : null;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Clones the style. The color is not cloned if it is an {@link ol.ColorLike}.
+ * @return {ol.style.Fill} The cloned style.
+ * @api
+ */
+ol.style.Fill.prototype.clone = function() {
+ var color = this.getColor();
+ return new ol.style.Fill({
+ color: (color && color.slice) ? color.slice() : color || undefined
+ });
+};
+
+
+/**
+ * Get the fill color.
+ * @return {ol.Color|ol.ColorLike} Color.
+ * @api
+ */
+ol.style.Fill.prototype.getColor = function() {
+ return this.color_;
+};
+
+
+/**
+ * Set the color.
+ *
+ * @param {ol.Color|ol.ColorLike} color Color.
+ * @api
+ */
+ol.style.Fill.prototype.setColor = function(color) {
+ this.color_ = color;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * @return {string} The checksum.
+ */
+ol.style.Fill.prototype.getChecksum = function() {
+ if (this.checksum_ === undefined) {
+ if (
+ this.color_ instanceof CanvasPattern ||
+ this.color_ instanceof CanvasGradient
+ ) {
+ this.checksum_ = ol.getUid(this.color_).toString();
+ } else {
+ this.checksum_ = 'f' + (this.color_ ?
+ ol.color.asString(this.color_) : '-');
+ }
+ }
+
+ return this.checksum_;
+};
+
+goog.provide('ol.style.Stroke');
+
+goog.require('ol');
+
+
+/**
+ * @classdesc
+ * Set stroke style for vector features.
+ * Note that the defaults given are the Canvas defaults, which will be used if
+ * option is not defined. The `get` functions return whatever was entered in
+ * the options; they will not return the default.
+ *
+ * @constructor
+ * @param {olx.style.StrokeOptions=} opt_options Options.
+ * @api
+ */
+ol.style.Stroke = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {ol.Color|ol.ColorLike}
+ */
+ this.color_ = options.color !== undefined ? options.color : null;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.lineCap_ = options.lineCap;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.lineJoin_ = options.lineJoin;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.miterLimit_ = options.miterLimit;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.width_ = options.width;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Clones the style.
+ * @return {ol.style.Stroke} The cloned style.
+ * @api
+ */
+ol.style.Stroke.prototype.clone = function() {
+ var color = this.getColor();
+ return new ol.style.Stroke({
+ color: (color && color.slice) ? color.slice() : color || undefined,
+ lineCap: this.getLineCap(),
+ lineDash: this.getLineDash() ? this.getLineDash().slice() : undefined,
+ lineJoin: this.getLineJoin(),
+ miterLimit: this.getMiterLimit(),
+ width: this.getWidth()
+ });
+};
+
+
+/**
+ * Get the stroke color.
+ * @return {ol.Color|ol.ColorLike} Color.
+ * @api
+ */
+ol.style.Stroke.prototype.getColor = function() {
+ return this.color_;
+};
+
+
+/**
+ * Get the line cap type for the stroke.
+ * @return {string|undefined} Line cap.
+ * @api
+ */
+ol.style.Stroke.prototype.getLineCap = function() {
+ return this.lineCap_;
+};
+
+
+/**
+ * Get the line dash style for the stroke.
+ * @return {Array.<number>} Line dash.
+ * @api
+ */
+ol.style.Stroke.prototype.getLineDash = function() {
+ return this.lineDash_;
+};
+
+
+/**
+ * Get the line join type for the stroke.
+ * @return {string|undefined} Line join.
+ * @api
+ */
+ol.style.Stroke.prototype.getLineJoin = function() {
+ return this.lineJoin_;
+};
+
+
+/**
+ * Get the miter limit for the stroke.
+ * @return {number|undefined} Miter limit.
+ * @api
+ */
+ol.style.Stroke.prototype.getMiterLimit = function() {
+ return this.miterLimit_;
+};
+
+
+/**
+ * Get the stroke width.
+ * @return {number|undefined} Width.
+ * @api
+ */
+ol.style.Stroke.prototype.getWidth = function() {
+ return this.width_;
+};
+
+
+/**
+ * Set the color.
+ *
+ * @param {ol.Color|ol.ColorLike} color Color.
+ * @api
+ */
+ol.style.Stroke.prototype.setColor = function(color) {
+ this.color_ = color;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Set the line cap.
+ *
+ * @param {string|undefined} lineCap Line cap.
+ * @api
+ */
+ol.style.Stroke.prototype.setLineCap = function(lineCap) {
+ this.lineCap_ = lineCap;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Set the line dash.
+ *
+ * Please note that Internet Explorer 10 and lower [do not support][mdn] the
+ * `setLineDash` method on the `CanvasRenderingContext2D` and therefore this
+ * property will have no visual effect in these browsers.
+ *
+ * [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility
+ *
+ * @param {Array.<number>} lineDash Line dash.
+ * @api
+ */
+ol.style.Stroke.prototype.setLineDash = function(lineDash) {
+ this.lineDash_ = lineDash;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Set the line join.
+ *
+ * @param {string|undefined} lineJoin Line join.
+ * @api
+ */
+ol.style.Stroke.prototype.setLineJoin = function(lineJoin) {
+ this.lineJoin_ = lineJoin;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Set the miter limit.
+ *
+ * @param {number|undefined} miterLimit Miter limit.
+ * @api
+ */
+ol.style.Stroke.prototype.setMiterLimit = function(miterLimit) {
+ this.miterLimit_ = miterLimit;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * Set the width.
+ *
+ * @param {number|undefined} width Width.
+ * @api
+ */
+ol.style.Stroke.prototype.setWidth = function(width) {
+ this.width_ = width;
+ this.checksum_ = undefined;
+};
+
+
+/**
+ * @return {string} The checksum.
+ */
+ol.style.Stroke.prototype.getChecksum = function() {
+ if (this.checksum_ === undefined) {
+ this.checksum_ = 's';
+ if (this.color_) {
+ if (typeof this.color_ === 'string') {
+ this.checksum_ += this.color_;
+ } else {
+ this.checksum_ += ol.getUid(this.color_).toString();
+ }
+ } else {
+ this.checksum_ += '-';
+ }
+ this.checksum_ += ',' +
+ (this.lineCap_ !== undefined ?
+ this.lineCap_.toString() : '-') + ',' +
+ (this.lineDash_ ?
+ this.lineDash_.toString() : '-') + ',' +
+ (this.lineJoin_ !== undefined ?
+ this.lineJoin_ : '-') + ',' +
+ (this.miterLimit_ !== undefined ?
+ this.miterLimit_.toString() : '-') + ',' +
+ (this.width_ !== undefined ?
+ this.width_.toString() : '-');
+ }
+
+ return this.checksum_;
+};
+
+goog.provide('ol.style.Style');
+
+goog.require('ol.asserts');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.style.Circle');
+goog.require('ol.style.Fill');
+goog.require('ol.style.Stroke');
+
+
+/**
+ * @classdesc
+ * Container for vector feature rendering styles. Any changes made to the style
+ * or its children through `set*()` methods will not take effect until the
+ * feature or layer that uses the style is re-rendered.
+ *
+ * @constructor
+ * @struct
+ * @param {olx.style.StyleOptions=} opt_options Style options.
+ * @api
+ */
+ol.style.Style = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {string|ol.geom.Geometry|ol.StyleGeometryFunction}
+ */
+ this.geometry_ = null;
+
+ /**
+ * @private
+ * @type {!ol.StyleGeometryFunction}
+ */
+ this.geometryFunction_ = ol.style.Style.defaultGeometryFunction;
+
+ if (options.geometry !== undefined) {
+ this.setGeometry(options.geometry);
+ }
+
+ /**
+ * @private
+ * @type {ol.style.Fill}
+ */
+ this.fill_ = options.fill !== undefined ? options.fill : null;
+
+ /**
+ * @private
+ * @type {ol.style.Image}
+ */
+ this.image_ = options.image !== undefined ? options.image : null;
+
+ /**
+ * @private
+ * @type {ol.style.Stroke}
+ */
+ this.stroke_ = options.stroke !== undefined ? options.stroke : null;
+
+ /**
+ * @private
+ * @type {ol.style.Text}
+ */
+ this.text_ = options.text !== undefined ? options.text : null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.zIndex_ = options.zIndex;
+
+};
+
+
+/**
+ * Clones the style.
+ * @return {ol.style.Style} The cloned style.
+ * @api
+ */
+ol.style.Style.prototype.clone = function() {
+ var geometry = this.getGeometry();
+ if (geometry && geometry.clone) {
+ geometry = geometry.clone();
+ }
+ return new ol.style.Style({
+ geometry: geometry,
+ fill: this.getFill() ? this.getFill().clone() : undefined,
+ image: this.getImage() ? this.getImage().clone() : undefined,
+ stroke: this.getStroke() ? this.getStroke().clone() : undefined,
+ text: this.getText() ? this.getText().clone() : undefined,
+ zIndex: this.getZIndex()
+ });
+};
+
+
+/**
+ * Get the geometry to be rendered.
+ * @return {string|ol.geom.Geometry|ol.StyleGeometryFunction}
+ * Feature property or geometry or function that returns the geometry that will
+ * be rendered with this style.
+ * @api
+ */
+ol.style.Style.prototype.getGeometry = function() {
+ return this.geometry_;
+};
+
+
+/**
+ * Get the function used to generate a geometry for rendering.
+ * @return {!ol.StyleGeometryFunction} Function that is called with a feature
+ * and returns the geometry to render instead of the feature's geometry.
+ * @api
+ */
+ol.style.Style.prototype.getGeometryFunction = function() {
+ return this.geometryFunction_;
+};
+
+
+/**
+ * Get the fill style.
+ * @return {ol.style.Fill} Fill style.
+ * @api
+ */
+ol.style.Style.prototype.getFill = function() {
+ return this.fill_;
+};
+
+
+/**
+ * Set the fill style.
+ * @param {ol.style.Fill} fill Fill style.
+ * @api
+ */
+ol.style.Style.prototype.setFill = function(fill) {
+ this.fill_ = fill;
+};
+
+
+/**
+ * Get the image style.
+ * @return {ol.style.Image} Image style.
+ * @api
+ */
+ol.style.Style.prototype.getImage = function() {
+ return this.image_;
+};
+
+
+/**
+ * Set the image style.
+ * @param {ol.style.Image} image Image style.
+ * @api
+ */
+ol.style.Style.prototype.setImage = function(image) {
+ this.image_ = image;
+};
+
+
+/**
+ * Get the stroke style.
+ * @return {ol.style.Stroke} Stroke style.
+ * @api
+ */
+ol.style.Style.prototype.getStroke = function() {
+ return this.stroke_;
+};
+
+
+/**
+ * Set the stroke style.
+ * @param {ol.style.Stroke} stroke Stroke style.
+ * @api
+ */
+ol.style.Style.prototype.setStroke = function(stroke) {
+ this.stroke_ = stroke;
+};
+
+
+/**
+ * Get the text style.
+ * @return {ol.style.Text} Text style.
+ * @api
+ */
+ol.style.Style.prototype.getText = function() {
+ return this.text_;
+};
+
+
+/**
+ * Set the text style.
+ * @param {ol.style.Text} text Text style.
+ * @api
+ */
+ol.style.Style.prototype.setText = function(text) {
+ this.text_ = text;
+};
+
+
+/**
+ * Get the z-index for the style.
+ * @return {number|undefined} ZIndex.
+ * @api
+ */
+ol.style.Style.prototype.getZIndex = function() {
+ return this.zIndex_;
+};
+
+
+/**
+ * Set a geometry that is rendered instead of the feature's geometry.
+ *
+ * @param {string|ol.geom.Geometry|ol.StyleGeometryFunction} geometry
+ * Feature property or geometry or function returning a geometry to render
+ * for this style.
+ * @api
+ */
+ol.style.Style.prototype.setGeometry = function(geometry) {
+ if (typeof geometry === 'function') {
+ this.geometryFunction_ = geometry;
+ } else if (typeof geometry === 'string') {
+ this.geometryFunction_ = function(feature) {
+ return /** @type {ol.geom.Geometry} */ (feature.get(geometry));
+ };
+ } else if (!geometry) {
+ this.geometryFunction_ = ol.style.Style.defaultGeometryFunction;
+ } else if (geometry !== undefined) {
+ this.geometryFunction_ = function() {
+ return /** @type {ol.geom.Geometry} */ (geometry);
+ };
+ }
+ this.geometry_ = geometry;
+};
+
+
+/**
+ * Set the z-index.
+ *
+ * @param {number|undefined} zIndex ZIndex.
+ * @api
+ */
+ol.style.Style.prototype.setZIndex = function(zIndex) {
+ this.zIndex_ = zIndex;
+};
+
+
+/**
+ * Convert the provided object into a style function. Functions passed through
+ * unchanged. Arrays of ol.style.Style or single style objects wrapped in a
+ * new style function.
+ * @param {ol.StyleFunction|Array.<ol.style.Style>|ol.style.Style} obj
+ * A style function, a single style, or an array of styles.
+ * @return {ol.StyleFunction} A style function.
+ */
+ol.style.Style.createFunction = function(obj) {
+ var styleFunction;
+
+ if (typeof obj === 'function') {
+ styleFunction = obj;
+ } else {
+ /**
+ * @type {Array.<ol.style.Style>}
+ */
+ var styles;
+ if (Array.isArray(obj)) {
+ styles = obj;
+ } else {
+ ol.asserts.assert(obj instanceof ol.style.Style,
+ 41); // Expected an `ol.style.Style` or an array of `ol.style.Style`
+ styles = [obj];
+ }
+ styleFunction = function() {
+ return styles;
+ };
+ }
+ return styleFunction;
+};
+
+
+/**
+ * @type {Array.<ol.style.Style>}
+ * @private
+ */
+ol.style.Style.default_ = null;
+
+
+/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {number} resolution Resolution.
+ * @return {Array.<ol.style.Style>} Style.
+ */
+ol.style.Style.defaultFunction = function(feature, resolution) {
+ // We don't use an immediately-invoked function
+ // and a closure so we don't get an error at script evaluation time in
+ // browsers that do not support Canvas. (ol.style.Circle does
+ // canvas.getContext('2d') at construction time, which will cause an.error
+ // in such browsers.)
+ if (!ol.style.Style.default_) {
+ var fill = new ol.style.Fill({
+ color: 'rgba(255,255,255,0.4)'
+ });
+ var stroke = new ol.style.Stroke({
+ color: '#3399CC',
+ width: 1.25
+ });
+ ol.style.Style.default_ = [
+ new ol.style.Style({
+ image: new ol.style.Circle({
+ fill: fill,
+ stroke: stroke,
+ radius: 5
+ }),
+ fill: fill,
+ stroke: stroke
+ })
+ ];
+ }
+ return ol.style.Style.default_;
+};
+
+
+/**
+ * Default styles for editing features.
+ * @return {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} Styles
+ */
+ol.style.Style.createDefaultEditing = function() {
+ /** @type {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} */
+ var styles = {};
+ var white = [255, 255, 255, 1];
+ var blue = [0, 153, 255, 1];
+ var width = 3;
+ styles[ol.geom.GeometryType.POLYGON] = [
+ new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: [255, 255, 255, 0.5]
+ })
+ })
+ ];
+ styles[ol.geom.GeometryType.MULTI_POLYGON] =
+ styles[ol.geom.GeometryType.POLYGON];
+
+ styles[ol.geom.GeometryType.LINE_STRING] = [
+ new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: white,
+ width: width + 2
+ })
+ }),
+ new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: blue,
+ width: width
+ })
+ })
+ ];
+ styles[ol.geom.GeometryType.MULTI_LINE_STRING] =
+ styles[ol.geom.GeometryType.LINE_STRING];
+
+ styles[ol.geom.GeometryType.CIRCLE] =
+ styles[ol.geom.GeometryType.POLYGON].concat(
+ styles[ol.geom.GeometryType.LINE_STRING]
+ );
+
+
+ styles[ol.geom.GeometryType.POINT] = [
+ new ol.style.Style({
+ image: new ol.style.Circle({
+ radius: width * 2,
+ fill: new ol.style.Fill({
+ color: blue
+ }),
+ stroke: new ol.style.Stroke({
+ color: white,
+ width: width / 2
+ })
+ }),
+ zIndex: Infinity
+ })
+ ];
+ styles[ol.geom.GeometryType.MULTI_POINT] =
+ styles[ol.geom.GeometryType.POINT];
+
+ styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] =
+ styles[ol.geom.GeometryType.POLYGON].concat(
+ styles[ol.geom.GeometryType.LINE_STRING],
+ styles[ol.geom.GeometryType.POINT]
+ );
+
+ return styles;
+};
+
+
+/**
+ * Function that is called with a feature and returns its default geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature to get the geometry
+ * for.
+ * @return {ol.geom.Geometry|ol.render.Feature|undefined} Geometry to render.
+ */
+ol.style.Style.defaultGeometryFunction = function(feature) {
+ return feature.getGeometry();
+};
+
+goog.provide('ol.layer.Vector');
+
+goog.require('ol');
+goog.require('ol.layer.Layer');
+goog.require('ol.obj');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * Vector data that is rendered client-side.
+ * Note that any property set in the options is set as a {@link ol.Object}
+ * property on the layer object; for example, setting `title: 'My Title'` in the
+ * options means that `title` is observable, and has get/set accessors.
+ *
+ * @constructor
+ * @extends {ol.layer.Layer}
+ * @fires ol.render.Event
+ * @param {olx.layer.VectorOptions=} opt_options Options.
+ * @api stable
+ */
+ol.layer.Vector = function(opt_options) {
+
+ var options = opt_options ?
+ opt_options : /** @type {olx.layer.VectorOptions} */ ({});
+
+ ol.DEBUG && console.assert(
+ options.renderOrder === undefined || !options.renderOrder ||
+ typeof options.renderOrder === 'function',
+ 'renderOrder must be a comparator function');
+
+ var baseOptions = ol.obj.assign({}, options);
+
+ delete baseOptions.style;
+ delete baseOptions.renderBuffer;
+ delete baseOptions.updateWhileAnimating;
+ delete baseOptions.updateWhileInteracting;
+ ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (baseOptions));
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.renderBuffer_ = options.renderBuffer !== undefined ?
+ options.renderBuffer : 100;
+
+ /**
+ * User provided style.
+ * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction}
+ * @private
+ */
+ this.style_ = null;
+
+ /**
+ * Style function for use within the library.
+ * @type {ol.StyleFunction|undefined}
+ * @private
+ */
+ this.styleFunction_ = undefined;
+
+ this.setStyle(options.style);
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ?
+ options.updateWhileAnimating : false;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ?
+ options.updateWhileInteracting : false;
+
+};
+ol.inherits(ol.layer.Vector, ol.layer.Layer);
+
+
+/**
+ * @return {number|undefined} Render buffer.
+ */
+ol.layer.Vector.prototype.getRenderBuffer = function() {
+ return this.renderBuffer_;
+};
+
+
+/**
+ * @return {function(ol.Feature, ol.Feature): number|null|undefined} Render
+ * order.
+ */
+ol.layer.Vector.prototype.getRenderOrder = function() {
+ return /** @type {function(ol.Feature, ol.Feature):number|null|undefined} */ (
+ this.get(ol.layer.Vector.Property.RENDER_ORDER));
+};
+
+
+/**
+ * Return the associated {@link ol.source.Vector vectorsource} of the layer.
+ * @function
+ * @return {ol.source.Vector} Source.
+ * @api stable
+ */
+ol.layer.Vector.prototype.getSource;
+
+
+/**
+ * Get the style for features. This returns whatever was passed to the `style`
+ * option at construction or to the `setStyle` method.
+ * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction}
+ * Layer style.
+ * @api stable
+ */
+ol.layer.Vector.prototype.getStyle = function() {
+ return this.style_;
+};
+
+
+/**
+ * Get the style function.
+ * @return {ol.StyleFunction|undefined} Layer style function.
+ * @api stable
+ */
+ol.layer.Vector.prototype.getStyleFunction = function() {
+ return this.styleFunction_;
+};
+
+
+/**
+ * @return {boolean} Whether the rendered layer should be updated while
+ * animating.
+ */
+ol.layer.Vector.prototype.getUpdateWhileAnimating = function() {
+ return this.updateWhileAnimating_;
+};
+
+
+/**
+ * @return {boolean} Whether the rendered layer should be updated while
+ * interacting.
+ */
+ol.layer.Vector.prototype.getUpdateWhileInteracting = function() {
+ return this.updateWhileInteracting_;
+};
+
+
+/**
+ * @param {function(ol.Feature, ol.Feature):number|null|undefined} renderOrder
+ * Render order.
+ */
+ol.layer.Vector.prototype.setRenderOrder = function(renderOrder) {
+ ol.DEBUG && console.assert(
+ renderOrder === undefined || !renderOrder ||
+ typeof renderOrder === 'function',
+ 'renderOrder must be a comparator function');
+ this.set(ol.layer.Vector.Property.RENDER_ORDER, renderOrder);
+};
+
+
+/**
+ * Set the style for features. This can be a single style object, an array
+ * of styles, or a function that takes a feature and resolution and returns
+ * an array of styles. If it is `undefined` the default style is used. If
+ * it is `null` the layer has no style (a `null` style), so only features
+ * that have their own styles will be rendered in the layer. See
+ * {@link ol.style} for information on the default style.
+ * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|null|undefined}
+ * style Layer style.
+ * @api stable
+ */
+ol.layer.Vector.prototype.setStyle = function(style) {
+ this.style_ = style !== undefined ? style : ol.style.Style.defaultFunction;
+ this.styleFunction_ = style === null ?
+ undefined : ol.style.Style.createFunction(this.style_);
+ this.changed();
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.layer.Vector.Property = {
+ RENDER_ORDER: 'renderOrder'
+};
+
+goog.provide('ol.layer.VectorTile');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.layer.Tile');
+goog.require('ol.layer.Vector');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * Layer for vector tile data that is rendered client-side.
+ * Note that any property set in the options is set as a {@link ol.Object}
+ * property on the layer object; for example, setting `title: 'My Title'` in the
+ * options means that `title` is observable, and has get/set accessors.
+ *
+ * @constructor
+ * @extends {ol.layer.Vector}
+ * @param {olx.layer.VectorTileOptions=} opt_options Options.
+ * @api
+ */
+ol.layer.VectorTile = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+
+ var baseOptions = ol.obj.assign({}, options);
+
+ delete baseOptions.preload;
+ delete baseOptions.useInterimTilesOnError;
+ ol.layer.Vector.call(this, /** @type {olx.layer.VectorOptions} */ (baseOptions));
+
+ this.setPreload(options.preload ? options.preload : 0);
+ this.setUseInterimTilesOnError(options.useInterimTilesOnError ?
+ options.useInterimTilesOnError : true);
+
+ ol.asserts.assert(options.renderMode == undefined ||
+ options.renderMode == ol.layer.VectorTile.RenderType.IMAGE ||
+ options.renderMode == ol.layer.VectorTile.RenderType.HYBRID ||
+ options.renderMode == ol.layer.VectorTile.RenderType.VECTOR,
+ 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`
+
+ /**
+ * @private
+ * @type {ol.layer.VectorTile.RenderType|string}
+ */
+ this.renderMode_ = options.renderMode || ol.layer.VectorTile.RenderType.HYBRID;
+
+};
+ol.inherits(ol.layer.VectorTile, ol.layer.Vector);
+
+
+/**
+ * Return the level as number to which we will preload tiles up to.
+ * @return {number} The level to preload tiles up to.
+ * @observable
+ * @api
+ */
+ol.layer.VectorTile.prototype.getPreload = function() {
+ return /** @type {number} */ (this.get(ol.layer.VectorTile.Property.PRELOAD));
+};
+
+
+/**
+ * @return {ol.layer.VectorTile.RenderType|string} The render mode.
+ */
+ol.layer.VectorTile.prototype.getRenderMode = function() {
+ return this.renderMode_;
+};
+
+
+/**
+ * Whether we use interim tiles on error.
+ * @return {boolean} Use interim tiles on error.
+ * @observable
+ * @api
+ */
+ol.layer.VectorTile.prototype.getUseInterimTilesOnError = function() {
+ return /** @type {boolean} */ (
+ this.get(ol.layer.VectorTile.Property.USE_INTERIM_TILES_ON_ERROR));
+};
+
+
+/**
+ * Set the level as number to which we will preload tiles up to.
+ * @param {number} preload The level to preload tiles up to.
+ * @observable
+ * @api
+ */
+ol.layer.VectorTile.prototype.setPreload = function(preload) {
+ this.set(ol.layer.Tile.Property.PRELOAD, preload);
+};
+
+
+/**
+ * Set whether we use interim tiles on error.
+ * @param {boolean} useInterimTilesOnError Use interim tiles on error.
+ * @observable
+ * @api
+ */
+ol.layer.VectorTile.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) {
+ this.set(
+ ol.layer.Tile.Property.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.layer.VectorTile.Property = {
+ PRELOAD: 'preload',
+ USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError'
+};
+
+
+/**
+ * @enum {string}
+ * Render mode for vector tiles:
+ * * `'image'`: Vector tiles are rendered as images. Great performance, but
+ * point symbols and texts are always rotated with the view and pixels are
+ * scaled during zoom animations.
+ * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
+ * are scaled during zoom animations. Point symbols and texts are accurately
+ * rendered as vectors and can stay upright on rotated views.
+ * * `'vector'`: Vector tiles are rendered as vectors. Most accurate rendering
+ * even during animations, but slower performance than the other options.
+ * @api
+ */
+ol.layer.VectorTile.RenderType = {
+ IMAGE: 'image',
+ HYBRID: 'hybrid',
+ VECTOR: 'vector'
+};
+
+goog.provide('ol.render.VectorContext');
+
+
+/**
+ * Context for drawing geometries. A vector context is available on render
+ * events and does not need to be constructed directly.
+ * @constructor
+ * @struct
+ * @api
+ */
+ol.render.VectorContext = function() {
+};
+
+
+/**
+ * Render a geometry.
+ *
+ * @abstract
+ * @param {ol.geom.Geometry} geometry The geometry to render.
+ */
+ol.render.VectorContext.prototype.drawGeometry = function(geometry) {};
+
+
+/**
+ * Set the rendering style.
+ *
+ * @abstract
+ * @param {ol.style.Style} style The rendering style.
+ */
+ol.render.VectorContext.prototype.setStyle = function(style) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.Circle} circleGeometry Circle geometry.
+ * @param {ol.Feature} feature Feature,
+ */
+ol.render.VectorContext.prototype.drawCircle = function(circleGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.Feature} feature Feature.
+ * @param {ol.style.Style} style Style.
+ */
+ol.render.VectorContext.prototype.drawFeature = function(feature, style) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.GeometryCollection} geometryCollectionGeometry Geometry
+ * collection.
+ * @param {ol.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawGeometryCollection = function(geometryCollectionGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.LineString|ol.render.Feature} lineStringGeometry Line
+ * string geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawLineString = function(lineStringGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.MultiLineString|ol.render.Feature} multiLineStringGeometry
+ * MultiLineString geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.MultiPoint|ol.render.Feature} multiPointGeometry MultiPoint
+ * geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawMultiPoint = function(multiPointGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.MultiPolygon} multiPolygonGeometry MultiPolygon geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.Point|ol.render.Feature} pointGeometry Point geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawPoint = function(pointGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.Polygon|ol.render.Feature} polygonGeometry Polygon
+ * geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawPolygon = function(polygonGeometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.VectorContext.prototype.drawText = function(flatCoordinates, offset, end, stride, geometry, feature) {};
+
+
+/**
+ * @abstract
+ * @param {ol.style.Fill} fillStyle Fill style.
+ * @param {ol.style.Stroke} strokeStyle Stroke style.
+ */
+ol.render.VectorContext.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {};
+
+
+/**
+ * @abstract
+ * @param {ol.style.Image} imageStyle Image style.
+ */
+ol.render.VectorContext.prototype.setImageStyle = function(imageStyle) {};
+
+
+/**
+ * @abstract
+ * @param {ol.style.Text} textStyle Text style.
+ */
+ol.render.VectorContext.prototype.setTextStyle = function(textStyle) {};
+
+// FIXME test, especially polygons with holes and multipolygons
+// FIXME need to handle large thick features (where pixel size matters)
+// FIXME add offset and end to ol.geom.flat.transform.transform2D?
+
+goog.provide('ol.render.canvas.Immediate');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.colorlike');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.has');
+goog.require('ol.render.VectorContext');
+goog.require('ol.render.canvas');
+goog.require('ol.transform');
+
+
+/**
+ * @classdesc
+ * A concrete subclass of {@link ol.render.VectorContext} that implements
+ * direct rendering of features and geometries to an HTML5 Canvas context.
+ * Instances of this class are created internally by the library and
+ * provided to application code as vectorContext member of the
+ * {@link ol.render.Event} object associated with postcompose, precompose and
+ * render events emitted by layers and maps.
+ *
+ * @constructor
+ * @extends {ol.render.VectorContext}
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Transform} transform Transform.
+ * @param {number} viewRotation View rotation.
+ * @struct
+ */
+ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform, viewRotation) {
+ ol.render.VectorContext.call(this);
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.context_ = context;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelRatio_ = pixelRatio;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.extent_ = extent;
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.transform_ = transform;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.viewRotation_ = viewRotation;
+
+ /**
+ * @private
+ * @type {?ol.CanvasFillState}
+ */
+ this.contextFillState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasStrokeState}
+ */
+ this.contextStrokeState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasTextState}
+ */
+ this.contextTextState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasFillState}
+ */
+ this.fillState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasStrokeState}
+ */
+ this.strokeState_ = null;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement|HTMLVideoElement|Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageAnchorX_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageAnchorY_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageHeight_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageOpacity_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageOriginX_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageOriginY_ = 0;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.imageRotateWithView_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageRotation_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageScale_ = 0;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.imageSnapToPixel_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.imageWidth_ = 0;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.text_ = '';
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textOffsetX_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textOffsetY_ = 0;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.textRotateWithView_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textRotation_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textScale_ = 0;
+
+ /**
+ * @private
+ * @type {?ol.CanvasFillState}
+ */
+ this.textFillState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasStrokeState}
+ */
+ this.textStrokeState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasTextState}
+ */
+ this.textState_ = null;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.pixelCoordinates_ = [];
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.tmpLocalTransform_ = ol.transform.create();
+
+};
+ol.inherits(ol.render.canvas.Immediate, ol.render.VectorContext);
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @private
+ */
+ol.render.canvas.Immediate.prototype.drawImages_ = function(flatCoordinates, offset, end, stride) {
+ if (!this.image_) {
+ return;
+ }
+ ol.DEBUG && console.assert(offset === 0, 'offset should be 0');
+ ol.DEBUG && console.assert(end == flatCoordinates.length,
+ 'end should be equal to the length of flatCoordinates');
+ var pixelCoordinates = ol.geom.flat.transform.transform2D(
+ flatCoordinates, offset, end, 2, this.transform_,
+ this.pixelCoordinates_);
+ var context = this.context_;
+ var localTransform = this.tmpLocalTransform_;
+ var alpha = context.globalAlpha;
+ if (this.imageOpacity_ != 1) {
+ context.globalAlpha = alpha * this.imageOpacity_;
+ }
+ var rotation = this.imageRotation_;
+ if (this.imageRotateWithView_) {
+ rotation += this.viewRotation_;
+ }
+ var i, ii;
+ for (i = 0, ii = pixelCoordinates.length; i < ii; i += 2) {
+ var x = pixelCoordinates[i] - this.imageAnchorX_;
+ var y = pixelCoordinates[i + 1] - this.imageAnchorY_;
+ if (this.imageSnapToPixel_) {
+ x = Math.round(x);
+ y = Math.round(y);
+ }
+ if (rotation !== 0 || this.imageScale_ != 1) {
+ var centerX = x + this.imageAnchorX_;
+ var centerY = y + this.imageAnchorY_;
+ ol.transform.compose(localTransform,
+ centerX, centerY,
+ this.imageScale_, this.imageScale_,
+ rotation,
+ -centerX, -centerY);
+ context.setTransform.apply(context, localTransform);
+ }
+ context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_,
+ this.imageWidth_, this.imageHeight_, x, y,
+ this.imageWidth_, this.imageHeight_);
+ }
+ if (rotation !== 0 || this.imageScale_ != 1) {
+ context.setTransform(1, 0, 0, 1, 0, 0);
+ }
+ if (this.imageOpacity_ != 1) {
+ context.globalAlpha = alpha;
+ }
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @private
+ */
+ol.render.canvas.Immediate.prototype.drawText_ = function(flatCoordinates, offset, end, stride) {
+ if (!this.textState_ || this.text_ === '') {
+ return;
+ }
+ if (this.textFillState_) {
+ this.setContextFillState_(this.textFillState_);
+ }
+ if (this.textStrokeState_) {
+ this.setContextStrokeState_(this.textStrokeState_);
+ }
+ this.setContextTextState_(this.textState_);
+ ol.DEBUG && console.assert(offset === 0, 'offset should be 0');
+ ol.DEBUG && console.assert(end == flatCoordinates.length,
+ 'end should be equal to the length of flatCoordinates');
+ var pixelCoordinates = ol.geom.flat.transform.transform2D(
+ flatCoordinates, offset, end, stride, this.transform_,
+ this.pixelCoordinates_);
+ var context = this.context_;
+ var rotation = this.textRotation_;
+ if (this.textRotateWithView_) {
+ rotation += this.viewRotation_;
+ }
+ for (; offset < end; offset += stride) {
+ var x = pixelCoordinates[offset] + this.textOffsetX_;
+ var y = pixelCoordinates[offset + 1] + this.textOffsetY_;
+ if (rotation !== 0 || this.textScale_ != 1) {
+ var localTransform = ol.transform.compose(this.tmpLocalTransform_,
+ x, y,
+ this.textScale_, this.textScale_,
+ rotation,
+ -x, -y);
+ context.setTransform.apply(context, localTransform);
+ }
+ if (this.textStrokeState_) {
+ context.strokeText(this.text_, x, y);
+ }
+ if (this.textFillState_) {
+ context.fillText(this.text_, x, y);
+ }
+ }
+ if (rotation !== 0 || this.textScale_ != 1) {
+ context.setTransform(1, 0, 0, 1, 0, 0);
+ }
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {boolean} close Close.
+ * @private
+ * @return {number} end End.
+ */
+ol.render.canvas.Immediate.prototype.moveToLineTo_ = function(flatCoordinates, offset, end, stride, close) {
+ var context = this.context_;
+ var pixelCoordinates = ol.geom.flat.transform.transform2D(
+ flatCoordinates, offset, end, stride, this.transform_,
+ this.pixelCoordinates_);
+ context.moveTo(pixelCoordinates[0], pixelCoordinates[1]);
+ var length = pixelCoordinates.length;
+ if (close) {
+ length -= 2;
+ }
+ for (var i = 2; i < length; i += 2) {
+ context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]);
+ }
+ if (close) {
+ context.closePath();
+ }
+ return end;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @private
+ * @return {number} End.
+ */
+ol.render.canvas.Immediate.prototype.drawRings_ = function(flatCoordinates, offset, ends, stride) {
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ offset = this.moveToLineTo_(
+ flatCoordinates, offset, ends[i], stride, true);
+ }
+ return offset;
+};
+
+
+/**
+ * Render a circle geometry into the canvas. Rendering is immediate and uses
+ * the current fill and stroke styles.
+ *
+ * @param {ol.geom.Circle} geometry Circle geometry.
+ * @api
+ */
+ol.render.canvas.Immediate.prototype.drawCircle = function(geometry) {
+ if (!ol.extent.intersects(this.extent_, geometry.getExtent())) {
+ return;
+ }
+ if (this.fillState_ || this.strokeState_) {
+ if (this.fillState_) {
+ this.setContextFillState_(this.fillState_);
+ }
+ if (this.strokeState_) {
+ this.setContextStrokeState_(this.strokeState_);
+ }
+ var pixelCoordinates = ol.geom.SimpleGeometry.transform2D(
+ geometry, this.transform_, this.pixelCoordinates_);
+ var dx = pixelCoordinates[2] - pixelCoordinates[0];
+ var dy = pixelCoordinates[3] - pixelCoordinates[1];
+ var radius = Math.sqrt(dx * dx + dy * dy);
+ var context = this.context_;
+ context.beginPath();
+ context.arc(
+ pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI);
+ if (this.fillState_) {
+ context.fill();
+ }
+ if (this.strokeState_) {
+ context.stroke();
+ }
+ }
+ if (this.text_ !== '') {
+ this.drawText_(geometry.getCenter(), 0, 2, 2);
+ }
+};
+
+
+/**
+ * Set the rendering style. Note that since this is an immediate rendering API,
+ * any `zIndex` on the provided style will be ignored.
+ *
+ * @param {ol.style.Style} style The rendering style.
+ * @api
+ */
+ol.render.canvas.Immediate.prototype.setStyle = function(style) {
+ this.setFillStrokeStyle(style.getFill(), style.getStroke());
+ this.setImageStyle(style.getImage());
+ this.setTextStyle(style.getText());
+};
+
+
+/**
+ * Render a geometry into the canvas. Call
+ * {@link ol.render.canvas.Immediate#setStyle} first to set the rendering style.
+ *
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render.
+ * @api
+ */
+ol.render.canvas.Immediate.prototype.drawGeometry = function(geometry) {
+ var type = geometry.getType();
+ switch (type) {
+ case ol.geom.GeometryType.POINT:
+ this.drawPoint(/** @type {ol.geom.Point} */ (geometry));
+ break;
+ case ol.geom.GeometryType.LINE_STRING:
+ this.drawLineString(/** @type {ol.geom.LineString} */ (geometry));
+ break;
+ case ol.geom.GeometryType.POLYGON:
+ this.drawPolygon(/** @type {ol.geom.Polygon} */ (geometry));
+ break;
+ case ol.geom.GeometryType.MULTI_POINT:
+ this.drawMultiPoint(/** @type {ol.geom.MultiPoint} */ (geometry));
+ break;
+ case ol.geom.GeometryType.MULTI_LINE_STRING:
+ this.drawMultiLineString(/** @type {ol.geom.MultiLineString} */ (geometry));
+ break;
+ case ol.geom.GeometryType.MULTI_POLYGON:
+ this.drawMultiPolygon(/** @type {ol.geom.MultiPolygon} */ (geometry));
+ break;
+ case ol.geom.GeometryType.GEOMETRY_COLLECTION:
+ this.drawGeometryCollection(/** @type {ol.geom.GeometryCollection} */ (geometry));
+ break;
+ case ol.geom.GeometryType.CIRCLE:
+ this.drawCircle(/** @type {ol.geom.Circle} */ (geometry));
+ break;
+ default:
+ ol.DEBUG && console.assert(false, 'Unsupported geometry type: ' + type);
+ }
+};
+
+
+/**
+ * Render a feature into the canvas. Note that any `zIndex` on the provided
+ * style will be ignored - features are rendered immediately in the order that
+ * this method is called. If you need `zIndex` support, you should be using an
+ * {@link ol.layer.Vector} instead.
+ *
+ * @param {ol.Feature} feature Feature.
+ * @param {ol.style.Style} style Style.
+ * @api
+ */
+ol.render.canvas.Immediate.prototype.drawFeature = function(feature, style) {
+ var geometry = style.getGeometryFunction()(feature);
+ if (!geometry ||
+ !ol.extent.intersects(this.extent_, geometry.getExtent())) {
+ return;
+ }
+ this.setStyle(style);
+ this.drawGeometry(geometry);
+};
+
+
+/**
+ * Render a GeometryCollection to the canvas. Rendering is immediate and
+ * uses the current styles appropriate for each geometry in the collection.
+ *
+ * @param {ol.geom.GeometryCollection} geometry Geometry collection.
+ */
+ol.render.canvas.Immediate.prototype.drawGeometryCollection = function(geometry) {
+ var geometries = geometry.getGeometriesArray();
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ this.drawGeometry(geometries[i]);
+ }
+};
+
+
+/**
+ * Render a Point geometry into the canvas. Rendering is immediate and uses
+ * the current style.
+ *
+ * @param {ol.geom.Point|ol.render.Feature} geometry Point geometry.
+ */
+ol.render.canvas.Immediate.prototype.drawPoint = function(geometry) {
+ var flatCoordinates = geometry.getFlatCoordinates();
+ var stride = geometry.getStride();
+ if (this.image_) {
+ this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride);
+ }
+ if (this.text_ !== '') {
+ this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride);
+ }
+};
+
+
+/**
+ * Render a MultiPoint geometry into the canvas. Rendering is immediate and
+ * uses the current style.
+ *
+ * @param {ol.geom.MultiPoint|ol.render.Feature} geometry MultiPoint geometry.
+ */
+ol.render.canvas.Immediate.prototype.drawMultiPoint = function(geometry) {
+ var flatCoordinates = geometry.getFlatCoordinates();
+ var stride = geometry.getStride();
+ if (this.image_) {
+ this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride);
+ }
+ if (this.text_ !== '') {
+ this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride);
+ }
+};
+
+
+/**
+ * Render a LineString into the canvas. Rendering is immediate and uses
+ * the current style.
+ *
+ * @param {ol.geom.LineString|ol.render.Feature} geometry LineString geometry.
+ */
+ol.render.canvas.Immediate.prototype.drawLineString = function(geometry) {
+ if (!ol.extent.intersects(this.extent_, geometry.getExtent())) {
+ return;
+ }
+ if (this.strokeState_) {
+ this.setContextStrokeState_(this.strokeState_);
+ var context = this.context_;
+ var flatCoordinates = geometry.getFlatCoordinates();
+ context.beginPath();
+ this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,
+ geometry.getStride(), false);
+ context.stroke();
+ }
+ if (this.text_ !== '') {
+ var flatMidpoint = geometry.getFlatMidpoint();
+ this.drawText_(flatMidpoint, 0, 2, 2);
+ }
+};
+
+
+/**
+ * Render a MultiLineString geometry into the canvas. Rendering is immediate
+ * and uses the current style.
+ *
+ * @param {ol.geom.MultiLineString|ol.render.Feature} geometry MultiLineString
+ * geometry.
+ */
+ol.render.canvas.Immediate.prototype.drawMultiLineString = function(geometry) {
+ var geometryExtent = geometry.getExtent();
+ if (!ol.extent.intersects(this.extent_, geometryExtent)) {
+ return;
+ }
+ if (this.strokeState_) {
+ this.setContextStrokeState_(this.strokeState_);
+ var context = this.context_;
+ var flatCoordinates = geometry.getFlatCoordinates();
+ var offset = 0;
+ var ends = geometry.getEnds();
+ var stride = geometry.getStride();
+ context.beginPath();
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ offset = this.moveToLineTo_(
+ flatCoordinates, offset, ends[i], stride, false);
+ }
+ context.stroke();
+ }
+ if (this.text_ !== '') {
+ var flatMidpoints = geometry.getFlatMidpoints();
+ this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2);
+ }
+};
+
+
+/**
+ * Render a Polygon geometry into the canvas. Rendering is immediate and uses
+ * the current style.
+ *
+ * @param {ol.geom.Polygon|ol.render.Feature} geometry Polygon geometry.
+ */
+ol.render.canvas.Immediate.prototype.drawPolygon = function(geometry) {
+ if (!ol.extent.intersects(this.extent_, geometry.getExtent())) {
+ return;
+ }
+ if (this.strokeState_ || this.fillState_) {
+ if (this.fillState_) {
+ this.setContextFillState_(this.fillState_);
+ }
+ if (this.strokeState_) {
+ this.setContextStrokeState_(this.strokeState_);
+ }
+ var context = this.context_;
+ context.beginPath();
+ this.drawRings_(geometry.getOrientedFlatCoordinates(),
+ 0, geometry.getEnds(), geometry.getStride());
+ if (this.fillState_) {
+ context.fill();
+ }
+ if (this.strokeState_) {
+ context.stroke();
+ }
+ }
+ if (this.text_ !== '') {
+ var flatInteriorPoint = geometry.getFlatInteriorPoint();
+ this.drawText_(flatInteriorPoint, 0, 2, 2);
+ }
+};
+
+
+/**
+ * Render MultiPolygon geometry into the canvas. Rendering is immediate and
+ * uses the current style.
+ * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry.
+ */
+ol.render.canvas.Immediate.prototype.drawMultiPolygon = function(geometry) {
+ if (!ol.extent.intersects(this.extent_, geometry.getExtent())) {
+ return;
+ }
+ if (this.strokeState_ || this.fillState_) {
+ if (this.fillState_) {
+ this.setContextFillState_(this.fillState_);
+ }
+ if (this.strokeState_) {
+ this.setContextStrokeState_(this.strokeState_);
+ }
+ var context = this.context_;
+ var flatCoordinates = geometry.getOrientedFlatCoordinates();
+ var offset = 0;
+ var endss = geometry.getEndss();
+ var stride = geometry.getStride();
+ var i, ii;
+ context.beginPath();
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ offset = this.drawRings_(flatCoordinates, offset, ends, stride);
+ }
+ if (this.fillState_) {
+ context.fill();
+ }
+ if (this.strokeState_) {
+ context.stroke();
+ }
+ }
+ if (this.text_ !== '') {
+ var flatInteriorPoints = geometry.getFlatInteriorPoints();
+ this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2);
+ }
+};
+
+
+/**
+ * @param {ol.CanvasFillState} fillState Fill state.
+ * @private
+ */
+ol.render.canvas.Immediate.prototype.setContextFillState_ = function(fillState) {
+ var context = this.context_;
+ var contextFillState = this.contextFillState_;
+ if (!contextFillState) {
+ context.fillStyle = fillState.fillStyle;
+ this.contextFillState_ = {
+ fillStyle: fillState.fillStyle
+ };
+ } else {
+ if (contextFillState.fillStyle != fillState.fillStyle) {
+ contextFillState.fillStyle = context.fillStyle = fillState.fillStyle;
+ }
+ }
+};
+
+
+/**
+ * @param {ol.CanvasStrokeState} strokeState Stroke state.
+ * @private
+ */
+ol.render.canvas.Immediate.prototype.setContextStrokeState_ = function(strokeState) {
+ var context = this.context_;
+ var contextStrokeState = this.contextStrokeState_;
+ if (!contextStrokeState) {
+ context.lineCap = strokeState.lineCap;
+ if (ol.has.CANVAS_LINE_DASH) {
+ context.setLineDash(strokeState.lineDash);
+ }
+ context.lineJoin = strokeState.lineJoin;
+ context.lineWidth = strokeState.lineWidth;
+ context.miterLimit = strokeState.miterLimit;
+ context.strokeStyle = strokeState.strokeStyle;
+ this.contextStrokeState_ = {
+ lineCap: strokeState.lineCap,
+ lineDash: strokeState.lineDash,
+ lineJoin: strokeState.lineJoin,
+ lineWidth: strokeState.lineWidth,
+ miterLimit: strokeState.miterLimit,
+ strokeStyle: strokeState.strokeStyle
+ };
+ } else {
+ if (contextStrokeState.lineCap != strokeState.lineCap) {
+ contextStrokeState.lineCap = context.lineCap = strokeState.lineCap;
+ }
+ if (ol.has.CANVAS_LINE_DASH) {
+ if (!ol.array.equals(
+ contextStrokeState.lineDash, strokeState.lineDash)) {
+ context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash);
+ }
+ }
+ if (contextStrokeState.lineJoin != strokeState.lineJoin) {
+ contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin;
+ }
+ if (contextStrokeState.lineWidth != strokeState.lineWidth) {
+ contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth;
+ }
+ if (contextStrokeState.miterLimit != strokeState.miterLimit) {
+ contextStrokeState.miterLimit = context.miterLimit =
+ strokeState.miterLimit;
+ }
+ if (contextStrokeState.strokeStyle != strokeState.strokeStyle) {
+ contextStrokeState.strokeStyle = context.strokeStyle =
+ strokeState.strokeStyle;
+ }
+ }
+};
+
+
+/**
+ * @param {ol.CanvasTextState} textState Text state.
+ * @private
+ */
+ol.render.canvas.Immediate.prototype.setContextTextState_ = function(textState) {
+ var context = this.context_;
+ var contextTextState = this.contextTextState_;
+ if (!contextTextState) {
+ context.font = textState.font;
+ context.textAlign = textState.textAlign;
+ context.textBaseline = textState.textBaseline;
+ this.contextTextState_ = {
+ font: textState.font,
+ textAlign: textState.textAlign,
+ textBaseline: textState.textBaseline
+ };
+ } else {
+ if (contextTextState.font != textState.font) {
+ contextTextState.font = context.font = textState.font;
+ }
+ if (contextTextState.textAlign != textState.textAlign) {
+ contextTextState.textAlign = context.textAlign = textState.textAlign;
+ }
+ if (contextTextState.textBaseline != textState.textBaseline) {
+ contextTextState.textBaseline = context.textBaseline =
+ textState.textBaseline;
+ }
+ }
+};
+
+
+/**
+ * Set the fill and stroke style for subsequent draw operations. To clear
+ * either fill or stroke styles, pass null for the appropriate parameter.
+ *
+ * @param {ol.style.Fill} fillStyle Fill style.
+ * @param {ol.style.Stroke} strokeStyle Stroke style.
+ */
+ol.render.canvas.Immediate.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ if (!fillStyle) {
+ this.fillState_ = null;
+ } else {
+ var fillStyleColor = fillStyle.getColor();
+ this.fillState_ = {
+ fillStyle: ol.colorlike.asColorLike(fillStyleColor ?
+ fillStyleColor : ol.render.canvas.defaultFillStyle)
+ };
+ }
+ if (!strokeStyle) {
+ this.strokeState_ = null;
+ } else {
+ var strokeStyleColor = strokeStyle.getColor();
+ var strokeStyleLineCap = strokeStyle.getLineCap();
+ var strokeStyleLineDash = strokeStyle.getLineDash();
+ var strokeStyleLineJoin = strokeStyle.getLineJoin();
+ var strokeStyleWidth = strokeStyle.getWidth();
+ var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
+ this.strokeState_ = {
+ lineCap: strokeStyleLineCap !== undefined ?
+ strokeStyleLineCap : ol.render.canvas.defaultLineCap,
+ lineDash: strokeStyleLineDash ?
+ strokeStyleLineDash : ol.render.canvas.defaultLineDash,
+ lineJoin: strokeStyleLineJoin !== undefined ?
+ strokeStyleLineJoin : ol.render.canvas.defaultLineJoin,
+ lineWidth: this.pixelRatio_ * (strokeStyleWidth !== undefined ?
+ strokeStyleWidth : ol.render.canvas.defaultLineWidth),
+ miterLimit: strokeStyleMiterLimit !== undefined ?
+ strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit,
+ strokeStyle: ol.colorlike.asColorLike(strokeStyleColor ?
+ strokeStyleColor : ol.render.canvas.defaultStrokeStyle)
+ };
+ }
+};
+
+
+/**
+ * Set the image style for subsequent draw operations. Pass null to remove
+ * the image style.
+ *
+ * @param {ol.style.Image} imageStyle Image style.
+ */
+ol.render.canvas.Immediate.prototype.setImageStyle = function(imageStyle) {
+ if (!imageStyle) {
+ this.image_ = null;
+ } else {
+ var imageAnchor = imageStyle.getAnchor();
+ // FIXME pixel ratio
+ var imageImage = imageStyle.getImage(1);
+ var imageOrigin = imageStyle.getOrigin();
+ var imageSize = imageStyle.getSize();
+ ol.DEBUG && console.assert(imageImage, 'imageImage must be truthy');
+ this.imageAnchorX_ = imageAnchor[0];
+ this.imageAnchorY_ = imageAnchor[1];
+ this.imageHeight_ = imageSize[1];
+ this.image_ = imageImage;
+ this.imageOpacity_ = imageStyle.getOpacity();
+ this.imageOriginX_ = imageOrigin[0];
+ this.imageOriginY_ = imageOrigin[1];
+ this.imageRotateWithView_ = imageStyle.getRotateWithView();
+ this.imageRotation_ = imageStyle.getRotation();
+ this.imageScale_ = imageStyle.getScale();
+ this.imageSnapToPixel_ = imageStyle.getSnapToPixel();
+ this.imageWidth_ = imageSize[0];
+ }
+};
+
+
+/**
+ * Set the text style for subsequent draw operations. Pass null to
+ * remove the text style.
+ *
+ * @param {ol.style.Text} textStyle Text style.
+ */
+ol.render.canvas.Immediate.prototype.setTextStyle = function(textStyle) {
+ if (!textStyle) {
+ this.text_ = '';
+ } else {
+ var textFillStyle = textStyle.getFill();
+ if (!textFillStyle) {
+ this.textFillState_ = null;
+ } else {
+ var textFillStyleColor = textFillStyle.getColor();
+ this.textFillState_ = {
+ fillStyle: ol.colorlike.asColorLike(textFillStyleColor ?
+ textFillStyleColor : ol.render.canvas.defaultFillStyle)
+ };
+ }
+ var textStrokeStyle = textStyle.getStroke();
+ if (!textStrokeStyle) {
+ this.textStrokeState_ = null;
+ } else {
+ var textStrokeStyleColor = textStrokeStyle.getColor();
+ var textStrokeStyleLineCap = textStrokeStyle.getLineCap();
+ var textStrokeStyleLineDash = textStrokeStyle.getLineDash();
+ var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin();
+ var textStrokeStyleWidth = textStrokeStyle.getWidth();
+ var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit();
+ this.textStrokeState_ = {
+ lineCap: textStrokeStyleLineCap !== undefined ?
+ textStrokeStyleLineCap : ol.render.canvas.defaultLineCap,
+ lineDash: textStrokeStyleLineDash ?
+ textStrokeStyleLineDash : ol.render.canvas.defaultLineDash,
+ lineJoin: textStrokeStyleLineJoin !== undefined ?
+ textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin,
+ lineWidth: textStrokeStyleWidth !== undefined ?
+ textStrokeStyleWidth : ol.render.canvas.defaultLineWidth,
+ miterLimit: textStrokeStyleMiterLimit !== undefined ?
+ textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit,
+ strokeStyle: ol.colorlike.asColorLike(textStrokeStyleColor ?
+ textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle)
+ };
+ }
+ var textFont = textStyle.getFont();
+ var textOffsetX = textStyle.getOffsetX();
+ var textOffsetY = textStyle.getOffsetY();
+ var textRotateWithView = textStyle.getRotateWithView();
+ var textRotation = textStyle.getRotation();
+ var textScale = textStyle.getScale();
+ var textText = textStyle.getText();
+ var textTextAlign = textStyle.getTextAlign();
+ var textTextBaseline = textStyle.getTextBaseline();
+ this.textState_ = {
+ font: textFont !== undefined ?
+ textFont : ol.render.canvas.defaultFont,
+ textAlign: textTextAlign !== undefined ?
+ textTextAlign : ol.render.canvas.defaultTextAlign,
+ textBaseline: textTextBaseline !== undefined ?
+ textTextBaseline : ol.render.canvas.defaultTextBaseline
+ };
+ this.text_ = textText !== undefined ? textText : '';
+ this.textOffsetX_ =
+ textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0;
+ this.textOffsetY_ =
+ textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0;
+ this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false;
+ this.textRotation_ = textRotation !== undefined ? textRotation : 0;
+ this.textScale_ = this.pixelRatio_ * (textScale !== undefined ?
+ textScale : 1);
+ }
+};
+
+goog.provide('ol.renderer.Layer');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.Observable');
+goog.require('ol.Tile');
+goog.require('ol.asserts');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.functions');
+goog.require('ol.source.State');
+
+
+/**
+ * @constructor
+ * @extends {ol.Observable}
+ * @param {ol.layer.Layer} layer Layer.
+ * @struct
+ */
+ol.renderer.Layer = function(layer) {
+
+ ol.Observable.call(this);
+
+ /**
+ * @private
+ * @type {ol.layer.Layer}
+ */
+ this.layer_ = layer;
+
+
+};
+ol.inherits(ol.renderer.Layer, ol.Observable);
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @param {function(this: S, (ol.Feature|ol.render.Feature), ol.layer.Layer): T}
+ * callback Feature callback.
+ * @param {S} thisArg Value to use as `this` when executing `callback`.
+ * @return {T|undefined} Callback result.
+ * @template S,T
+ */
+ol.renderer.Layer.prototype.forEachFeatureAtCoordinate = ol.nullFunction;
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {olx.FrameState} frameState Frame state.
+ * @return {boolean} Is there a feature at the given coordinate?
+ */
+ol.renderer.Layer.prototype.hasFeatureAtCoordinate = ol.functions.FALSE;
+
+
+/**
+ * Create a function that adds loaded tiles to the tile lookup.
+ * @param {ol.source.Tile} source Tile source.
+ * @param {ol.proj.Projection} projection Projection of the tiles.
+ * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
+ * tiles by zoom level.
+ * @return {function(number, ol.TileRange):boolean} A function that can be
+ * called with a zoom level and a tile range to add loaded tiles to the
+ * lookup.
+ * @protected
+ */
+ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, projection, tiles) {
+ return (
+ /**
+ * @param {number} zoom Zoom level.
+ * @param {ol.TileRange} tileRange Tile range.
+ * @return {boolean} The tile range is fully loaded.
+ */
+ function(zoom, tileRange) {
+ function callback(tile) {
+ if (!tiles[zoom]) {
+ tiles[zoom] = {};
+ }
+ tiles[zoom][tile.tileCoord.toString()] = tile;
+ }
+ return source.forEachLoadedTile(projection, zoom, tileRange, callback);
+ });
+};
+
+
+/**
+ * @return {ol.layer.Layer} Layer.
+ */
+ol.renderer.Layer.prototype.getLayer = function() {
+ return this.layer_;
+};
+
+
+/**
+ * Handle changes in image state.
+ * @param {ol.events.Event} event Image change event.
+ * @private
+ */
+ol.renderer.Layer.prototype.handleImageChange_ = function(event) {
+ var image = /** @type {ol.Image} */ (event.target);
+ if (image.getState() === ol.Image.State.LOADED) {
+ this.renderIfReadyAndVisible();
+ }
+};
+
+
+/**
+ * Load the image if not already loaded, and register the image change
+ * listener if needed.
+ * @param {ol.ImageBase} image Image.
+ * @return {boolean} `true` if the image is already loaded, `false`
+ * otherwise.
+ * @protected
+ */
+ol.renderer.Layer.prototype.loadImage = function(image) {
+ var imageState = image.getState();
+ if (imageState != ol.Image.State.LOADED &&
+ imageState != ol.Image.State.ERROR) {
+ // the image is either "idle" or "loading", register the change
+ // listener (a noop if the listener was already registered)
+ ol.DEBUG && console.assert(imageState == ol.Image.State.IDLE ||
+ imageState == ol.Image.State.LOADING,
+ 'imageState is "idle" or "loading"');
+ ol.events.listen(image, ol.events.EventType.CHANGE,
+ this.handleImageChange_, this);
+ }
+ if (imageState == ol.Image.State.IDLE) {
+ image.load();
+ imageState = image.getState();
+ ol.DEBUG && console.assert(imageState == ol.Image.State.LOADING ||
+ imageState == ol.Image.State.LOADED,
+ 'imageState is "loading" or "loaded"');
+ }
+ return imageState == ol.Image.State.LOADED;
+};
+
+
+/**
+ * @protected
+ */
+ol.renderer.Layer.prototype.renderIfReadyAndVisible = function() {
+ var layer = this.getLayer();
+ if (layer.getVisible() && layer.getSourceState() == ol.source.State.READY) {
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.source.Tile} tileSource Tile source.
+ * @protected
+ */
+ol.renderer.Layer.prototype.scheduleExpireCache = function(frameState, tileSource) {
+ if (tileSource.canExpireCache()) {
+ /**
+ * @param {ol.source.Tile} tileSource Tile source.
+ * @param {ol.Map} map Map.
+ * @param {olx.FrameState} frameState Frame state.
+ */
+ var postRenderFunction = function(tileSource, map, frameState) {
+ var tileSourceKey = ol.getUid(tileSource).toString();
+ tileSource.expireCache(frameState.viewState.projection,
+ frameState.usedTiles[tileSourceKey]);
+ }.bind(null, tileSource);
+
+ frameState.postRenderFunctions.push(
+ /** @type {ol.PostRenderFunction} */ (postRenderFunction)
+ );
+ }
+};
+
+
+/**
+ * @param {Object.<string, ol.Attribution>} attributionsSet Attributions
+ * set (target).
+ * @param {Array.<ol.Attribution>} attributions Attributions (source).
+ * @protected
+ */
+ol.renderer.Layer.prototype.updateAttributions = function(attributionsSet, attributions) {
+ if (attributions) {
+ var attribution, i, ii;
+ for (i = 0, ii = attributions.length; i < ii; ++i) {
+ attribution = attributions[i];
+ attributionsSet[ol.getUid(attribution).toString()] = attribution;
+ }
+ }
+};
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.source.Source} source Source.
+ * @protected
+ */
+ol.renderer.Layer.prototype.updateLogos = function(frameState, source) {
+ var logo = source.getLogo();
+ if (logo !== undefined) {
+ if (typeof logo === 'string') {
+ frameState.logos[logo] = '';
+ } else if (logo) {
+ ol.asserts.assert(typeof logo.href == 'string', 44); // `logo.href` should be a string.
+ ol.asserts.assert(typeof logo.src == 'string', 45); // `logo.src` should be a string.
+ frameState.logos[logo.src] = logo.href;
+ }
+ }
+};
+
+
+/**
+ * @param {Object.<string, Object.<string, ol.TileRange>>} usedTiles Used tiles.
+ * @param {ol.source.Tile} tileSource Tile source.
+ * @param {number} z Z.
+ * @param {ol.TileRange} tileRange Tile range.
+ * @protected
+ */
+ol.renderer.Layer.prototype.updateUsedTiles = function(usedTiles, tileSource, z, tileRange) {
+ // FIXME should we use tilesToDrawByZ instead?
+ var tileSourceKey = ol.getUid(tileSource).toString();
+ var zKey = z.toString();
+ if (tileSourceKey in usedTiles) {
+ if (zKey in usedTiles[tileSourceKey]) {
+ usedTiles[tileSourceKey][zKey].extend(tileRange);
+ } else {
+ usedTiles[tileSourceKey][zKey] = tileRange;
+ }
+ } else {
+ usedTiles[tileSourceKey] = {};
+ usedTiles[tileSourceKey][zKey] = tileRange;
+ }
+};
+
+
+/**
+ * Manage tile pyramid.
+ * This function performs a number of functions related to the tiles at the
+ * current zoom and lower zoom levels:
+ * - registers idle tiles in frameState.wantedTiles so that they are not
+ * discarded by the tile queue
+ * - enqueues missing tiles
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.source.Tile} tileSource Tile source.
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} currentZ Current Z.
+ * @param {number} preload Load low resolution tiles up to 'preload' levels.
+ * @param {function(this: T, ol.Tile)=} opt_tileCallback Tile callback.
+ * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`.
+ * @protected
+ * @template T
+ */
+ol.renderer.Layer.prototype.manageTilePyramid = function(
+ frameState, tileSource, tileGrid, pixelRatio, projection, extent,
+ currentZ, preload, opt_tileCallback, opt_this) {
+ var tileSourceKey = ol.getUid(tileSource).toString();
+ if (!(tileSourceKey in frameState.wantedTiles)) {
+ frameState.wantedTiles[tileSourceKey] = {};
+ }
+ var wantedTiles = frameState.wantedTiles[tileSourceKey];
+ var tileQueue = frameState.tileQueue;
+ var minZoom = tileGrid.getMinZoom();
+ var tile, tileRange, tileResolution, x, y, z;
+ for (z = currentZ; z >= minZoom; --z) {
+ tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange);
+ tileResolution = tileGrid.getResolution(z);
+ for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
+ for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
+ if (currentZ - z <= preload) {
+ tile = tileSource.getTile(z, x, y, pixelRatio, projection);
+ if (tile.getState() == ol.Tile.State.IDLE) {
+ wantedTiles[tile.getKey()] = true;
+ if (!tileQueue.isKeyQueued(tile.getKey())) {
+ tileQueue.enqueue([tile, tileSourceKey,
+ tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]);
+ }
+ }
+ if (opt_tileCallback !== undefined) {
+ opt_tileCallback.call(opt_this, tile);
+ }
+ } else {
+ tileSource.useTile(z, x, y, projection);
+ }
+ }
+ }
+ }
+};
+
+goog.provide('ol.renderer.canvas.Layer');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.functions');
+goog.require('ol.render.Event');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.Immediate');
+goog.require('ol.renderer.Layer');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.Layer}
+ * @param {ol.layer.Layer} layer Layer.
+ */
+ol.renderer.canvas.Layer = function(layer) {
+
+ ol.renderer.Layer.call(this, layer);
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.renderedResolution;
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.transform_ = ol.transform.create();
+
+};
+ol.inherits(ol.renderer.canvas.Layer, ol.renderer.Layer);
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.Extent} extent Clip extent.
+ * @protected
+ */
+ol.renderer.canvas.Layer.prototype.clip = function(context, frameState, extent) {
+ var pixelRatio = frameState.pixelRatio;
+ var width = frameState.size[0] * pixelRatio;
+ var height = frameState.size[1] * pixelRatio;
+ var rotation = frameState.viewState.rotation;
+ var topLeft = ol.extent.getTopLeft(/** @type {ol.Extent} */ (extent));
+ var topRight = ol.extent.getTopRight(/** @type {ol.Extent} */ (extent));
+ var bottomRight = ol.extent.getBottomRight(/** @type {ol.Extent} */ (extent));
+ var bottomLeft = ol.extent.getBottomLeft(/** @type {ol.Extent} */ (extent));
+
+ ol.transform.apply(frameState.coordinateToPixelTransform, topLeft);
+ ol.transform.apply(frameState.coordinateToPixelTransform, topRight);
+ ol.transform.apply(frameState.coordinateToPixelTransform, bottomRight);
+ ol.transform.apply(frameState.coordinateToPixelTransform, bottomLeft);
+
+ context.save();
+ ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2);
+ context.beginPath();
+ context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio);
+ context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio);
+ context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio);
+ context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio);
+ context.clip();
+ ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2);
+};
+
+
+/**
+ * @param {ol.render.Event.Type} type Event type.
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.Transform=} opt_transform Transform.
+ * @private
+ */
+ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) {
+ var layer = this.getLayer();
+ if (layer.hasListener(type)) {
+ var width = frameState.size[0] * frameState.pixelRatio;
+ var height = frameState.size[1] * frameState.pixelRatio;
+ var rotation = frameState.viewState.rotation;
+ ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2);
+ var transform = opt_transform !== undefined ?
+ opt_transform : this.getTransform(frameState, 0);
+ var render = new ol.render.canvas.Immediate(
+ context, frameState.pixelRatio, frameState.extent, transform,
+ frameState.viewState.rotation);
+ var composeEvent = new ol.render.Event(type, render, frameState,
+ context, null);
+ layer.dispatchEvent(composeEvent);
+ ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2);
+ }
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {olx.FrameState} frameState FrameState.
+ * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer
+ * callback.
+ * @param {S} thisArg Value to use as `this` when executing `callback`.
+ * @return {T|undefined} Callback result.
+ * @template S,T,U
+ */
+ol.renderer.canvas.Layer.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, callback, thisArg) {
+ var hasFeature = this.forEachFeatureAtCoordinate(
+ coordinate, frameState, 0, ol.functions.TRUE, this);
+
+ if (hasFeature) {
+ return callback.call(thisArg, this.getLayer(), null);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @param {ol.Transform=} opt_transform Transform.
+ * @protected
+ */
+ol.renderer.canvas.Layer.prototype.postCompose = function(context, frameState, layerState, opt_transform) {
+ this.dispatchComposeEvent_(ol.render.Event.Type.POSTCOMPOSE, context,
+ frameState, opt_transform);
+};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.Transform=} opt_transform Transform.
+ * @protected
+ */
+ol.renderer.canvas.Layer.prototype.preCompose = function(context, frameState, opt_transform) {
+ this.dispatchComposeEvent_(ol.render.Event.Type.PRECOMPOSE, context,
+ frameState, opt_transform);
+};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.Transform=} opt_transform Transform.
+ * @protected
+ */
+ol.renderer.canvas.Layer.prototype.dispatchRenderEvent = function(context, frameState, opt_transform) {
+ this.dispatchComposeEvent_(ol.render.Event.Type.RENDER, context,
+ frameState, opt_transform);
+};
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {number} offsetX Offset on the x-axis in view coordinates.
+ * @protected
+ * @return {!ol.Transform} Transform.
+ */
+ol.renderer.canvas.Layer.prototype.getTransform = function(frameState, offsetX) {
+ var viewState = frameState.viewState;
+ var pixelRatio = frameState.pixelRatio;
+ var dx1 = pixelRatio * frameState.size[0] / 2;
+ var dy1 = pixelRatio * frameState.size[1] / 2;
+ var sx = pixelRatio / viewState.resolution;
+ var sy = -sx;
+ var angle = -viewState.rotation;
+ var dx2 = -viewState.center[0] + offsetX;
+ var dy2 = -viewState.center[1];
+ return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2);
+};
+
+
+/**
+ * @abstract
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @param {CanvasRenderingContext2D} context Context.
+ */
+ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerState, context) {};
+
+/**
+ * @abstract
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @return {boolean} whether composeFrame should be called.
+ */
+ol.renderer.canvas.Layer.prototype.prepareFrame = function(frameState, layerState) {};
+
+goog.provide('ol.renderer.canvas.IntermediateCanvas');
+
+goog.require('ol');
+goog.require('ol.coordinate');
+goog.require('ol.dom');
+goog.require('ol.renderer.canvas.Layer');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.canvas.Layer}
+ * @param {ol.layer.Layer} layer Layer.
+ */
+ol.renderer.canvas.IntermediateCanvas = function(layer) {
+
+ ol.renderer.canvas.Layer.call(this, layer);
+
+ /**
+ * @protected
+ * @type {ol.Transform}
+ */
+ this.coordinateToCanvasPixelTransform = ol.transform.create();
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.hitCanvasContext_ = null;
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.renderedResolution;
+
+};
+ol.inherits(ol.renderer.canvas.IntermediateCanvas, ol.renderer.canvas.Layer);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.IntermediateCanvas.prototype.composeFrame = function(frameState, layerState, context) {
+
+ this.preCompose(context, frameState);
+
+ var image = this.getImage();
+ if (image) {
+
+ // clipped rendering if layer extent is set
+ var extent = layerState.extent;
+ var clipped = extent !== undefined;
+ if (clipped) {
+ this.clip(context, frameState, /** @type {ol.Extent} */ (extent));
+ }
+
+ var imageTransform = this.getImageTransform();
+ // for performance reasons, context.save / context.restore is not used
+ // to save and restore the transformation matrix and the opacity.
+ // see http://jsperf.com/context-save-restore-versus-variable
+ var alpha = context.globalAlpha;
+ context.globalAlpha = layerState.opacity;
+
+ // for performance reasons, context.setTransform is only used
+ // when the view is rotated. see http://jsperf.com/canvas-transform
+ var dx = imageTransform[4];
+ var dy = imageTransform[5];
+ var dw = image.width * imageTransform[0];
+ var dh = image.height * imageTransform[3];
+ context.drawImage(image, 0, 0, +image.width, +image.height,
+ Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
+ context.globalAlpha = alpha;
+
+ if (clipped) {
+ context.restore();
+ }
+ }
+
+ this.postCompose(context, frameState, layerState);
+};
+
+
+/**
+ * @abstract
+ * @return {HTMLCanvasElement|HTMLVideoElement|Image} Canvas.
+ */
+ol.renderer.canvas.IntermediateCanvas.prototype.getImage = function() {};
+
+
+/**
+ * @abstract
+ * @return {!ol.Transform} Image transform.
+ */
+ol.renderer.canvas.IntermediateCanvas.prototype.getImageTransform = function() {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.IntermediateCanvas.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) {
+ var layer = this.getLayer();
+ var source = layer.getSource();
+ var resolution = frameState.viewState.resolution;
+ var rotation = frameState.viewState.rotation;
+ var skippedFeatureUids = frameState.skippedFeatureUids;
+ return source.forEachFeatureAtCoordinate(
+ coordinate, resolution, rotation, hitTolerance, skippedFeatureUids,
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ return callback.call(thisArg, feature, layer);
+ });
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.IntermediateCanvas.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, callback, thisArg) {
+ if (!this.getImage()) {
+ return undefined;
+ }
+
+ if (this.getLayer().getSource().forEachFeatureAtCoordinate !== ol.nullFunction) {
+ // for ImageVector sources use the original hit-detection logic,
+ // so that for example also transparent polygons are detected
+ return ol.renderer.canvas.Layer.prototype.forEachLayerAtCoordinate.apply(this, arguments);
+ } else {
+ var pixel = ol.transform.apply(this.coordinateToCanvasPixelTransform, coordinate.slice());
+ ol.coordinate.scale(pixel, frameState.viewState.resolution / this.renderedResolution);
+
+ if (!this.hitCanvasContext_) {
+ this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1);
+ }
+
+ this.hitCanvasContext_.clearRect(0, 0, 1, 1);
+ this.hitCanvasContext_.drawImage(this.getImage(), pixel[0], pixel[1], 1, 1, 0, 0, 1, 1);
+
+ var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data;
+ if (imageData[3] > 0) {
+ return callback.call(thisArg, this.getLayer(), imageData);
+ } else {
+ return undefined;
+ }
+ }
+};
+
+goog.provide('ol.renderer.canvas.ImageLayer');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.extent');
+goog.require('ol.proj');
+goog.require('ol.renderer.canvas.IntermediateCanvas');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.canvas.IntermediateCanvas}
+ * @param {ol.layer.Image} imageLayer Single image layer.
+ */
+ol.renderer.canvas.ImageLayer = function(imageLayer) {
+
+ ol.renderer.canvas.IntermediateCanvas.call(this, imageLayer);
+
+ /**
+ * @private
+ * @type {?ol.ImageBase}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.imageTransform_ = ol.transform.create();
+
+};
+ol.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.IntermediateCanvas);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.ImageLayer.prototype.getImage = function() {
+ return !this.image_ ? null : this.image_.getImage();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.ImageLayer.prototype.getImageTransform = function() {
+ return this.imageTransform_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, layerState) {
+
+ var pixelRatio = frameState.pixelRatio;
+ var size = frameState.size;
+ var viewState = frameState.viewState;
+ var viewCenter = viewState.center;
+ var viewResolution = viewState.resolution;
+
+ var image;
+ var imageLayer = /** @type {ol.layer.Image} */ (this.getLayer());
+ var imageSource = imageLayer.getSource();
+
+ var hints = frameState.viewHints;
+
+ var renderedExtent = frameState.extent;
+ if (layerState.extent !== undefined) {
+ renderedExtent = ol.extent.getIntersection(
+ renderedExtent, layerState.extent);
+ }
+
+ if (!hints[ol.View.Hint.ANIMATING] && !hints[ol.View.Hint.INTERACTING] &&
+ !ol.extent.isEmpty(renderedExtent)) {
+ var projection = viewState.projection;
+ if (!ol.ENABLE_RASTER_REPROJECTION) {
+ var sourceProjection = imageSource.getProjection();
+ if (sourceProjection) {
+ ol.DEBUG && console.assert(ol.proj.equivalent(projection, sourceProjection),
+ 'projection and sourceProjection are equivalent');
+ projection = sourceProjection;
+ }
+ }
+ image = imageSource.getImage(
+ renderedExtent, viewResolution, pixelRatio, projection);
+ if (image) {
+ var loaded = this.loadImage(image);
+ if (loaded) {
+ this.image_ = image;
+ this.renderedResolution = viewResolution;
+ }
+ }
+ }
+
+ if (this.image_) {
+ image = this.image_;
+ var imageExtent = image.getExtent();
+ var imageResolution = image.getResolution();
+ var imagePixelRatio = image.getPixelRatio();
+ var scale = pixelRatio * imageResolution /
+ (viewResolution * imagePixelRatio);
+ var transform = ol.transform.compose(this.imageTransform_,
+ pixelRatio * size[0] / 2, pixelRatio * size[1] / 2,
+ scale, scale,
+ 0,
+ imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution,
+ imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution);
+ ol.transform.compose(this.coordinateToCanvasPixelTransform,
+ pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5],
+ pixelRatio / viewResolution, -pixelRatio / viewResolution,
+ 0,
+ -viewCenter[0], -viewCenter[1]);
+
+ this.updateAttributions(frameState.attributions, image.getAttributions());
+ this.updateLogos(frameState, imageSource);
+ }
+
+ return !!this.image_;
+};
+
+// FIXME find correct globalCompositeOperation
+
+goog.provide('ol.renderer.canvas.TileLayer');
+
+goog.require('ol');
+goog.require('ol.transform');
+goog.require('ol.TileRange');
+goog.require('ol.Tile');
+goog.require('ol.array');
+goog.require('ol.dom');
+goog.require('ol.extent');
+goog.require('ol.renderer.canvas.IntermediateCanvas');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.canvas.IntermediateCanvas}
+ * @param {ol.layer.Tile|ol.layer.VectorTile} tileLayer Tile layer.
+ */
+ol.renderer.canvas.TileLayer = function(tileLayer) {
+
+ ol.renderer.canvas.IntermediateCanvas.call(this, tileLayer);
+
+ /**
+ * @protected
+ * @type {CanvasRenderingContext2D}
+ */
+ this.context = ol.dom.createCanvasContext2D();
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.renderedExtent_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_;
+
+ /**
+ * @protected
+ * @type {!Array.<ol.Tile>}
+ */
+ this.renderedTiles = [];
+
+ /**
+ * @protected
+ * @type {ol.Extent}
+ */
+ this.tmpExtent = ol.extent.createEmpty();
+
+ /**
+ * @private
+ * @type {ol.TileCoord}
+ */
+ this.tmpTileCoord_ = [0, 0, 0];
+
+ /**
+ * @private
+ * @type {ol.TileRange}
+ */
+ this.tmpTileRange_ = new ol.TileRange(0, 0, 0, 0);
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.imageTransform_ = ol.transform.create();
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.zDirection = 0;
+
+};
+ol.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.IntermediateCanvas);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layerState) {
+
+ var pixelRatio = frameState.pixelRatio;
+ var size = frameState.size;
+ var viewState = frameState.viewState;
+ var projection = viewState.projection;
+ var viewResolution = viewState.resolution;
+ var viewCenter = viewState.center;
+
+ var tileLayer = this.getLayer();
+ var tileSource = /** @type {ol.source.Tile} */ (tileLayer.getSource());
+ var sourceRevision = tileSource.getRevision();
+ var tileGrid = tileSource.getTileGridForProjection(projection);
+ var z = tileGrid.getZForResolution(viewResolution, this.zDirection);
+ var tileResolution = tileGrid.getResolution(z);
+ var extent = frameState.extent;
+
+ if (layerState.extent !== undefined) {
+ extent = ol.extent.getIntersection(extent, layerState.extent);
+ }
+ if (ol.extent.isEmpty(extent)) {
+ // Return false to prevent the rendering of the layer.
+ return false;
+ }
+
+ var tileRange = tileGrid.getTileRangeForExtentAndResolution(
+ extent, tileResolution);
+ var imageExtent = tileGrid.getTileRangeExtent(z, tileRange);
+
+ var tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio);
+
+ /**
+ * @type {Object.<number, Object.<string, ol.Tile>>}
+ */
+ var tilesToDrawByZ = {};
+ tilesToDrawByZ[z] = {};
+
+ var findLoadedTiles = this.createLoadedTileFinder(
+ tileSource, projection, tilesToDrawByZ);
+
+ var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
+ var tmpExtent = this.tmpExtent;
+ var tmpTileRange = this.tmpTileRange_;
+ var newTiles = false;
+ var tile, x, y;
+ for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
+ for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
+ tile = tileSource.getTile(z, x, y, pixelRatio, projection);
+ var tileState = tile.getState();
+ var drawable = tileState == ol.Tile.State.LOADED ||
+ tileState == ol.Tile.State.EMPTY ||
+ tileState == ol.Tile.State.ERROR && !useInterimTilesOnError;
+ if (!drawable) {
+ tile = tile.getInterimTile();
+ } else {
+ if (tileState == ol.Tile.State.LOADED) {
+ tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
+ if (!newTiles && this.renderedTiles.indexOf(tile) == -1) {
+ newTiles = true;
+ }
+ }
+ continue;
+ }
+
+ var fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
+ tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
+ if (!fullyLoaded) {
+ var childTileRange = tileGrid.getTileCoordChildTileRange(
+ tile.tileCoord, tmpTileRange, tmpExtent);
+ if (childTileRange) {
+ findLoadedTiles(z + 1, childTileRange);
+ }
+ }
+
+ }
+ }
+
+ var hints = frameState.viewHints;
+ if (!(this.renderedResolution && Date.now() - frameState.time > 16 &&
+ (hints[ol.View.Hint.ANIMATING] || hints[ol.View.Hint.INTERACTING])) &&
+ (newTiles || !(this.renderedExtent_ &&
+ ol.extent.equals(this.renderedExtent_, imageExtent)) ||
+ this.renderedRevision_ != sourceRevision)) {
+
+ var tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection);
+ var width = tileRange.getWidth() * tilePixelSize[0];
+ var height = tileRange.getHeight() * tilePixelSize[0];
+ var context = this.context;
+ var canvas = context.canvas;
+ var opaque = tileSource.getOpaque(projection);
+ if (canvas.width != width || canvas.height != height) {
+ canvas.width = width;
+ canvas.height = height;
+ } else {
+ context.clearRect(0, 0, width, height);
+ }
+
+ this.renderedTiles.length = 0;
+ /** @type {Array.<number>} */
+ var zs = Object.keys(tilesToDrawByZ).map(Number);
+ zs.sort(ol.array.numberSafeCompareFunction);
+ var currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii;
+ var tileExtent, tileGutter, tilesToDraw, w, h;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ currentZ = zs[i];
+ currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
+ currentResolution = tileGrid.getResolution(currentZ);
+ currentScale = currentResolution / tileResolution;
+ tileGutter = tilePixelRatio * tileSource.getGutter(projection);
+ tilesToDraw = tilesToDrawByZ[currentZ];
+ for (var tileCoordKey in tilesToDraw) {
+ tile = tilesToDraw[tileCoordKey];
+ tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent);
+ x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio;
+ y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio;
+ w = currentTilePixelSize[0] * currentScale;
+ h = currentTilePixelSize[1] * currentScale;
+ if (!opaque) {
+ context.clearRect(x, y, w, h);
+ }
+ this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter);
+ this.renderedTiles.push(tile);
+ }
+ }
+
+ this.renderedRevision_ = sourceRevision;
+ this.renderedResolution = tileResolution;
+ this.renderedExtent_ = imageExtent;
+ }
+
+ var scale = pixelRatio / tilePixelRatio * this.renderedResolution / viewResolution;
+ var transform = ol.transform.compose(this.imageTransform_,
+ pixelRatio * size[0] / 2, pixelRatio * size[1] / 2,
+ scale, scale,
+ 0,
+ tilePixelRatio * (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution,
+ tilePixelRatio * (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution);
+ ol.transform.compose(this.coordinateToCanvasPixelTransform,
+ pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5],
+ pixelRatio / viewResolution, -pixelRatio / viewResolution,
+ 0,
+ -viewCenter[0], -viewCenter[1]);
+
+
+ this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
+ this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
+ projection, extent, z, tileLayer.getPreload());
+ this.scheduleExpireCache(frameState, tileSource);
+ this.updateLogos(frameState, tileSource);
+
+ return this.renderedTiles.length > 0;
+};
+
+
+/**
+ * @param {ol.Tile} tile Tile.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @param {number} x Left of the tile.
+ * @param {number} y Top of the tile.
+ * @param {number} w Width of the tile.
+ * @param {number} h Height of the tile.
+ * @param {number} gutter Tile gutter.
+ */
+ol.renderer.canvas.TileLayer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter) {
+ var image = tile.getImage();
+ if (image) {
+ this.context.drawImage(image, gutter, gutter,
+ image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.TileLayer.prototype.getImage = function() {
+ return this.context.canvas;
+};
+
+
+/**
+ * @function
+ * @return {ol.layer.Tile|ol.layer.VectorTile}
+ */
+ol.renderer.canvas.TileLayer.prototype.getLayer;
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.TileLayer.prototype.getImageTransform = function() {
+ return this.imageTransform_;
+};
+
+goog.provide('ol.render.ReplayGroup');
+
+
+/**
+ * Base class for replay groups.
+ * @constructor
+ */
+ol.render.ReplayGroup = function() {};
+
+
+/**
+ * @abstract
+ * @param {number|undefined} zIndex Z index.
+ * @param {ol.render.ReplayType} replayType Replay type.
+ * @return {ol.render.VectorContext} Replay.
+ */
+ol.render.ReplayGroup.prototype.getReplay = function(zIndex, replayType) {};
+
+
+/**
+ * @abstract
+ * @return {boolean} Is empty.
+ */
+ol.render.ReplayGroup.prototype.isEmpty = function() {};
+
+goog.provide('ol.render.canvas.Instruction');
+
+/**
+ * @enum {number}
+ */
+ol.render.canvas.Instruction = {
+ BEGIN_GEOMETRY: 0,
+ BEGIN_PATH: 1,
+ CIRCLE: 2,
+ CLOSE_PATH: 3,
+ DRAW_IMAGE: 4,
+ DRAW_TEXT: 5,
+ END_GEOMETRY: 6,
+ FILL: 7,
+ MOVE_TO_LINE_TO: 8,
+ SET_FILL_STYLE: 9,
+ SET_STROKE_STYLE: 10,
+ SET_TEXT_STYLE: 11,
+ STROKE: 12
+};
+
+goog.provide('ol.render.canvas.Replay');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.colorlike');
+goog.require('ol.extent');
+goog.require('ol.extent.Relationship');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.has');
+goog.require('ol.obj');
+goog.require('ol.render.VectorContext');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.Instruction');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.VectorContext}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Maximum extent.
+ * @param {number} resolution Resolution.
+ * @param {boolean} overlaps The replay can have overlapping geometries.
+ * @struct
+ */
+ol.render.canvas.Replay = function(tolerance, maxExtent, resolution, overlaps) {
+ ol.render.VectorContext.call(this);
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.tolerance = tolerance;
+
+ /**
+ * @protected
+ * @const
+ * @type {ol.Extent}
+ */
+ this.maxExtent = maxExtent;
+
+ /**
+ * @protected
+ * @type {boolean}
+ */
+ this.overlaps = overlaps;
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.maxLineWidth = 0;
+
+ /**
+ * @protected
+ * @const
+ * @type {number}
+ */
+ this.resolution = resolution;
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.fillOrigin_;
+
+ /**
+ * @private
+ * @type {Array.<*>}
+ */
+ this.beginGeometryInstruction1_ = null;
+
+ /**
+ * @private
+ * @type {Array.<*>}
+ */
+ this.beginGeometryInstruction2_ = null;
+
+ /**
+ * @protected
+ * @type {Array.<*>}
+ */
+ this.instructions = [];
+
+ /**
+ * @protected
+ * @type {Array.<number>}
+ */
+ this.coordinates = [];
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.renderedTransform_ = ol.transform.create();
+
+ /**
+ * @protected
+ * @type {Array.<*>}
+ */
+ this.hitDetectionInstructions = [];
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.pixelCoordinates_ = [];
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.tmpLocalTransform_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.resetTransform_ = ol.transform.create();
+};
+ol.inherits(ol.render.canvas.Replay, ol.render.VectorContext);
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {boolean} closed Last input coordinate equals first.
+ * @param {boolean} skipFirst Skip first coordinate.
+ * @protected
+ * @return {number} My end.
+ */
+ol.render.canvas.Replay.prototype.appendFlatCoordinates = function(flatCoordinates, offset, end, stride, closed, skipFirst) {
+
+ var myEnd = this.coordinates.length;
+ var extent = this.getBufferedMaxExtent();
+ if (skipFirst) {
+ offset += stride;
+ }
+ var lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]];
+ var nextCoord = [NaN, NaN];
+ var skipped = true;
+
+ var i, lastRel, nextRel;
+ for (i = offset + stride; i < end; i += stride) {
+ nextCoord[0] = flatCoordinates[i];
+ nextCoord[1] = flatCoordinates[i + 1];
+ nextRel = ol.extent.coordinateRelationship(extent, nextCoord);
+ if (nextRel !== lastRel) {
+ if (skipped) {
+ this.coordinates[myEnd++] = lastCoord[0];
+ this.coordinates[myEnd++] = lastCoord[1];
+ }
+ this.coordinates[myEnd++] = nextCoord[0];
+ this.coordinates[myEnd++] = nextCoord[1];
+ skipped = false;
+ } else if (nextRel === ol.extent.Relationship.INTERSECTING) {
+ this.coordinates[myEnd++] = nextCoord[0];
+ this.coordinates[myEnd++] = nextCoord[1];
+ skipped = false;
+ } else {
+ skipped = true;
+ }
+ lastCoord[0] = nextCoord[0];
+ lastCoord[1] = nextCoord[1];
+ lastRel = nextRel;
+ }
+
+ // Last coordinate equals first or only one point to append:
+ if ((closed && skipped) || i === offset + stride) {
+ this.coordinates[myEnd++] = lastCoord[0];
+ this.coordinates[myEnd++] = lastCoord[1];
+ }
+ return myEnd;
+};
+
+
+/**
+ * @protected
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) {
+ this.beginGeometryInstruction1_ =
+ [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0];
+ this.instructions.push(this.beginGeometryInstruction1_);
+ this.beginGeometryInstruction2_ =
+ [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0];
+ this.hitDetectionInstructions.push(this.beginGeometryInstruction2_);
+};
+
+
+/**
+ * @private
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {number} rotation Rotation.
+ */
+ol.render.canvas.Replay.prototype.fill_ = function(context, rotation) {
+ if (this.fillOrigin_) {
+ var origin = ol.transform.apply(this.renderedTransform_, this.fillOrigin_.slice());
+ context.translate(origin[0], origin[1]);
+ context.rotate(rotation);
+ }
+ context.fill();
+ if (this.fillOrigin_) {
+ context.setTransform.apply(context, this.resetTransform_);
+ }
+};
+
+
+/**
+ * @private
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Transform} transform Transform.
+ * @param {number} viewRotation View rotation.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {Array.<*>} instructions Instructions array.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined}
+ * featureCallback Feature callback.
+ * @param {ol.Extent=} opt_hitExtent Only check features that intersect this
+ * extent.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.canvas.Replay.prototype.replay_ = function(
+ context, pixelRatio, transform, viewRotation, skippedFeaturesHash,
+ instructions, featureCallback, opt_hitExtent) {
+ /** @type {Array.<number>} */
+ var pixelCoordinates;
+ if (ol.array.equals(transform, this.renderedTransform_)) {
+ pixelCoordinates = this.pixelCoordinates_;
+ } else {
+ pixelCoordinates = ol.geom.flat.transform.transform2D(
+ this.coordinates, 0, this.coordinates.length, 2,
+ transform, this.pixelCoordinates_);
+ ol.transform.setFromArray(this.renderedTransform_, transform);
+ ol.DEBUG && console.assert(pixelCoordinates === this.pixelCoordinates_,
+ 'pixelCoordinates should be the same as this.pixelCoordinates_');
+ }
+ var skipFeatures = !ol.obj.isEmpty(skippedFeaturesHash);
+ var i = 0; // instruction index
+ var ii = instructions.length; // end of instructions
+ var d = 0; // data index
+ var dd; // end of per-instruction data
+ var localTransform = this.tmpLocalTransform_;
+ var resetTransform = this.resetTransform_;
+ var prevX, prevY, roundX, roundY;
+ var pendingFill = 0;
+ var pendingStroke = 0;
+ // When the batch size gets too big, performance decreases. 200 is a good
+ // balance between batch size and number of fill/stroke instructions.
+ var batchSize =
+ this.instructions != instructions || this.overlaps ? 0 : 200;
+ while (i < ii) {
+ var instruction = instructions[i];
+ var type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]);
+ var feature, fill, stroke, text, x, y;
+ switch (type) {
+ case ol.render.canvas.Instruction.BEGIN_GEOMETRY:
+ feature = /** @type {ol.Feature|ol.render.Feature} */ (instruction[1]);
+ if ((skipFeatures &&
+ skippedFeaturesHash[ol.getUid(feature).toString()]) ||
+ !feature.getGeometry()) {
+ i = /** @type {number} */ (instruction[2]);
+ } else if (opt_hitExtent !== undefined && !ol.extent.intersects(
+ opt_hitExtent, feature.getGeometry().getExtent())) {
+ i = /** @type {number} */ (instruction[2]) + 1;
+ } else {
+ ++i;
+ }
+ break;
+ case ol.render.canvas.Instruction.BEGIN_PATH:
+ if (pendingFill > batchSize) {
+ this.fill_(context, viewRotation);
+ pendingFill = 0;
+ }
+ if (pendingStroke > batchSize) {
+ context.stroke();
+ pendingStroke = 0;
+ }
+ if (!pendingFill && !pendingStroke) {
+ context.beginPath();
+ }
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.CIRCLE:
+ ol.DEBUG && console.assert(typeof instruction[1] === 'number',
+ 'second instruction should be a number');
+ d = /** @type {number} */ (instruction[1]);
+ var x1 = pixelCoordinates[d];
+ var y1 = pixelCoordinates[d + 1];
+ var x2 = pixelCoordinates[d + 2];
+ var y2 = pixelCoordinates[d + 3];
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ var r = Math.sqrt(dx * dx + dy * dy);
+ context.moveTo(x1 + r, y1);
+ context.arc(x1, y1, r, 0, 2 * Math.PI, true);
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.CLOSE_PATH:
+ context.closePath();
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.DRAW_IMAGE:
+ ol.DEBUG && console.assert(typeof instruction[1] === 'number',
+ 'second instruction should be a number');
+ d = /** @type {number} */ (instruction[1]);
+ ol.DEBUG && console.assert(typeof instruction[2] === 'number',
+ 'third instruction should be a number');
+ dd = /** @type {number} */ (instruction[2]);
+ var image = /** @type {HTMLCanvasElement|HTMLVideoElement|Image} */
+ (instruction[3]);
+ // Remaining arguments in DRAW_IMAGE are in alphabetical order
+ var anchorX = /** @type {number} */ (instruction[4]) * pixelRatio;
+ var anchorY = /** @type {number} */ (instruction[5]) * pixelRatio;
+ var height = /** @type {number} */ (instruction[6]);
+ var opacity = /** @type {number} */ (instruction[7]);
+ var originX = /** @type {number} */ (instruction[8]);
+ var originY = /** @type {number} */ (instruction[9]);
+ var rotateWithView = /** @type {boolean} */ (instruction[10]);
+ var rotation = /** @type {number} */ (instruction[11]);
+ var scale = /** @type {number} */ (instruction[12]);
+ var snapToPixel = /** @type {boolean} */ (instruction[13]);
+ var width = /** @type {number} */ (instruction[14]);
+ if (rotateWithView) {
+ rotation += viewRotation;
+ }
+ for (; d < dd; d += 2) {
+ x = pixelCoordinates[d] - anchorX;
+ y = pixelCoordinates[d + 1] - anchorY;
+ if (snapToPixel) {
+ x = Math.round(x);
+ y = Math.round(y);
+ }
+ if (scale != 1 || rotation !== 0) {
+ var centerX = x + anchorX;
+ var centerY = y + anchorY;
+ ol.transform.compose(localTransform,
+ centerX, centerY, scale, scale, rotation, -centerX, -centerY);
+ context.setTransform.apply(context, localTransform);
+ }
+ var alpha = context.globalAlpha;
+ if (opacity != 1) {
+ context.globalAlpha = alpha * opacity;
+ }
+
+ var w = (width + originX > image.width) ? image.width - originX : width;
+ var h = (height + originY > image.height) ? image.height - originY : height;
+
+ context.drawImage(image, originX, originY, w, h,
+ x, y, w * pixelRatio, h * pixelRatio);
+
+ if (opacity != 1) {
+ context.globalAlpha = alpha;
+ }
+ if (scale != 1 || rotation !== 0) {
+ context.setTransform.apply(context, resetTransform);
+ }
+ }
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.DRAW_TEXT:
+ ol.DEBUG && console.assert(typeof instruction[1] === 'number',
+ '2nd instruction should be a number');
+ d = /** @type {number} */ (instruction[1]);
+ ol.DEBUG && console.assert(typeof instruction[2] === 'number',
+ '3rd instruction should be a number');
+ dd = /** @type {number} */ (instruction[2]);
+ ol.DEBUG && console.assert(typeof instruction[3] === 'string',
+ '4th instruction should be a string');
+ text = /** @type {string} */ (instruction[3]);
+ ol.DEBUG && console.assert(typeof instruction[4] === 'number',
+ '5th instruction should be a number');
+ var offsetX = /** @type {number} */ (instruction[4]) * pixelRatio;
+ ol.DEBUG && console.assert(typeof instruction[5] === 'number',
+ '6th instruction should be a number');
+ var offsetY = /** @type {number} */ (instruction[5]) * pixelRatio;
+ ol.DEBUG && console.assert(typeof instruction[6] === 'number',
+ '7th instruction should be a number');
+ rotation = /** @type {number} */ (instruction[6]);
+ ol.DEBUG && console.assert(typeof instruction[7] === 'number',
+ '8th instruction should be a number');
+ scale = /** @type {number} */ (instruction[7]) * pixelRatio;
+ ol.DEBUG && console.assert(typeof instruction[8] === 'boolean',
+ '9th instruction should be a boolean');
+ fill = /** @type {boolean} */ (instruction[8]);
+ ol.DEBUG && console.assert(typeof instruction[9] === 'boolean',
+ '10th instruction should be a boolean');
+ stroke = /** @type {boolean} */ (instruction[9]);
+ rotateWithView = /** @type {boolean} */ (instruction[10]);
+ if (rotateWithView) {
+ rotation += viewRotation;
+ }
+ for (; d < dd; d += 2) {
+ x = pixelCoordinates[d] + offsetX;
+ y = pixelCoordinates[d + 1] + offsetY;
+ if (scale != 1 || rotation !== 0) {
+ ol.transform.compose(localTransform, x, y, scale, scale, rotation, -x, -y);
+ context.setTransform.apply(context, localTransform);
+ }
+
+ // Support multiple lines separated by \n
+ var lines = text.split('\n');
+ var numLines = lines.length;
+ var fontSize, lineY;
+ if (numLines > 1) {
+ // Estimate line height using width of capital M, and add padding
+ fontSize = Math.round(context.measureText('M').width * 1.5);
+ lineY = y - (((numLines - 1) / 2) * fontSize);
+ } else {
+ // No need to calculate line height/offset for a single line
+ fontSize = 0;
+ lineY = y;
+ }
+
+ for (var lineIndex = 0; lineIndex < numLines; lineIndex++) {
+ var line = lines[lineIndex];
+ if (stroke) {
+ context.strokeText(line, x, lineY);
+ }
+ if (fill) {
+ context.fillText(line, x, lineY);
+ }
+
+ // Move next line down by fontSize px
+ lineY = lineY + fontSize;
+ }
+
+ if (scale != 1 || rotation !== 0) {
+ context.setTransform.apply(context, resetTransform);
+ }
+ }
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.END_GEOMETRY:
+ if (featureCallback !== undefined) {
+ feature =
+ /** @type {ol.Feature|ol.render.Feature} */ (instruction[1]);
+ var result = featureCallback(feature);
+ if (result) {
+ return result;
+ }
+ }
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.FILL:
+ if (batchSize) {
+ pendingFill++;
+ } else {
+ this.fill_(context, viewRotation);
+ }
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.MOVE_TO_LINE_TO:
+ ol.DEBUG && console.assert(typeof instruction[1] === 'number',
+ '2nd instruction should be a number');
+ d = /** @type {number} */ (instruction[1]);
+ ol.DEBUG && console.assert(typeof instruction[2] === 'number',
+ '3rd instruction should be a number');
+ dd = /** @type {number} */ (instruction[2]);
+ x = pixelCoordinates[d];
+ y = pixelCoordinates[d + 1];
+ roundX = (x + 0.5) | 0;
+ roundY = (y + 0.5) | 0;
+ if (roundX !== prevX || roundY !== prevY) {
+ context.moveTo(x, y);
+ prevX = roundX;
+ prevY = roundY;
+ }
+ for (d += 2; d < dd; d += 2) {
+ x = pixelCoordinates[d];
+ y = pixelCoordinates[d + 1];
+ roundX = (x + 0.5) | 0;
+ roundY = (y + 0.5) | 0;
+ if (d == dd - 2 || roundX !== prevX || roundY !== prevY) {
+ context.lineTo(x, y);
+ prevX = roundX;
+ prevY = roundY;
+ }
+ }
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.SET_FILL_STYLE:
+ ol.DEBUG && console.assert(
+ ol.colorlike.isColorLike(instruction[1]),
+ '2nd instruction should be a string, ' +
+ 'CanvasPattern, or CanvasGradient');
+ this.fillOrigin_ = instruction[2];
+
+ if (pendingFill) {
+ this.fill_(context, viewRotation);
+ pendingFill = 0;
+ }
+
+ context.fillStyle = /** @type {ol.ColorLike} */ (instruction[1]);
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.SET_STROKE_STYLE:
+ ol.DEBUG && console.assert(ol.colorlike.isColorLike(instruction[1]),
+ '2nd instruction should be a string, CanvasPattern, or CanvasGradient');
+ ol.DEBUG && console.assert(typeof instruction[2] === 'number',
+ '3rd instruction should be a number');
+ ol.DEBUG && console.assert(typeof instruction[3] === 'string',
+ '4rd instruction should be a string');
+ ol.DEBUG && console.assert(typeof instruction[4] === 'string',
+ '5th instruction should be a string');
+ ol.DEBUG && console.assert(typeof instruction[5] === 'number',
+ '6th instruction should be a number');
+ ol.DEBUG && console.assert(instruction[6],
+ '7th instruction should not be null');
+ ol.DEBUG && console.assert(typeof instruction[8] === 'number',
+ '9th instruction should be a number');
+ var usePixelRatio = instruction[7] !== undefined ?
+ instruction[7] : true;
+ var renderedPixelRatio = instruction[8];
+
+ var lineWidth = /** @type {number} */ (instruction[2]);
+ if (pendingStroke) {
+ context.stroke();
+ pendingStroke = 0;
+ }
+ context.strokeStyle = /** @type {ol.ColorLike} */ (instruction[1]);
+ context.lineWidth = usePixelRatio ? lineWidth * pixelRatio : lineWidth;
+ context.lineCap = /** @type {string} */ (instruction[3]);
+ context.lineJoin = /** @type {string} */ (instruction[4]);
+ context.miterLimit = /** @type {number} */ (instruction[5]);
+ if (ol.has.CANVAS_LINE_DASH) {
+ var lineDash = /** @type {Array.<number>} */ (instruction[6]);
+ if (usePixelRatio && pixelRatio !== renderedPixelRatio) {
+ lineDash = lineDash.map(function(dash) {
+ return dash * pixelRatio / renderedPixelRatio;
+ });
+ instruction[6] = lineDash;
+ instruction[8] = pixelRatio;
+ }
+ context.setLineDash(lineDash);
+ }
+ prevX = NaN;
+ prevY = NaN;
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.SET_TEXT_STYLE:
+ ol.DEBUG && console.assert(typeof instruction[1] === 'string',
+ '2nd instruction should be a string');
+ ol.DEBUG && console.assert(typeof instruction[2] === 'string',
+ '3rd instruction should be a string');
+ ol.DEBUG && console.assert(typeof instruction[3] === 'string',
+ '4th instruction should be a string');
+ context.font = /** @type {string} */ (instruction[1]);
+ context.textAlign = /** @type {string} */ (instruction[2]);
+ context.textBaseline = /** @type {string} */ (instruction[3]);
+ ++i;
+ break;
+ case ol.render.canvas.Instruction.STROKE:
+ if (batchSize) {
+ pendingStroke++;
+ } else {
+ context.stroke();
+ }
+ ++i;
+ break;
+ default:
+ ol.DEBUG && console.assert(false, 'Unknown canvas render instruction');
+ ++i; // consume the instruction anyway, to avoid an infinite loop
+ break;
+ }
+ }
+ if (pendingFill) {
+ this.fill_(context, viewRotation);
+ }
+ if (pendingStroke) {
+ context.stroke();
+ }
+ // assert that all instructions were consumed
+ ol.DEBUG && console.assert(i == instructions.length,
+ 'all instructions should be consumed');
+ return undefined;
+};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Transform} transform Transform.
+ * @param {number} viewRotation View rotation.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ */
+ol.render.canvas.Replay.prototype.replay = function(
+ context, pixelRatio, transform, viewRotation, skippedFeaturesHash) {
+ var instructions = this.instructions;
+ this.replay_(context, pixelRatio, transform, viewRotation,
+ skippedFeaturesHash, instructions, undefined, undefined);
+};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {ol.Transform} transform Transform.
+ * @param {number} viewRotation View rotation.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T=} opt_featureCallback
+ * Feature callback.
+ * @param {ol.Extent=} opt_hitExtent Only check features that intersect this
+ * extent.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.canvas.Replay.prototype.replayHitDetection = function(
+ context, transform, viewRotation, skippedFeaturesHash,
+ opt_featureCallback, opt_hitExtent) {
+ var instructions = this.hitDetectionInstructions;
+ return this.replay_(context, 1, transform, viewRotation,
+ skippedFeaturesHash, instructions, opt_featureCallback, opt_hitExtent);
+};
+
+
+/**
+ * Reverse the hit detection instructions.
+ */
+ol.render.canvas.Replay.prototype.reverseHitDetectionInstructions = function() {
+ var hitDetectionInstructions = this.hitDetectionInstructions;
+ // step 1 - reverse array
+ hitDetectionInstructions.reverse();
+ // step 2 - reverse instructions within geometry blocks
+ var i;
+ var n = hitDetectionInstructions.length;
+ var instruction;
+ var type;
+ var begin = -1;
+ for (i = 0; i < n; ++i) {
+ instruction = hitDetectionInstructions[i];
+ type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]);
+ if (type == ol.render.canvas.Instruction.END_GEOMETRY) {
+ ol.DEBUG && console.assert(begin == -1, 'begin should be -1');
+ begin = i;
+ } else if (type == ol.render.canvas.Instruction.BEGIN_GEOMETRY) {
+ instruction[2] = i;
+ ol.DEBUG && console.assert(begin >= 0,
+ 'begin should be larger than or equal to 0');
+ ol.array.reverseSubArray(this.hitDetectionInstructions, begin, i);
+ begin = -1;
+ }
+ }
+};
+
+
+/**
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.canvas.Replay.prototype.endGeometry = function(geometry, feature) {
+ ol.DEBUG && console.assert(this.beginGeometryInstruction1_,
+ 'this.beginGeometryInstruction1_ should not be null');
+ this.beginGeometryInstruction1_[2] = this.instructions.length;
+ this.beginGeometryInstruction1_ = null;
+ ol.DEBUG && console.assert(this.beginGeometryInstruction2_,
+ 'this.beginGeometryInstruction2_ should not be null');
+ this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length;
+ this.beginGeometryInstruction2_ = null;
+ var endGeometryInstruction =
+ [ol.render.canvas.Instruction.END_GEOMETRY, feature];
+ this.instructions.push(endGeometryInstruction);
+ this.hitDetectionInstructions.push(endGeometryInstruction);
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.render.canvas.Replay.prototype.finish = ol.nullFunction;
+
+
+/**
+ * Get the buffered rendering extent. Rendering will be clipped to the extent
+ * provided to the constructor. To account for symbolizers that may intersect
+ * this extent, we calculate a buffered extent (e.g. based on stroke width).
+ * @return {ol.Extent} The buffered rendering extent.
+ * @protected
+ */
+ol.render.canvas.Replay.prototype.getBufferedMaxExtent = function() {
+ return this.maxExtent;
+};
+
+goog.provide('ol.render.canvas.ImageReplay');
+
+goog.require('ol');
+goog.require('ol.render.canvas.Instruction');
+goog.require('ol.render.canvas.Replay');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.canvas.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Maximum extent.
+ * @param {number} resolution Resolution.
+ * @param {boolean} overlaps The replay can have overlapping geometries.
+ * @struct
+ */
+ol.render.canvas.ImageReplay = function(tolerance, maxExtent, resolution, overlaps) {
+ ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps);
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement|HTMLVideoElement|Image}
+ */
+ this.hitDetectionImage_ = null;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement|HTMLVideoElement|Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.anchorX_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.anchorY_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.height_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.opacity_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.originX_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.originY_ = undefined;
+
+ /**
+ * @private
+ * @type {boolean|undefined}
+ */
+ this.rotateWithView_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.rotation_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.scale_ = undefined;
+
+ /**
+ * @private
+ * @type {boolean|undefined}
+ */
+ this.snapToPixel_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.width_ = undefined;
+
+};
+ol.inherits(ol.render.canvas.ImageReplay, ol.render.canvas.Replay);
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @private
+ * @return {number} My end.
+ */
+ol.render.canvas.ImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) {
+ return this.appendFlatCoordinates(
+ flatCoordinates, offset, end, stride, false, false);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.ImageReplay.prototype.drawPoint = function(pointGeometry, feature) {
+ if (!this.image_) {
+ return;
+ }
+ ol.DEBUG && console.assert(this.anchorX_ !== undefined,
+ 'this.anchorX_ should be defined');
+ ol.DEBUG && console.assert(this.anchorY_ !== undefined,
+ 'this.anchorY_ should be defined');
+ ol.DEBUG && console.assert(this.height_ !== undefined,
+ 'this.height_ should be defined');
+ ol.DEBUG && console.assert(this.opacity_ !== undefined,
+ 'this.opacity_ should be defined');
+ ol.DEBUG && console.assert(this.originX_ !== undefined,
+ 'this.originX_ should be defined');
+ ol.DEBUG && console.assert(this.originY_ !== undefined,
+ 'this.originY_ should be defined');
+ ol.DEBUG && console.assert(this.rotateWithView_ !== undefined,
+ 'this.rotateWithView_ should be defined');
+ ol.DEBUG && console.assert(this.rotation_ !== undefined,
+ 'this.rotation_ should be defined');
+ ol.DEBUG && console.assert(this.scale_ !== undefined,
+ 'this.scale_ should be defined');
+ ol.DEBUG && console.assert(this.width_ !== undefined,
+ 'this.width_ should be defined');
+ this.beginGeometry(pointGeometry, feature);
+ var flatCoordinates = pointGeometry.getFlatCoordinates();
+ var stride = pointGeometry.getStride();
+ var myBegin = this.coordinates.length;
+ var myEnd = this.drawCoordinates_(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+ this.instructions.push([
+ ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
+ // Remaining arguments to DRAW_IMAGE are in alphabetical order
+ this.anchorX_, this.anchorY_, this.height_, this.opacity_,
+ this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
+ this.scale_, this.snapToPixel_, this.width_
+ ]);
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd,
+ this.hitDetectionImage_,
+ // Remaining arguments to DRAW_IMAGE are in alphabetical order
+ this.anchorX_, this.anchorY_, this.height_, this.opacity_,
+ this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
+ this.scale_, this.snapToPixel_, this.width_
+ ]);
+ this.endGeometry(pointGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.ImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) {
+ if (!this.image_) {
+ return;
+ }
+ ol.DEBUG && console.assert(this.anchorX_ !== undefined,
+ 'this.anchorX_ should be defined');
+ ol.DEBUG && console.assert(this.anchorY_ !== undefined,
+ 'this.anchorY_ should be defined');
+ ol.DEBUG && console.assert(this.height_ !== undefined,
+ 'this.height_ should be defined');
+ ol.DEBUG && console.assert(this.opacity_ !== undefined,
+ 'this.opacity_ should be defined');
+ ol.DEBUG && console.assert(this.originX_ !== undefined,
+ 'this.originX_ should be defined');
+ ol.DEBUG && console.assert(this.originY_ !== undefined,
+ 'this.originY_ should be defined');
+ ol.DEBUG && console.assert(this.rotateWithView_ !== undefined,
+ 'this.rotateWithView_ should be defined');
+ ol.DEBUG && console.assert(this.rotation_ !== undefined,
+ 'this.rotation_ should be defined');
+ ol.DEBUG && console.assert(this.scale_ !== undefined,
+ 'this.scale_ should be defined');
+ ol.DEBUG && console.assert(this.width_ !== undefined,
+ 'this.width_ should be defined');
+ this.beginGeometry(multiPointGeometry, feature);
+ var flatCoordinates = multiPointGeometry.getFlatCoordinates();
+ var stride = multiPointGeometry.getStride();
+ var myBegin = this.coordinates.length;
+ var myEnd = this.drawCoordinates_(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+ this.instructions.push([
+ ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
+ // Remaining arguments to DRAW_IMAGE are in alphabetical order
+ this.anchorX_, this.anchorY_, this.height_, this.opacity_,
+ this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
+ this.scale_, this.snapToPixel_, this.width_
+ ]);
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd,
+ this.hitDetectionImage_,
+ // Remaining arguments to DRAW_IMAGE are in alphabetical order
+ this.anchorX_, this.anchorY_, this.height_, this.opacity_,
+ this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
+ this.scale_, this.snapToPixel_, this.width_
+ ]);
+ this.endGeometry(multiPointGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.ImageReplay.prototype.finish = function() {
+ this.reverseHitDetectionInstructions();
+ // FIXME this doesn't really protect us against further calls to draw*Geometry
+ this.anchorX_ = undefined;
+ this.anchorY_ = undefined;
+ this.hitDetectionImage_ = null;
+ this.image_ = null;
+ this.height_ = undefined;
+ this.scale_ = undefined;
+ this.opacity_ = undefined;
+ this.originX_ = undefined;
+ this.originY_ = undefined;
+ this.rotateWithView_ = undefined;
+ this.rotation_ = undefined;
+ this.snapToPixel_ = undefined;
+ this.width_ = undefined;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) {
+ ol.DEBUG && console.assert(imageStyle, 'imageStyle should not be null');
+ var anchor = imageStyle.getAnchor();
+ ol.DEBUG && console.assert(anchor, 'anchor should not be null');
+ var size = imageStyle.getSize();
+ ol.DEBUG && console.assert(size, 'size should not be null');
+ var hitDetectionImage = imageStyle.getHitDetectionImage(1);
+ ol.DEBUG && console.assert(hitDetectionImage,
+ 'hitDetectionImage should not be null');
+ var image = imageStyle.getImage(1);
+ ol.DEBUG && console.assert(image, 'image should not be null');
+ var origin = imageStyle.getOrigin();
+ ol.DEBUG && console.assert(origin, 'origin should not be null');
+ this.anchorX_ = anchor[0];
+ this.anchorY_ = anchor[1];
+ this.hitDetectionImage_ = hitDetectionImage;
+ this.image_ = image;
+ this.height_ = size[1];
+ this.opacity_ = imageStyle.getOpacity();
+ this.originX_ = origin[0];
+ this.originY_ = origin[1];
+ this.rotateWithView_ = imageStyle.getRotateWithView();
+ this.rotation_ = imageStyle.getRotation();
+ this.scale_ = imageStyle.getScale();
+ this.snapToPixel_ = imageStyle.getSnapToPixel();
+ this.width_ = size[0];
+};
+
+goog.provide('ol.render.canvas.LineStringReplay');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.colorlike');
+goog.require('ol.extent');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.Instruction');
+goog.require('ol.render.canvas.Replay');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.canvas.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Maximum extent.
+ * @param {number} resolution Resolution.
+ * @param {boolean} overlaps The replay can have overlapping geometries.
+ * @struct
+ */
+ol.render.canvas.LineStringReplay = function(tolerance, maxExtent, resolution, overlaps) {
+
+ ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps);
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.bufferedMaxExtent_ = null;
+
+ /**
+ * @private
+ * @type {{currentStrokeStyle: (ol.ColorLike|undefined),
+ * currentLineCap: (string|undefined),
+ * currentLineDash: Array.<number>,
+ * currentLineJoin: (string|undefined),
+ * currentLineWidth: (number|undefined),
+ * currentMiterLimit: (number|undefined),
+ * lastStroke: number,
+ * strokeStyle: (ol.ColorLike|undefined),
+ * lineCap: (string|undefined),
+ * lineDash: Array.<number>,
+ * lineJoin: (string|undefined),
+ * lineWidth: (number|undefined),
+ * miterLimit: (number|undefined)}|null}
+ */
+ this.state_ = {
+ currentStrokeStyle: undefined,
+ currentLineCap: undefined,
+ currentLineDash: null,
+ currentLineJoin: undefined,
+ currentLineWidth: undefined,
+ currentMiterLimit: undefined,
+ lastStroke: 0,
+ strokeStyle: undefined,
+ lineCap: undefined,
+ lineDash: null,
+ lineJoin: undefined,
+ lineWidth: undefined,
+ miterLimit: undefined
+ };
+
+};
+ol.inherits(ol.render.canvas.LineStringReplay, ol.render.canvas.Replay);
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @private
+ * @return {number} end.
+ */
+ol.render.canvas.LineStringReplay.prototype.drawFlatCoordinates_ = function(flatCoordinates, offset, end, stride) {
+ var myBegin = this.coordinates.length;
+ var myEnd = this.appendFlatCoordinates(
+ flatCoordinates, offset, end, stride, false, false);
+ var moveToLineToInstruction =
+ [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd];
+ this.instructions.push(moveToLineToInstruction);
+ this.hitDetectionInstructions.push(moveToLineToInstruction);
+ return end;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.LineStringReplay.prototype.getBufferedMaxExtent = function() {
+ if (!this.bufferedMaxExtent_) {
+ this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent);
+ if (this.maxLineWidth > 0) {
+ var width = this.resolution * (this.maxLineWidth + 1) / 2;
+ ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
+ }
+ }
+ return this.bufferedMaxExtent_;
+};
+
+
+/**
+ * @private
+ */
+ol.render.canvas.LineStringReplay.prototype.setStrokeStyle_ = function() {
+ var state = this.state_;
+ var strokeStyle = state.strokeStyle;
+ var lineCap = state.lineCap;
+ var lineDash = state.lineDash;
+ var lineJoin = state.lineJoin;
+ var lineWidth = state.lineWidth;
+ var miterLimit = state.miterLimit;
+ ol.DEBUG && console.assert(strokeStyle !== undefined,
+ 'strokeStyle should be defined');
+ ol.DEBUG && console.assert(lineCap !== undefined, 'lineCap should be defined');
+ ol.DEBUG && console.assert(lineDash, 'lineDash should not be null');
+ ol.DEBUG && console.assert(lineJoin !== undefined, 'lineJoin should be defined');
+ ol.DEBUG && console.assert(lineWidth !== undefined, 'lineWidth should be defined');
+ ol.DEBUG && console.assert(miterLimit !== undefined, 'miterLimit should be defined');
+ if (state.currentStrokeStyle != strokeStyle ||
+ state.currentLineCap != lineCap ||
+ !ol.array.equals(state.currentLineDash, lineDash) ||
+ state.currentLineJoin != lineJoin ||
+ state.currentLineWidth != lineWidth ||
+ state.currentMiterLimit != miterLimit) {
+ if (state.lastStroke != this.coordinates.length) {
+ this.instructions.push([ol.render.canvas.Instruction.STROKE]);
+ state.lastStroke = this.coordinates.length;
+ }
+ this.instructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash, true, 1
+ ], [
+ ol.render.canvas.Instruction.BEGIN_PATH
+ ]);
+ state.currentStrokeStyle = strokeStyle;
+ state.currentLineCap = lineCap;
+ state.currentLineDash = lineDash;
+ state.currentLineJoin = lineJoin;
+ state.currentLineWidth = lineWidth;
+ state.currentMiterLimit = miterLimit;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.LineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) {
+ var state = this.state_;
+ ol.DEBUG && console.assert(state, 'state should not be null');
+ var strokeStyle = state.strokeStyle;
+ var lineWidth = state.lineWidth;
+ if (strokeStyle === undefined || lineWidth === undefined) {
+ return;
+ }
+ this.setStrokeStyle_();
+ this.beginGeometry(lineStringGeometry, feature);
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
+ state.miterLimit, state.lineDash, true, 1
+ ], [
+ ol.render.canvas.Instruction.BEGIN_PATH
+ ]);
+ var flatCoordinates = lineStringGeometry.getFlatCoordinates();
+ var stride = lineStringGeometry.getStride();
+ this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
+ this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]);
+ this.endGeometry(lineStringGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.LineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {
+ var state = this.state_;
+ ol.DEBUG && console.assert(state, 'state should not be null');
+ var strokeStyle = state.strokeStyle;
+ var lineWidth = state.lineWidth;
+ if (strokeStyle === undefined || lineWidth === undefined) {
+ return;
+ }
+ this.setStrokeStyle_();
+ this.beginGeometry(multiLineStringGeometry, feature);
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
+ state.miterLimit, state.lineDash, true, 1
+ ], [
+ ol.render.canvas.Instruction.BEGIN_PATH
+ ]);
+ var ends = multiLineStringGeometry.getEnds();
+ var flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
+ var stride = multiLineStringGeometry.getStride();
+ var offset = 0;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ offset = this.drawFlatCoordinates_(
+ flatCoordinates, offset, ends[i], stride);
+ }
+ this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]);
+ this.endGeometry(multiLineStringGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.LineStringReplay.prototype.finish = function() {
+ var state = this.state_;
+ ol.DEBUG && console.assert(state, 'state should not be null');
+ if (state.lastStroke != this.coordinates.length) {
+ this.instructions.push([ol.render.canvas.Instruction.STROKE]);
+ }
+ this.reverseHitDetectionInstructions();
+ this.state_ = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
+ ol.DEBUG && console.assert(!fillStyle, 'fillStyle should be null');
+ ol.DEBUG && console.assert(strokeStyle, 'strokeStyle should not be null');
+ var strokeStyleColor = strokeStyle.getColor();
+ this.state_.strokeStyle = ol.colorlike.asColorLike(strokeStyleColor ?
+ strokeStyleColor : ol.render.canvas.defaultStrokeStyle);
+ var strokeStyleLineCap = strokeStyle.getLineCap();
+ this.state_.lineCap = strokeStyleLineCap !== undefined ?
+ strokeStyleLineCap : ol.render.canvas.defaultLineCap;
+ var strokeStyleLineDash = strokeStyle.getLineDash();
+ this.state_.lineDash = strokeStyleLineDash ?
+ strokeStyleLineDash : ol.render.canvas.defaultLineDash;
+ var strokeStyleLineJoin = strokeStyle.getLineJoin();
+ this.state_.lineJoin = strokeStyleLineJoin !== undefined ?
+ strokeStyleLineJoin : ol.render.canvas.defaultLineJoin;
+ var strokeStyleWidth = strokeStyle.getWidth();
+ this.state_.lineWidth = strokeStyleWidth !== undefined ?
+ strokeStyleWidth : ol.render.canvas.defaultLineWidth;
+ var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
+ this.state_.miterLimit = strokeStyleMiterLimit !== undefined ?
+ strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit;
+
+ if (this.state_.lineWidth > this.maxLineWidth) {
+ this.maxLineWidth = this.state_.lineWidth;
+ // invalidate the buffered max extent cache
+ this.bufferedMaxExtent_ = null;
+ }
+};
+
+goog.provide('ol.render.canvas.PolygonReplay');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.color');
+goog.require('ol.colorlike');
+goog.require('ol.extent');
+goog.require('ol.geom.flat.simplify');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.Instruction');
+goog.require('ol.render.canvas.Replay');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.canvas.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Maximum extent.
+ * @param {number} resolution Resolution.
+ * @param {boolean} overlaps The replay can have overlapping geometries.
+ * @struct
+ */
+ol.render.canvas.PolygonReplay = function(tolerance, maxExtent, resolution, overlaps) {
+
+ ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps);
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.bufferedMaxExtent_ = null;
+
+ /**
+ * @private
+ * @type {{currentFillStyle: (ol.ColorLike|undefined),
+ * currentStrokeStyle: (ol.ColorLike|undefined),
+ * currentLineCap: (string|undefined),
+ * currentLineDash: Array.<number>,
+ * currentLineJoin: (string|undefined),
+ * currentLineWidth: (number|undefined),
+ * currentMiterLimit: (number|undefined),
+ * fillStyle: (ol.ColorLike|undefined),
+ * strokeStyle: (ol.ColorLike|undefined),
+ * lineCap: (string|undefined),
+ * lineDash: Array.<number>,
+ * lineJoin: (string|undefined),
+ * lineWidth: (number|undefined),
+ * miterLimit: (number|undefined)}|null}
+ */
+ this.state_ = {
+ currentFillStyle: undefined,
+ currentStrokeStyle: undefined,
+ currentLineCap: undefined,
+ currentLineDash: null,
+ currentLineJoin: undefined,
+ currentLineWidth: undefined,
+ currentMiterLimit: undefined,
+ fillStyle: undefined,
+ strokeStyle: undefined,
+ lineCap: undefined,
+ lineDash: null,
+ lineJoin: undefined,
+ lineWidth: undefined,
+ miterLimit: undefined
+ };
+
+};
+ol.inherits(ol.render.canvas.PolygonReplay, ol.render.canvas.Replay);
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @private
+ * @return {number} End.
+ */
+ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCoordinates, offset, ends, stride) {
+ var state = this.state_;
+ var fill = state.fillStyle !== undefined;
+ var stroke = state.strokeStyle != undefined;
+ var numEnds = ends.length;
+ var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH];
+ this.instructions.push(beginPathInstruction);
+ this.hitDetectionInstructions.push(beginPathInstruction);
+ for (var i = 0; i < numEnds; ++i) {
+ var end = ends[i];
+ var myBegin = this.coordinates.length;
+ var myEnd = this.appendFlatCoordinates(
+ flatCoordinates, offset, end, stride, true, !stroke);
+ var moveToLineToInstruction =
+ [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd];
+ this.instructions.push(moveToLineToInstruction);
+ this.hitDetectionInstructions.push(moveToLineToInstruction);
+ if (stroke) {
+ // Performance optimization: only call closePath() when we have a stroke.
+ // Otherwise the ring is closed already (see appendFlatCoordinates above).
+ var closePathInstruction = [ol.render.canvas.Instruction.CLOSE_PATH];
+ this.instructions.push(closePathInstruction);
+ this.hitDetectionInstructions.push(closePathInstruction);
+ }
+ offset = end;
+ }
+ var fillInstruction = [ol.render.canvas.Instruction.FILL];
+ this.hitDetectionInstructions.push(fillInstruction);
+ if (fill) {
+ this.instructions.push(fillInstruction);
+ }
+ if (stroke) {
+ ol.DEBUG && console.assert(state.lineWidth !== undefined,
+ 'state.lineWidth should be defined');
+ var strokeInstruction = [ol.render.canvas.Instruction.STROKE];
+ this.instructions.push(strokeInstruction);
+ this.hitDetectionInstructions.push(strokeInstruction);
+ }
+ return offset;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.PolygonReplay.prototype.drawCircle = function(circleGeometry, feature) {
+ var state = this.state_;
+ ol.DEBUG && console.assert(state, 'state should not be null');
+ var fillStyle = state.fillStyle;
+ var strokeStyle = state.strokeStyle;
+ if (fillStyle === undefined && strokeStyle === undefined) {
+ return;
+ }
+ if (strokeStyle !== undefined) {
+ ol.DEBUG && console.assert(state.lineWidth !== undefined,
+ 'state.lineWidth should be defined');
+ }
+ this.setFillStrokeStyles_(circleGeometry);
+ this.beginGeometry(circleGeometry, feature);
+ // always fill the circle for hit detection
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_FILL_STYLE,
+ ol.color.asString(ol.render.canvas.defaultFillStyle)
+ ]);
+ if (state.strokeStyle !== undefined) {
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
+ state.miterLimit, state.lineDash, true, 1
+ ]);
+ }
+ var flatCoordinates = circleGeometry.getFlatCoordinates();
+ var stride = circleGeometry.getStride();
+ var myBegin = this.coordinates.length;
+ this.appendFlatCoordinates(
+ flatCoordinates, 0, flatCoordinates.length, stride, false, false);
+ var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH];
+ var circleInstruction = [ol.render.canvas.Instruction.CIRCLE, myBegin];
+ this.instructions.push(beginPathInstruction, circleInstruction);
+ this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
+ var fillInstruction = [ol.render.canvas.Instruction.FILL];
+ this.hitDetectionInstructions.push(fillInstruction);
+ if (state.fillStyle !== undefined) {
+ this.instructions.push(fillInstruction);
+ }
+ if (state.strokeStyle !== undefined) {
+ ol.DEBUG && console.assert(state.lineWidth !== undefined,
+ 'state.lineWidth should be defined');
+ var strokeInstruction = [ol.render.canvas.Instruction.STROKE];
+ this.instructions.push(strokeInstruction);
+ this.hitDetectionInstructions.push(strokeInstruction);
+ }
+ this.endGeometry(circleGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) {
+ var state = this.state_;
+ ol.DEBUG && console.assert(state, 'state should not be null');
+ var strokeStyle = state.strokeStyle;
+ ol.DEBUG && console.assert(state.fillStyle !== undefined || strokeStyle !== undefined,
+ 'fillStyle or strokeStyle should be defined');
+ if (strokeStyle !== undefined) {
+ ol.DEBUG && console.assert(state.lineWidth !== undefined,
+ 'state.lineWidth should be defined');
+ }
+ this.setFillStrokeStyles_(polygonGeometry);
+ this.beginGeometry(polygonGeometry, feature);
+ // always fill the polygon for hit detection
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_FILL_STYLE,
+ ol.color.asString(ol.render.canvas.defaultFillStyle)]
+ );
+ if (state.strokeStyle !== undefined) {
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
+ state.miterLimit, state.lineDash, true, 1
+ ]);
+ }
+ var ends = polygonGeometry.getEnds();
+ var flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
+ var stride = polygonGeometry.getStride();
+ this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
+ this.endGeometry(polygonGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {
+ var state = this.state_;
+ ol.DEBUG && console.assert(state, 'state should not be null');
+ var fillStyle = state.fillStyle;
+ var strokeStyle = state.strokeStyle;
+ if (fillStyle === undefined && strokeStyle === undefined) {
+ return;
+ }
+ if (strokeStyle !== undefined) {
+ ol.DEBUG && console.assert(state.lineWidth !== undefined,
+ 'state.lineWidth should be defined');
+ }
+ this.setFillStrokeStyles_(multiPolygonGeometry);
+ this.beginGeometry(multiPolygonGeometry, feature);
+ // always fill the multi-polygon for hit detection
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_FILL_STYLE,
+ ol.color.asString(ol.render.canvas.defaultFillStyle)
+ ]);
+ if (state.strokeStyle !== undefined) {
+ this.hitDetectionInstructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
+ state.miterLimit, state.lineDash, true, 1
+ ]);
+ }
+ var endss = multiPolygonGeometry.getEndss();
+ var flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
+ var stride = multiPolygonGeometry.getStride();
+ var offset = 0;
+ var i, ii;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ offset = this.drawFlatCoordinatess_(
+ flatCoordinates, offset, endss[i], stride);
+ }
+ this.endGeometry(multiPolygonGeometry, feature);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.PolygonReplay.prototype.finish = function() {
+ ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
+ this.reverseHitDetectionInstructions();
+ this.state_ = null;
+ // We want to preserve topology when drawing polygons. Polygons are
+ // simplified using quantization and point elimination. However, we might
+ // have received a mix of quantized and non-quantized geometries, so ensure
+ // that all are quantized by quantizing all coordinates in the batch.
+ var tolerance = this.tolerance;
+ if (tolerance !== 0) {
+ var coordinates = this.coordinates;
+ var i, ii;
+ for (i = 0, ii = coordinates.length; i < ii; ++i) {
+ coordinates[i] = ol.geom.flat.simplify.snap(coordinates[i], tolerance);
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.PolygonReplay.prototype.getBufferedMaxExtent = function() {
+ if (!this.bufferedMaxExtent_) {
+ this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent);
+ if (this.maxLineWidth > 0) {
+ var width = this.resolution * (this.maxLineWidth + 1) / 2;
+ ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
+ }
+ }
+ return this.bufferedMaxExtent_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
+ ol.DEBUG && console.assert(fillStyle || strokeStyle,
+ 'fillStyle or strokeStyle should not be null');
+ var state = this.state_;
+ if (fillStyle) {
+ var fillStyleColor = fillStyle.getColor();
+ state.fillStyle = ol.colorlike.asColorLike(fillStyleColor ?
+ fillStyleColor : ol.render.canvas.defaultFillStyle);
+ } else {
+ state.fillStyle = undefined;
+ }
+ if (strokeStyle) {
+ var strokeStyleColor = strokeStyle.getColor();
+ state.strokeStyle = ol.colorlike.asColorLike(strokeStyleColor ?
+ strokeStyleColor : ol.render.canvas.defaultStrokeStyle);
+ var strokeStyleLineCap = strokeStyle.getLineCap();
+ state.lineCap = strokeStyleLineCap !== undefined ?
+ strokeStyleLineCap : ol.render.canvas.defaultLineCap;
+ var strokeStyleLineDash = strokeStyle.getLineDash();
+ state.lineDash = strokeStyleLineDash ?
+ strokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash;
+ var strokeStyleLineJoin = strokeStyle.getLineJoin();
+ state.lineJoin = strokeStyleLineJoin !== undefined ?
+ strokeStyleLineJoin : ol.render.canvas.defaultLineJoin;
+ var strokeStyleWidth = strokeStyle.getWidth();
+ state.lineWidth = strokeStyleWidth !== undefined ?
+ strokeStyleWidth : ol.render.canvas.defaultLineWidth;
+ var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
+ state.miterLimit = strokeStyleMiterLimit !== undefined ?
+ strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit;
+
+ if (state.lineWidth > this.maxLineWidth) {
+ this.maxLineWidth = state.lineWidth;
+ // invalidate the buffered max extent cache
+ this.bufferedMaxExtent_ = null;
+ }
+ } else {
+ state.strokeStyle = undefined;
+ state.lineCap = undefined;
+ state.lineDash = null;
+ state.lineJoin = undefined;
+ state.lineWidth = undefined;
+ state.miterLimit = undefined;
+ }
+};
+
+
+/**
+ * @private
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry.
+ */
+ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function(geometry) {
+ var state = this.state_;
+ var fillStyle = state.fillStyle;
+ var strokeStyle = state.strokeStyle;
+ var lineCap = state.lineCap;
+ var lineDash = state.lineDash;
+ var lineJoin = state.lineJoin;
+ var lineWidth = state.lineWidth;
+ var miterLimit = state.miterLimit;
+ if (fillStyle !== undefined && (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle)) {
+ var fillInstruction = [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle];
+ if (typeof fillStyle !== 'string') {
+ var fillExtent = geometry.getExtent();
+ fillInstruction.push([fillExtent[0], fillExtent[3]]);
+ }
+ this.instructions.push(fillInstruction);
+ state.currentFillStyle = state.fillStyle;
+ }
+ if (strokeStyle !== undefined) {
+ ol.DEBUG && console.assert(lineCap !== undefined, 'lineCap should be defined');
+ ol.DEBUG && console.assert(lineDash, 'lineDash should not be null');
+ ol.DEBUG && console.assert(lineJoin !== undefined, 'lineJoin should be defined');
+ ol.DEBUG && console.assert(lineWidth !== undefined, 'lineWidth should be defined');
+ ol.DEBUG && console.assert(miterLimit !== undefined,
+ 'miterLimit should be defined');
+ if (state.currentStrokeStyle != strokeStyle ||
+ state.currentLineCap != lineCap ||
+ !ol.array.equals(state.currentLineDash, lineDash) ||
+ state.currentLineJoin != lineJoin ||
+ state.currentLineWidth != lineWidth ||
+ state.currentMiterLimit != miterLimit) {
+ this.instructions.push([
+ ol.render.canvas.Instruction.SET_STROKE_STYLE,
+ strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash, true, 1
+ ]);
+ state.currentStrokeStyle = strokeStyle;
+ state.currentLineCap = lineCap;
+ state.currentLineDash = lineDash;
+ state.currentLineJoin = lineJoin;
+ state.currentLineWidth = lineWidth;
+ state.currentMiterLimit = miterLimit;
+ }
+ }
+};
+
+goog.provide('ol.render.canvas.TextReplay');
+
+goog.require('ol');
+goog.require('ol.colorlike');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.Instruction');
+goog.require('ol.render.canvas.Replay');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.canvas.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Maximum extent.
+ * @param {number} resolution Resolution.
+ * @param {boolean} overlaps The replay can have overlapping geometries.
+ * @struct
+ */
+ol.render.canvas.TextReplay = function(tolerance, maxExtent, resolution, overlaps) {
+
+ ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps);
+
+ /**
+ * @private
+ * @type {?ol.CanvasFillState}
+ */
+ this.replayFillState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasStrokeState}
+ */
+ this.replayStrokeState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasTextState}
+ */
+ this.replayTextState_ = null;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.text_ = '';
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textOffsetX_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textOffsetY_ = 0;
+
+ /**
+ * @private
+ * @type {boolean|undefined}
+ */
+ this.textRotateWithView_ = undefined;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textRotation_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textScale_ = 0;
+
+ /**
+ * @private
+ * @type {?ol.CanvasFillState}
+ */
+ this.textFillState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasStrokeState}
+ */
+ this.textStrokeState_ = null;
+
+ /**
+ * @private
+ * @type {?ol.CanvasTextState}
+ */
+ this.textState_ = null;
+
+};
+ol.inherits(ol.render.canvas.TextReplay, ol.render.canvas.Replay);
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.TextReplay.prototype.drawText = function(flatCoordinates, offset, end, stride, geometry, feature) {
+ if (this.text_ === '' || !this.textState_ ||
+ (!this.textFillState_ && !this.textStrokeState_)) {
+ return;
+ }
+ if (this.textFillState_) {
+ this.setReplayFillState_(this.textFillState_);
+ }
+ if (this.textStrokeState_) {
+ this.setReplayStrokeState_(this.textStrokeState_);
+ }
+ this.setReplayTextState_(this.textState_);
+ this.beginGeometry(geometry, feature);
+ var myBegin = this.coordinates.length;
+ var myEnd =
+ this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false);
+ var fill = !!this.textFillState_;
+ var stroke = !!this.textStrokeState_;
+ var drawTextInstruction = [
+ ol.render.canvas.Instruction.DRAW_TEXT, myBegin, myEnd, this.text_,
+ this.textOffsetX_, this.textOffsetY_, this.textRotation_, this.textScale_,
+ fill, stroke, this.textRotateWithView_];
+ this.instructions.push(drawTextInstruction);
+ this.hitDetectionInstructions.push(drawTextInstruction);
+ this.endGeometry(geometry, feature);
+};
+
+
+/**
+ * @param {ol.CanvasFillState} fillState Fill state.
+ * @private
+ */
+ol.render.canvas.TextReplay.prototype.setReplayFillState_ = function(fillState) {
+ var replayFillState = this.replayFillState_;
+ if (replayFillState &&
+ replayFillState.fillStyle == fillState.fillStyle) {
+ return;
+ }
+ var setFillStyleInstruction =
+ [ol.render.canvas.Instruction.SET_FILL_STYLE, fillState.fillStyle];
+ this.instructions.push(setFillStyleInstruction);
+ this.hitDetectionInstructions.push(setFillStyleInstruction);
+ if (!replayFillState) {
+ this.replayFillState_ = {
+ fillStyle: fillState.fillStyle
+ };
+ } else {
+ replayFillState.fillStyle = fillState.fillStyle;
+ }
+};
+
+
+/**
+ * @param {ol.CanvasStrokeState} strokeState Stroke state.
+ * @private
+ */
+ol.render.canvas.TextReplay.prototype.setReplayStrokeState_ = function(strokeState) {
+ var replayStrokeState = this.replayStrokeState_;
+ if (replayStrokeState &&
+ replayStrokeState.lineCap == strokeState.lineCap &&
+ replayStrokeState.lineDash == strokeState.lineDash &&
+ replayStrokeState.lineJoin == strokeState.lineJoin &&
+ replayStrokeState.lineWidth == strokeState.lineWidth &&
+ replayStrokeState.miterLimit == strokeState.miterLimit &&
+ replayStrokeState.strokeStyle == strokeState.strokeStyle) {
+ return;
+ }
+ var setStrokeStyleInstruction = [
+ ol.render.canvas.Instruction.SET_STROKE_STYLE, strokeState.strokeStyle,
+ strokeState.lineWidth, strokeState.lineCap, strokeState.lineJoin,
+ strokeState.miterLimit, strokeState.lineDash, false, 1
+ ];
+ this.instructions.push(setStrokeStyleInstruction);
+ this.hitDetectionInstructions.push(setStrokeStyleInstruction);
+ if (!replayStrokeState) {
+ this.replayStrokeState_ = {
+ lineCap: strokeState.lineCap,
+ lineDash: strokeState.lineDash,
+ lineJoin: strokeState.lineJoin,
+ lineWidth: strokeState.lineWidth,
+ miterLimit: strokeState.miterLimit,
+ strokeStyle: strokeState.strokeStyle
+ };
+ } else {
+ replayStrokeState.lineCap = strokeState.lineCap;
+ replayStrokeState.lineDash = strokeState.lineDash;
+ replayStrokeState.lineJoin = strokeState.lineJoin;
+ replayStrokeState.lineWidth = strokeState.lineWidth;
+ replayStrokeState.miterLimit = strokeState.miterLimit;
+ replayStrokeState.strokeStyle = strokeState.strokeStyle;
+ }
+};
+
+
+/**
+ * @param {ol.CanvasTextState} textState Text state.
+ * @private
+ */
+ol.render.canvas.TextReplay.prototype.setReplayTextState_ = function(textState) {
+ var replayTextState = this.replayTextState_;
+ if (replayTextState &&
+ replayTextState.font == textState.font &&
+ replayTextState.textAlign == textState.textAlign &&
+ replayTextState.textBaseline == textState.textBaseline) {
+ return;
+ }
+ var setTextStyleInstruction = [ol.render.canvas.Instruction.SET_TEXT_STYLE,
+ textState.font, textState.textAlign, textState.textBaseline];
+ this.instructions.push(setTextStyleInstruction);
+ this.hitDetectionInstructions.push(setTextStyleInstruction);
+ if (!replayTextState) {
+ this.replayTextState_ = {
+ font: textState.font,
+ textAlign: textState.textAlign,
+ textBaseline: textState.textBaseline
+ };
+ } else {
+ replayTextState.font = textState.font;
+ replayTextState.textAlign = textState.textAlign;
+ replayTextState.textBaseline = textState.textBaseline;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) {
+ if (!textStyle) {
+ this.text_ = '';
+ } else {
+ var textFillStyle = textStyle.getFill();
+ if (!textFillStyle) {
+ this.textFillState_ = null;
+ } else {
+ var textFillStyleColor = textFillStyle.getColor();
+ var fillStyle = ol.colorlike.asColorLike(textFillStyleColor ?
+ textFillStyleColor : ol.render.canvas.defaultFillStyle);
+ if (!this.textFillState_) {
+ this.textFillState_ = {
+ fillStyle: fillStyle
+ };
+ } else {
+ var textFillState = this.textFillState_;
+ textFillState.fillStyle = fillStyle;
+ }
+ }
+ var textStrokeStyle = textStyle.getStroke();
+ if (!textStrokeStyle) {
+ this.textStrokeState_ = null;
+ } else {
+ var textStrokeStyleColor = textStrokeStyle.getColor();
+ var textStrokeStyleLineCap = textStrokeStyle.getLineCap();
+ var textStrokeStyleLineDash = textStrokeStyle.getLineDash();
+ var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin();
+ var textStrokeStyleWidth = textStrokeStyle.getWidth();
+ var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit();
+ var lineCap = textStrokeStyleLineCap !== undefined ?
+ textStrokeStyleLineCap : ol.render.canvas.defaultLineCap;
+ var lineDash = textStrokeStyleLineDash ?
+ textStrokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash;
+ var lineJoin = textStrokeStyleLineJoin !== undefined ?
+ textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin;
+ var lineWidth = textStrokeStyleWidth !== undefined ?
+ textStrokeStyleWidth : ol.render.canvas.defaultLineWidth;
+ var miterLimit = textStrokeStyleMiterLimit !== undefined ?
+ textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit;
+ var strokeStyle = ol.colorlike.asColorLike(textStrokeStyleColor ?
+ textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle);
+ if (!this.textStrokeState_) {
+ this.textStrokeState_ = {
+ lineCap: lineCap,
+ lineDash: lineDash,
+ lineJoin: lineJoin,
+ lineWidth: lineWidth,
+ miterLimit: miterLimit,
+ strokeStyle: strokeStyle
+ };
+ } else {
+ var textStrokeState = this.textStrokeState_;
+ textStrokeState.lineCap = lineCap;
+ textStrokeState.lineDash = lineDash;
+ textStrokeState.lineJoin = lineJoin;
+ textStrokeState.lineWidth = lineWidth;
+ textStrokeState.miterLimit = miterLimit;
+ textStrokeState.strokeStyle = strokeStyle;
+ }
+ }
+ var textFont = textStyle.getFont();
+ var textOffsetX = textStyle.getOffsetX();
+ var textOffsetY = textStyle.getOffsetY();
+ var textRotateWithView = textStyle.getRotateWithView();
+ var textRotation = textStyle.getRotation();
+ var textScale = textStyle.getScale();
+ var textText = textStyle.getText();
+ var textTextAlign = textStyle.getTextAlign();
+ var textTextBaseline = textStyle.getTextBaseline();
+ var font = textFont !== undefined ?
+ textFont : ol.render.canvas.defaultFont;
+ var textAlign = textTextAlign !== undefined ?
+ textTextAlign : ol.render.canvas.defaultTextAlign;
+ var textBaseline = textTextBaseline !== undefined ?
+ textTextBaseline : ol.render.canvas.defaultTextBaseline;
+ if (!this.textState_) {
+ this.textState_ = {
+ font: font,
+ textAlign: textAlign,
+ textBaseline: textBaseline
+ };
+ } else {
+ var textState = this.textState_;
+ textState.font = font;
+ textState.textAlign = textAlign;
+ textState.textBaseline = textBaseline;
+ }
+ this.text_ = textText !== undefined ? textText : '';
+ this.textOffsetX_ = textOffsetX !== undefined ? textOffsetX : 0;
+ this.textOffsetY_ = textOffsetY !== undefined ? textOffsetY : 0;
+ this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false;
+ this.textRotation_ = textRotation !== undefined ? textRotation : 0;
+ this.textScale_ = textScale !== undefined ? textScale : 1;
+ }
+};
+
+goog.provide('ol.render.ReplayType');
+
+
+/**
+ * @enum {string}
+ */
+ol.render.ReplayType = {
+ CIRCLE: 'Circle',
+ IMAGE: 'Image',
+ LINE_STRING: 'LineString',
+ POLYGON: 'Polygon',
+ TEXT: 'Text'
+};
+
+goog.provide('ol.render.replay');
+
+goog.require('ol.render.ReplayType');
+
+
+/**
+ * @const
+ * @type {Array.<ol.render.ReplayType>}
+ */
+ol.render.replay.ORDER = [
+ ol.render.ReplayType.POLYGON,
+ ol.render.ReplayType.CIRCLE,
+ ol.render.ReplayType.LINE_STRING,
+ ol.render.ReplayType.IMAGE,
+ ol.render.ReplayType.TEXT
+];
+
+goog.provide('ol.render.canvas.ReplayGroup');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.dom');
+goog.require('ol.extent');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.obj');
+goog.require('ol.render.ReplayGroup');
+goog.require('ol.render.canvas.ImageReplay');
+goog.require('ol.render.canvas.LineStringReplay');
+goog.require('ol.render.canvas.PolygonReplay');
+goog.require('ol.render.canvas.TextReplay');
+goog.require('ol.render.replay');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.ReplayGroup}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @param {number} resolution Resolution.
+ * @param {boolean} overlaps The replay group can have overlapping geometries.
+ * @param {number=} opt_renderBuffer Optional rendering buffer.
+ * @struct
+ */
+ol.render.canvas.ReplayGroup = function(
+ tolerance, maxExtent, resolution, overlaps, opt_renderBuffer) {
+ ol.render.ReplayGroup.call(this);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.tolerance_ = tolerance;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.maxExtent_ = maxExtent;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.overlaps_ = overlaps;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.resolution_ = resolution;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.renderBuffer_ = opt_renderBuffer;
+
+ /**
+ * @private
+ * @type {!Object.<string,
+ * Object.<ol.render.ReplayType, ol.render.canvas.Replay>>}
+ */
+ this.replaysByZIndex_ = {};
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.hitDetectionContext_ = ol.dom.createCanvasContext2D(1, 1);
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.hitDetectionTransform_ = ol.transform.create();
+};
+ol.inherits(ol.render.canvas.ReplayGroup, ol.render.ReplayGroup);
+
+
+/**
+ * This cache is used for storing calculated pixel circles for increasing performance.
+ * It is a static property to allow each Replaygroup to access it.
+ * @type {Object.<number, Array.<Array.<(boolean|undefined)>>>}
+ * @private
+ */
+ol.render.canvas.ReplayGroup.circleArrayCache_ = {
+ 0: [[true]]
+};
+
+
+/**
+ * This method fills a row in the array from the given coordinate to the
+ * middle with `true`.
+ * @param {Array.<Array.<(boolean|undefined)>>} array The array that will be altered.
+ * @param {number} x X coordinate.
+ * @param {number} y Y coordinate.
+ * @private
+ */
+ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_ = function(array, x, y) {
+ var i;
+ var radius = Math.floor(array.length / 2);
+ if (x >= radius) {
+ for (i = radius; i < x; i++) {
+ array[i][y] = true;
+ }
+ } else if (x < radius) {
+ for (i = x + 1; i < radius; i++) {
+ array[i][y] = true;
+ }
+ }
+};
+
+
+/**
+ * This methods creates a circle inside a fitting array. Points inside the
+ * circle are marked by true, points on the outside are undefined.
+ * It uses the midpoint circle algorithm.
+ * A cache is used to increase performance.
+ * @param {number} radius Radius.
+ * @returns {Array.<Array.<(boolean|undefined)>>} An array with marked circle points.
+ * @private
+ */
+ol.render.canvas.ReplayGroup.getCircleArray_ = function(radius) {
+ if (ol.render.canvas.ReplayGroup.circleArrayCache_[radius] !== undefined) {
+ return ol.render.canvas.ReplayGroup.circleArrayCache_[radius];
+ }
+
+ var arraySize = radius * 2 + 1;
+ var arr = new Array(arraySize);
+ for (var i = 0; i < arraySize; i++) {
+ arr[i] = new Array(arraySize);
+ }
+
+ var x = radius;
+ var y = 0;
+ var error = 0;
+
+ while (x >= y) {
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + x, radius + y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + y, radius + x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - y, radius + x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - x, radius + y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - x, radius - y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - y, radius - x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + y, radius - x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + x, radius - y);
+
+ y++;
+ error += 1 + 2 * y;
+ if (2 * (error - x) + 1 > 0) {
+ x -= 1;
+ error += 1 - 2 * x;
+ }
+ }
+
+ ol.render.canvas.ReplayGroup.circleArrayCache_[radius] = arr;
+ return arr;
+};
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.render.canvas.ReplayGroup.prototype.finish = function() {
+ var zKey;
+ for (zKey in this.replaysByZIndex_) {
+ var replays = this.replaysByZIndex_[zKey];
+ var replayKey;
+ for (replayKey in replays) {
+ replays[replayKey].finish();
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T} callback Feature
+ * callback.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
+ coordinate, resolution, rotation, hitTolerance, skippedFeaturesHash, callback) {
+
+ hitTolerance = Math.round(hitTolerance);
+ var contextSize = hitTolerance * 2 + 1;
+ var transform = ol.transform.compose(this.hitDetectionTransform_,
+ hitTolerance + 0.5, hitTolerance + 0.5,
+ 1 / resolution, -1 / resolution,
+ -rotation,
+ -coordinate[0], -coordinate[1]);
+ var context = this.hitDetectionContext_;
+
+ if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
+ context.canvas.width = contextSize;
+ context.canvas.height = contextSize;
+ } else {
+ context.clearRect(0, 0, contextSize, contextSize);
+ }
+
+ /**
+ * @type {ol.Extent}
+ */
+ var hitExtent;
+ if (this.renderBuffer_ !== undefined) {
+ hitExtent = ol.extent.createEmpty();
+ ol.extent.extendCoordinate(hitExtent, coordinate);
+ ol.extent.buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent);
+ }
+
+ var mask = ol.render.canvas.ReplayGroup.getCircleArray_(hitTolerance);
+
+ return this.replayHitDetection_(context, transform, rotation,
+ skippedFeaturesHash,
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ var imageData = context.getImageData(0, 0, contextSize, contextSize).data;
+ for (var i = 0; i < contextSize; i++) {
+ for (var j = 0; j < contextSize; j++) {
+ if (mask[i][j]) {
+ if (imageData[(j * contextSize + i) * 4 + 3] > 0) {
+ var result = callback(feature);
+ if (result) {
+ return result;
+ } else {
+ context.clearRect(0, 0, contextSize, contextSize);
+ return undefined;
+ }
+ }
+ }
+ }
+ }
+ }, hitExtent);
+};
+
+
+/**
+ * @param {ol.Transform} transform Transform.
+ * @return {Array.<number>} Clip coordinates.
+ */
+ol.render.canvas.ReplayGroup.prototype.getClipCoords = function(transform) {
+ var maxExtent = this.maxExtent_;
+ var minX = maxExtent[0];
+ var minY = maxExtent[1];
+ var maxX = maxExtent[2];
+ var maxY = maxExtent[3];
+ var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
+ ol.geom.flat.transform.transform2D(
+ flatClipCoords, 0, 8, 2, transform, flatClipCoords);
+ return flatClipCoords;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.ReplayGroup.prototype.getReplay = function(zIndex, replayType) {
+ var zIndexKey = zIndex !== undefined ? zIndex.toString() : '0';
+ var replays = this.replaysByZIndex_[zIndexKey];
+ if (replays === undefined) {
+ replays = {};
+ this.replaysByZIndex_[zIndexKey] = replays;
+ }
+ var replay = replays[replayType];
+ if (replay === undefined) {
+ var Constructor = ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_[replayType];
+ ol.DEBUG && console.assert(Constructor !== undefined,
+ replayType +
+ ' constructor missing from ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_');
+ replay = new Constructor(this.tolerance_, this.maxExtent_,
+ this.resolution_, this.overlaps_);
+ replays[replayType] = replay;
+ }
+ return replay;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.canvas.ReplayGroup.prototype.isEmpty = function() {
+ return ol.obj.isEmpty(this.replaysByZIndex_);
+};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Transform} transform Transform.
+ * @param {number} viewRotation View rotation.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {Array.<ol.render.ReplayType>=} opt_replayTypes Ordered replay types
+ * to replay. Default is {@link ol.render.replay.ORDER}
+ */
+ol.render.canvas.ReplayGroup.prototype.replay = function(context, pixelRatio,
+ transform, viewRotation, skippedFeaturesHash, opt_replayTypes) {
+
+ /** @type {Array.<number>} */
+ var zs = Object.keys(this.replaysByZIndex_).map(Number);
+ zs.sort(ol.array.numberSafeCompareFunction);
+
+ // setup clipping so that the parts of over-simplified geometries are not
+ // visible outside the current extent when panning
+ var flatClipCoords = this.getClipCoords(transform);
+ context.save();
+ context.beginPath();
+ context.moveTo(flatClipCoords[0], flatClipCoords[1]);
+ context.lineTo(flatClipCoords[2], flatClipCoords[3]);
+ context.lineTo(flatClipCoords[4], flatClipCoords[5]);
+ context.lineTo(flatClipCoords[6], flatClipCoords[7]);
+ context.clip();
+
+ var replayTypes = opt_replayTypes ? opt_replayTypes : ol.render.replay.ORDER;
+ var i, ii, j, jj, replays, replay;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ replays = this.replaysByZIndex_[zs[i].toString()];
+ for (j = 0, jj = replayTypes.length; j < jj; ++j) {
+ replay = replays[replayTypes[j]];
+ if (replay !== undefined) {
+ replay.replay(context, pixelRatio, transform, viewRotation,
+ skippedFeaturesHash);
+ }
+ }
+ }
+
+ context.restore();
+};
+
+
+/**
+ * @private
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {ol.Transform} transform Transform.
+ * @param {number} viewRotation View rotation.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T} featureCallback
+ * Feature callback.
+ * @param {ol.Extent=} opt_hitExtent Only check features that intersect this
+ * extent.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function(
+ context, transform, viewRotation, skippedFeaturesHash,
+ featureCallback, opt_hitExtent) {
+ /** @type {Array.<number>} */
+ var zs = Object.keys(this.replaysByZIndex_).map(Number);
+ zs.sort(function(a, b) {
+ return b - a;
+ });
+
+ var i, ii, j, replays, replay, result;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ replays = this.replaysByZIndex_[zs[i].toString()];
+ for (j = ol.render.replay.ORDER.length - 1; j >= 0; --j) {
+ replay = replays[ol.render.replay.ORDER[j]];
+ if (replay !== undefined) {
+ result = replay.replayHitDetection(context, transform, viewRotation,
+ skippedFeaturesHash, featureCallback, opt_hitExtent);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<ol.render.ReplayType,
+ * function(new: ol.render.canvas.Replay, number, ol.Extent,
+ * number, boolean)>}
+ */
+ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_ = {
+ 'Circle': ol.render.canvas.PolygonReplay,
+ 'Image': ol.render.canvas.ImageReplay,
+ 'LineString': ol.render.canvas.LineStringReplay,
+ 'Polygon': ol.render.canvas.PolygonReplay,
+ 'Text': ol.render.canvas.TextReplay
+};
+
+goog.provide('ol.renderer.vector');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.render.ReplayType');
+
+
+/**
+ * @param {ol.Feature|ol.render.Feature} feature1 Feature 1.
+ * @param {ol.Feature|ol.render.Feature} feature2 Feature 2.
+ * @return {number} Order.
+ */
+ol.renderer.vector.defaultOrder = function(feature1, feature2) {
+ return ol.getUid(feature1) - ol.getUid(feature2);
+};
+
+
+/**
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {number} Squared pixel tolerance.
+ */
+ol.renderer.vector.getSquaredTolerance = function(resolution, pixelRatio) {
+ var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
+ return tolerance * tolerance;
+};
+
+
+/**
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {number} Pixel tolerance.
+ */
+ol.renderer.vector.getTolerance = function(resolution, pixelRatio) {
+ return ol.SIMPLIFY_TOLERANCE * resolution / pixelRatio;
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.Circle} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderCircleGeometry_ = function(replayGroup, geometry, style, feature) {
+ var fillStyle = style.getFill();
+ var strokeStyle = style.getStroke();
+ if (fillStyle || strokeStyle) {
+ var circleReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.CIRCLE);
+ circleReplay.setFillStrokeStyle(fillStyle, strokeStyle);
+ circleReplay.drawCircle(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ textReplay.drawText(geometry.getCenter(), 0, 2, 2, geometry, feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {ol.style.Style} style Style.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {function(this: T, ol.events.Event)} listener Listener function.
+ * @param {T} thisArg Value to use as `this` when executing `listener`.
+ * @return {boolean} `true` if style is loading.
+ * @template T
+ */
+ol.renderer.vector.renderFeature = function(
+ replayGroup, feature, style, squaredTolerance, listener, thisArg) {
+ var loading = false;
+ var imageStyle, imageState;
+ imageStyle = style.getImage();
+ if (imageStyle) {
+ imageState = imageStyle.getImageState();
+ if (imageState == ol.Image.State.LOADED ||
+ imageState == ol.Image.State.ERROR) {
+ imageStyle.unlistenImageChange(listener, thisArg);
+ } else {
+ if (imageState == ol.Image.State.IDLE) {
+ imageStyle.load();
+ }
+ imageState = imageStyle.getImageState();
+ ol.DEBUG && console.assert(imageState == ol.Image.State.LOADING,
+ 'imageState should be LOADING');
+ imageStyle.listenImageChange(listener, thisArg);
+ loading = true;
+ }
+ }
+ ol.renderer.vector.renderFeature_(replayGroup, feature, style,
+ squaredTolerance);
+ return loading;
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {ol.style.Style} style Style.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @private
+ */
+ol.renderer.vector.renderFeature_ = function(
+ replayGroup, feature, style, squaredTolerance) {
+ var geometry = style.getGeometryFunction()(feature);
+ if (!geometry) {
+ return;
+ }
+ var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
+ var geometryRenderer =
+ ol.renderer.vector.GEOMETRY_RENDERERS_[simplifiedGeometry.getType()];
+ geometryRenderer(replayGroup, simplifiedGeometry, style, feature);
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.GeometryCollection} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderGeometryCollectionGeometry_ = function(replayGroup, geometry, style, feature) {
+ var geometries = geometry.getGeometriesArray();
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ var geometryRenderer =
+ ol.renderer.vector.GEOMETRY_RENDERERS_[geometries[i].getType()];
+ geometryRenderer(replayGroup, geometries[i], style, feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.LineString|ol.render.Feature} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderLineStringGeometry_ = function(replayGroup, geometry, style, feature) {
+ var strokeStyle = style.getStroke();
+ if (strokeStyle) {
+ var lineStringReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.LINE_STRING);
+ lineStringReplay.setFillStrokeStyle(null, strokeStyle);
+ lineStringReplay.drawLineString(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ textReplay.drawText(geometry.getFlatMidpoint(), 0, 2, 2, geometry, feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.MultiLineString|ol.render.Feature} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderMultiLineStringGeometry_ = function(replayGroup, geometry, style, feature) {
+ var strokeStyle = style.getStroke();
+ if (strokeStyle) {
+ var lineStringReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.LINE_STRING);
+ lineStringReplay.setFillStrokeStyle(null, strokeStyle);
+ lineStringReplay.drawMultiLineString(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ var flatMidpointCoordinates = geometry.getFlatMidpoints();
+ textReplay.drawText(flatMidpointCoordinates, 0,
+ flatMidpointCoordinates.length, 2, geometry, feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.MultiPolygon} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderMultiPolygonGeometry_ = function(replayGroup, geometry, style, feature) {
+ var fillStyle = style.getFill();
+ var strokeStyle = style.getStroke();
+ if (strokeStyle || fillStyle) {
+ var polygonReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.POLYGON);
+ polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
+ polygonReplay.drawMultiPolygon(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ var flatInteriorPointCoordinates = geometry.getFlatInteriorPoints();
+ textReplay.drawText(flatInteriorPointCoordinates, 0,
+ flatInteriorPointCoordinates.length, 2, geometry, feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.Point|ol.render.Feature} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderPointGeometry_ = function(replayGroup, geometry, style, feature) {
+ var imageStyle = style.getImage();
+ if (imageStyle) {
+ if (imageStyle.getImageState() != ol.Image.State.LOADED) {
+ return;
+ }
+ var imageReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.IMAGE);
+ imageReplay.setImageStyle(imageStyle);
+ imageReplay.drawPoint(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ textReplay.drawText(geometry.getFlatCoordinates(), 0, 2, 2, geometry,
+ feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.MultiPoint|ol.render.Feature} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderMultiPointGeometry_ = function(replayGroup, geometry, style, feature) {
+ var imageStyle = style.getImage();
+ if (imageStyle) {
+ if (imageStyle.getImageState() != ol.Image.State.LOADED) {
+ return;
+ }
+ var imageReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.IMAGE);
+ imageReplay.setImageStyle(imageStyle);
+ imageReplay.drawMultiPoint(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ var flatCoordinates = geometry.getFlatCoordinates();
+ textReplay.drawText(flatCoordinates, 0, flatCoordinates.length,
+ geometry.getStride(), geometry, feature);
+ }
+};
+
+
+/**
+ * @param {ol.render.ReplayGroup} replayGroup Replay group.
+ * @param {ol.geom.Polygon|ol.render.Feature} geometry Geometry.
+ * @param {ol.style.Style} style Style.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @private
+ */
+ol.renderer.vector.renderPolygonGeometry_ = function(replayGroup, geometry, style, feature) {
+ var fillStyle = style.getFill();
+ var strokeStyle = style.getStroke();
+ if (fillStyle || strokeStyle) {
+ var polygonReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.POLYGON);
+ polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
+ polygonReplay.drawPolygon(geometry, feature);
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ var textReplay = replayGroup.getReplay(
+ style.getZIndex(), ol.render.ReplayType.TEXT);
+ textReplay.setTextStyle(textStyle);
+ textReplay.drawText(
+ geometry.getFlatInteriorPoint(), 0, 2, 2, geometry, feature);
+ }
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<ol.geom.GeometryType,
+ * function(ol.render.ReplayGroup, ol.geom.Geometry,
+ * ol.style.Style, Object)>}
+ */
+ol.renderer.vector.GEOMETRY_RENDERERS_ = {
+ 'Point': ol.renderer.vector.renderPointGeometry_,
+ 'LineString': ol.renderer.vector.renderLineStringGeometry_,
+ 'Polygon': ol.renderer.vector.renderPolygonGeometry_,
+ 'MultiPoint': ol.renderer.vector.renderMultiPointGeometry_,
+ 'MultiLineString': ol.renderer.vector.renderMultiLineStringGeometry_,
+ 'MultiPolygon': ol.renderer.vector.renderMultiPolygonGeometry_,
+ 'GeometryCollection': ol.renderer.vector.renderGeometryCollectionGeometry_,
+ 'Circle': ol.renderer.vector.renderCircleGeometry_
+};
+
+goog.provide('ol.renderer.canvas.VectorLayer');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.dom');
+goog.require('ol.extent');
+goog.require('ol.render.Event');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.ReplayGroup');
+goog.require('ol.renderer.canvas.Layer');
+goog.require('ol.renderer.vector');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.canvas.Layer}
+ * @param {ol.layer.Vector} vectorLayer Vector layer.
+ */
+ol.renderer.canvas.VectorLayer = function(vectorLayer) {
+
+ ol.renderer.canvas.Layer.call(this, vectorLayer);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.dirty_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedResolution_ = NaN;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.renderedExtent_ = ol.extent.createEmpty();
+
+ /**
+ * @private
+ * @type {function(ol.Feature, ol.Feature): number|null}
+ */
+ this.renderedRenderOrder_ = null;
+
+ /**
+ * @private
+ * @type {ol.render.canvas.ReplayGroup}
+ */
+ this.replayGroup_ = null;
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.context_ = ol.dom.createCanvasContext2D();
+
+};
+ol.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) {
+
+ var extent = frameState.extent;
+ var pixelRatio = frameState.pixelRatio;
+ var skippedFeatureUids = layerState.managed ?
+ frameState.skippedFeatureUids : {};
+ var viewState = frameState.viewState;
+ var projection = viewState.projection;
+ var rotation = viewState.rotation;
+ var projectionExtent = projection.getExtent();
+ var vectorSource = /** @type {ol.source.Vector} */ (this.getLayer().getSource());
+
+ var transform = this.getTransform(frameState, 0);
+
+ this.preCompose(context, frameState, transform);
+
+ // clipped rendering if layer extent is set
+ var clipExtent = layerState.extent;
+ var clipped = clipExtent !== undefined;
+ if (clipped) {
+ this.clip(context, frameState, /** @type {ol.Extent} */ (clipExtent));
+ }
+ var replayGroup = this.replayGroup_;
+ if (replayGroup && !replayGroup.isEmpty()) {
+ var layer = this.getLayer();
+ var drawOffsetX = 0;
+ var drawOffsetY = 0;
+ var replayContext;
+ if (layer.hasListener(ol.render.Event.Type.RENDER)) {
+ var drawWidth = context.canvas.width;
+ var drawHeight = context.canvas.height;
+ if (rotation) {
+ var drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight));
+ drawOffsetX = (drawSize - drawWidth) / 2;
+ drawOffsetY = (drawSize - drawHeight) / 2;
+ drawWidth = drawHeight = drawSize;
+ }
+ // resize and clear
+ this.context_.canvas.width = drawWidth;
+ this.context_.canvas.height = drawHeight;
+ replayContext = this.context_;
+ } else {
+ replayContext = context;
+ }
+ // for performance reasons, context.save / context.restore is not used
+ // to save and restore the transformation matrix and the opacity.
+ // see http://jsperf.com/context-save-restore-versus-variable
+ var alpha = replayContext.globalAlpha;
+ replayContext.globalAlpha = layerState.opacity;
+ if (replayContext != context) {
+ replayContext.translate(drawOffsetX, drawOffsetY);
+ }
+
+ var width = frameState.size[0] * pixelRatio;
+ var height = frameState.size[1] * pixelRatio;
+ ol.render.canvas.rotateAtOffset(replayContext, -rotation,
+ width / 2, height / 2);
+ replayGroup.replay(replayContext, pixelRatio, transform, rotation,
+ skippedFeatureUids);
+ if (vectorSource.getWrapX() && projection.canWrapX() &&
+ !ol.extent.containsExtent(projectionExtent, extent)) {
+ var startX = extent[0];
+ var worldWidth = ol.extent.getWidth(projectionExtent);
+ var world = 0;
+ var offsetX;
+ while (startX < projectionExtent[0]) {
+ --world;
+ offsetX = worldWidth * world;
+ transform = this.getTransform(frameState, offsetX);
+ replayGroup.replay(replayContext, pixelRatio, transform, rotation,
+ skippedFeatureUids);
+ startX += worldWidth;
+ }
+ world = 0;
+ startX = extent[2];
+ while (startX > projectionExtent[2]) {
+ ++world;
+ offsetX = worldWidth * world;
+ transform = this.getTransform(frameState, offsetX);
+ replayGroup.replay(replayContext, pixelRatio, transform, rotation,
+ skippedFeatureUids);
+ startX -= worldWidth;
+ }
+ // restore original transform for render and compose events
+ transform = this.getTransform(frameState, 0);
+ }
+ ol.render.canvas.rotateAtOffset(replayContext, rotation,
+ width / 2, height / 2);
+
+ if (replayContext != context) {
+ this.dispatchRenderEvent(replayContext, frameState, transform);
+ context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY);
+ replayContext.translate(-drawOffsetX, -drawOffsetY);
+ }
+ replayContext.globalAlpha = alpha;
+ }
+
+ if (clipped) {
+ context.restore();
+ }
+ this.postCompose(context, frameState, layerState, transform);
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) {
+ if (!this.replayGroup_) {
+ return undefined;
+ } else {
+ var resolution = frameState.viewState.resolution;
+ var rotation = frameState.viewState.rotation;
+ var layer = this.getLayer();
+ /** @type {Object.<string, boolean>} */
+ var features = {};
+ return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution,
+ rotation, hitTolerance, {},
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ var key = ol.getUid(feature).toString();
+ if (!(key in features)) {
+ features[key] = true;
+ return callback.call(thisArg, feature, layer);
+ }
+ });
+ }
+};
+
+
+/**
+ * Handle changes in image style state.
+ * @param {ol.events.Event} event Image style change event.
+ * @private
+ */
+ol.renderer.canvas.VectorLayer.prototype.handleStyleImageChange_ = function(event) {
+ this.renderIfReadyAndVisible();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, layerState) {
+
+ var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
+ var vectorSource = vectorLayer.getSource();
+
+ this.updateAttributions(
+ frameState.attributions, vectorSource.getAttributions());
+ this.updateLogos(frameState, vectorSource);
+
+ var animating = frameState.viewHints[ol.View.Hint.ANIMATING];
+ var interacting = frameState.viewHints[ol.View.Hint.INTERACTING];
+ var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating();
+ var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting();
+
+ if (!this.dirty_ && (!updateWhileAnimating && animating) ||
+ (!updateWhileInteracting && interacting)) {
+ return true;
+ }
+
+ var frameStateExtent = frameState.extent;
+ var viewState = frameState.viewState;
+ var projection = viewState.projection;
+ var resolution = viewState.resolution;
+ var pixelRatio = frameState.pixelRatio;
+ var vectorLayerRevision = vectorLayer.getRevision();
+ var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
+ var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
+
+ if (vectorLayerRenderOrder === undefined) {
+ vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
+ }
+
+ var extent = ol.extent.buffer(frameStateExtent,
+ vectorLayerRenderBuffer * resolution);
+ var projectionExtent = viewState.projection.getExtent();
+
+ if (vectorSource.getWrapX() && viewState.projection.canWrapX() &&
+ !ol.extent.containsExtent(projectionExtent, frameState.extent)) {
+ // For the replay group, we need an extent that intersects the real world
+ // (-180° to +180°). To support geometries in a coordinate range from -540°
+ // to +540°, we add at least 1 world width on each side of the projection
+ // extent. If the viewport is wider than the world, we need to add half of
+ // the viewport width to make sure we cover the whole viewport.
+ var worldWidth = ol.extent.getWidth(projectionExtent);
+ var buffer = Math.max(ol.extent.getWidth(extent) / 2, worldWidth);
+ extent[0] = projectionExtent[0] - buffer;
+ extent[2] = projectionExtent[2] + buffer;
+ }
+
+ if (!this.dirty_ &&
+ this.renderedResolution_ == resolution &&
+ this.renderedRevision_ == vectorLayerRevision &&
+ this.renderedRenderOrder_ == vectorLayerRenderOrder &&
+ ol.extent.containsExtent(this.renderedExtent_, extent)) {
+ return true;
+ }
+
+ this.replayGroup_ = null;
+
+ this.dirty_ = false;
+
+ var replayGroup =
+ new ol.render.canvas.ReplayGroup(
+ ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
+ resolution, vectorSource.getOverlaps(), vectorLayer.getRenderBuffer());
+ vectorSource.loadFeatures(extent, resolution, projection);
+ /**
+ * @param {ol.Feature} feature Feature.
+ * @this {ol.renderer.canvas.VectorLayer}
+ */
+ var renderFeature = function(feature) {
+ var styles;
+ var styleFunction = feature.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction.call(feature, resolution);
+ } else {
+ styleFunction = vectorLayer.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction(feature, resolution);
+ }
+ }
+ if (styles) {
+ var dirty = this.renderFeature(
+ feature, resolution, pixelRatio, styles, replayGroup);
+ this.dirty_ = this.dirty_ || dirty;
+ }
+ };
+ if (vectorLayerRenderOrder) {
+ /** @type {Array.<ol.Feature>} */
+ var features = [];
+ vectorSource.forEachFeatureInExtent(extent,
+ /**
+ * @param {ol.Feature} feature Feature.
+ */
+ function(feature) {
+ features.push(feature);
+ }, this);
+ features.sort(vectorLayerRenderOrder);
+ features.forEach(renderFeature, this);
+ } else {
+ vectorSource.forEachFeatureInExtent(extent, renderFeature, this);
+ }
+ replayGroup.finish();
+
+ this.renderedResolution_ = resolution;
+ this.renderedRevision_ = vectorLayerRevision;
+ this.renderedRenderOrder_ = vectorLayerRenderOrder;
+ this.renderedExtent_ = extent;
+ this.replayGroup_ = replayGroup;
+
+ return true;
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
+ * styles.
+ * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
+ * @return {boolean} `true` if an image is loading.
+ */
+ol.renderer.canvas.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) {
+ if (!styles) {
+ return false;
+ }
+ var loading = false;
+ if (Array.isArray(styles)) {
+ for (var i = 0, ii = styles.length; i < ii; ++i) {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles[i],
+ ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
+ this.handleStyleImageChange_, this) || loading;
+ }
+ } else {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles,
+ ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
+ this.handleStyleImageChange_, this) || loading;
+ }
+ return loading;
+};
+
+goog.provide('ol.renderer.canvas.VectorTileLayer');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.proj');
+goog.require('ol.proj.Units');
+goog.require('ol.layer.VectorTile');
+goog.require('ol.render.ReplayType');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.ReplayGroup');
+goog.require('ol.render.replay');
+goog.require('ol.renderer.canvas.TileLayer');
+goog.require('ol.renderer.vector');
+goog.require('ol.size');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.canvas.TileLayer}
+ * @param {ol.layer.VectorTile} layer VectorTile layer.
+ */
+ol.renderer.canvas.VectorTileLayer = function(layer) {
+
+ ol.renderer.canvas.TileLayer.call(this, layer);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.dirty_ = false;
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.tmpTransform_ = ol.transform.create();
+
+ // Use lower resolution for pure vector rendering. Closest resolution otherwise.
+ this.zDirection =
+ layer.getRenderMode() == ol.layer.VectorTile.RenderType.VECTOR ? 1 : 0;
+
+};
+ol.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.TileLayer);
+
+
+/**
+ * @const
+ * @type {!Object.<string, Array.<ol.render.ReplayType>>}
+ */
+ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS = {
+ 'image': ol.render.replay.ORDER,
+ 'hybrid': [ol.render.ReplayType.POLYGON, ol.render.ReplayType.LINE_STRING]
+};
+
+
+/**
+ * @const
+ * @type {!Object.<string, Array.<ol.render.ReplayType>>}
+ */
+ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS = {
+ 'hybrid': [ol.render.ReplayType.IMAGE, ol.render.ReplayType.TEXT],
+ 'vector': ol.render.replay.ORDER
+};
+
+
+/**
+ * @param {ol.VectorTile} tile Tile.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function(tile,
+ frameState) {
+ var layer = this.getLayer();
+ var pixelRatio = frameState.pixelRatio;
+ var projection = frameState.viewState.projection;
+ var revision = layer.getRevision();
+ var renderOrder = layer.getRenderOrder() || null;
+
+ var replayState = tile.getReplayState();
+ if (!replayState.dirty && replayState.renderedRevision == revision &&
+ replayState.renderedRenderOrder == renderOrder) {
+ return;
+ }
+
+ replayState.replayGroup = null;
+ replayState.dirty = false;
+
+ var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
+ var tileGrid = source.getTileGrid();
+ var tileCoord = tile.tileCoord;
+ var tileProjection = tile.getProjection();
+ var resolution = tileGrid.getResolution(tileCoord[0]);
+ var extent, reproject, tileResolution;
+ if (tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS) {
+ var tilePixelRatio = tileResolution = source.getTilePixelRatio();
+ var tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0]));
+ extent = [0, 0, tileSize[0] * tilePixelRatio, tileSize[1] * tilePixelRatio];
+ } else {
+ tileResolution = resolution;
+ extent = tileGrid.getTileCoordExtent(tileCoord);
+ if (!ol.proj.equivalent(projection, tileProjection)) {
+ reproject = true;
+ tile.setProjection(projection);
+ }
+ }
+ replayState.dirty = false;
+ var replayGroup = new ol.render.canvas.ReplayGroup(0, extent,
+ tileResolution, source.getOverlaps(), layer.getRenderBuffer());
+ var squaredTolerance = ol.renderer.vector.getSquaredTolerance(
+ tileResolution, pixelRatio);
+
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @this {ol.renderer.canvas.VectorTileLayer}
+ */
+ function renderFeature(feature) {
+ var styles;
+ var styleFunction = feature.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction.call(/** @type {ol.Feature} */ (feature), resolution);
+ } else {
+ styleFunction = layer.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction(feature, resolution);
+ }
+ }
+ if (styles) {
+ if (!Array.isArray(styles)) {
+ styles = [styles];
+ }
+ var dirty = this.renderFeature(feature, squaredTolerance, styles,
+ replayGroup);
+ this.dirty_ = this.dirty_ || dirty;
+ replayState.dirty = replayState.dirty || dirty;
+ }
+ }
+
+ var features = tile.getFeatures();
+ if (renderOrder && renderOrder !== replayState.renderedRenderOrder) {
+ features.sort(renderOrder);
+ }
+ var feature;
+ for (var i = 0, ii = features.length; i < ii; ++i) {
+ feature = features[i];
+ if (reproject) {
+ feature.getGeometry().transform(tileProjection, projection);
+ }
+ renderFeature.call(this, feature);
+ }
+
+ replayGroup.finish();
+
+ replayState.renderedRevision = revision;
+ replayState.renderedRenderOrder = renderOrder;
+ replayState.replayGroup = replayGroup;
+ replayState.resolution = NaN;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.drawTileImage = function(
+ tile, frameState, layerState, x, y, w, h, gutter) {
+ var vectorTile = /** @type {ol.VectorTile} */ (tile);
+ this.createReplayGroup_(vectorTile, frameState);
+ var layer = this.getLayer();
+ if (layer.getRenderMode() != ol.layer.VectorTile.RenderType.VECTOR) {
+ this.renderTileImage_(vectorTile, frameState, layerState);
+ }
+ ol.renderer.canvas.TileLayer.prototype.drawTileImage.apply(this, arguments);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) {
+ var resolution = frameState.viewState.resolution;
+ var rotation = frameState.viewState.rotation;
+ hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
+ var layer = this.getLayer();
+ /** @type {Object.<string, boolean>} */
+ var features = {};
+
+ /** @type {Array.<ol.VectorTile>} */
+ var replayables = this.renderedTiles;
+
+ var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
+ var tileGrid = source.getTileGrid();
+ var found, tileSpaceCoordinate;
+ var i, ii, origin, replayGroup;
+ var tile, tileCoord, tileExtent, tilePixelRatio, tileResolution;
+ for (i = 0, ii = replayables.length; i < ii; ++i) {
+ tile = replayables[i];
+ tileCoord = tile.tileCoord;
+ tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord, this.tmpExtent);
+ if (!ol.extent.containsCoordinate(ol.extent.buffer(tileExtent, hitTolerance * resolution), coordinate)) {
+ continue;
+ }
+ if (tile.getProjection().getUnits() === ol.proj.Units.TILE_PIXELS) {
+ origin = ol.extent.getTopLeft(tileExtent);
+ tilePixelRatio = source.getTilePixelRatio();
+ tileResolution = tileGrid.getResolution(tileCoord[0]) / tilePixelRatio;
+ tileSpaceCoordinate = [
+ (coordinate[0] - origin[0]) / tileResolution,
+ (origin[1] - coordinate[1]) / tileResolution
+ ];
+ resolution = tilePixelRatio;
+ } else {
+ tileSpaceCoordinate = coordinate;
+ }
+ replayGroup = tile.getReplayState().replayGroup;
+ found = found || replayGroup.forEachFeatureAtCoordinate(
+ tileSpaceCoordinate, resolution, rotation, hitTolerance, {},
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ var key = ol.getUid(feature).toString();
+ if (!(key in features)) {
+ features[key] = true;
+ return callback.call(thisArg, feature, layer);
+ }
+ });
+ }
+ return found;
+};
+
+
+/**
+ * @param {ol.Tile} tile Tile.
+ * @param {olx.FrameState} frameState Frame state.
+ * @return {ol.Transform} transform Transform.
+ * @private
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.getReplayTransform_ = function(tile, frameState) {
+ if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) {
+ var layer = this.getLayer();
+ var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
+ var tileGrid = source.getTileGrid();
+ var tileCoord = tile.tileCoord;
+ var tileResolution =
+ tileGrid.getResolution(tileCoord[0]) / source.getTilePixelRatio();
+ var viewState = frameState.viewState;
+ var pixelRatio = frameState.pixelRatio;
+ var renderResolution = viewState.resolution / pixelRatio;
+ var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
+ var center = viewState.center;
+ var origin = ol.extent.getTopLeft(tileExtent);
+ var size = frameState.size;
+ var offsetX = Math.round(pixelRatio * size[0] / 2);
+ var offsetY = Math.round(pixelRatio * size[1] / 2);
+ return ol.transform.compose(this.tmpTransform_,
+ offsetX, offsetY,
+ tileResolution / renderResolution, tileResolution / renderResolution,
+ viewState.rotation,
+ (origin[0] - center[0]) / tileResolution,
+ (center[1] - origin[1]) / tileResolution);
+ } else {
+ return this.getTransform(frameState, 0);
+ }
+};
+
+
+/**
+ * Handle changes in image style state.
+ * @param {ol.events.Event} event Image style change event.
+ * @private
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ = function(event) {
+ this.renderIfReadyAndVisible();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, frameState, layerState) {
+ var renderMode = this.getLayer().getRenderMode();
+ var replays = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode];
+ if (replays) {
+ var pixelRatio = frameState.pixelRatio;
+ var rotation = frameState.viewState.rotation;
+ var size = frameState.size;
+ var offsetX = Math.round(pixelRatio * size[0] / 2);
+ var offsetY = Math.round(pixelRatio * size[1] / 2);
+ var tiles = this.renderedTiles;
+ var clips = [];
+ var zs = [];
+ for (var i = tiles.length - 1; i >= 0; --i) {
+ var tile = /** @type {ol.VectorTile} */ (tiles[i]);
+ // Create a clip mask for regions in this low resolution tile that are
+ // already filled by a higher resolution tile
+ var transform = this.getReplayTransform_(tile, frameState);
+ var currentClip = tile.getReplayState().replayGroup.getClipCoords(transform);
+ var currentZ = tile.tileCoord[0];
+ context.save();
+ context.globalAlpha = layerState.opacity;
+ ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY);
+ for (var j = 0, jj = clips.length; j < jj; ++j) {
+ var clip = clips[j];
+ if (currentZ < zs[j]) {
+ context.beginPath();
+ // counter-clockwise (outer ring) for current tile
+ context.moveTo(currentClip[0], currentClip[1]);
+ context.lineTo(currentClip[2], currentClip[3]);
+ context.lineTo(currentClip[4], currentClip[5]);
+ context.lineTo(currentClip[6], currentClip[7]);
+ // clockwise (inner ring) for higher resolution tile
+ context.moveTo(clip[6], clip[7]);
+ context.lineTo(clip[4], clip[5]);
+ context.lineTo(clip[2], clip[3]);
+ context.lineTo(clip[0], clip[1]);
+ context.clip();
+ }
+ }
+ var replayGroup = tile.getReplayState().replayGroup;
+ replayGroup.replay(context, pixelRatio, transform, rotation, {}, replays);
+ context.restore();
+ clips.push(currentClip);
+ zs.push(currentZ);
+ }
+ }
+ ol.renderer.canvas.TileLayer.prototype.postCompose.apply(this, arguments);
+};
+
+
+/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
+ * styles.
+ * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
+ * @return {boolean} `true` if an image is loading.
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, squaredTolerance, styles, replayGroup) {
+ if (!styles) {
+ return false;
+ }
+ var loading = false;
+ if (Array.isArray(styles)) {
+ for (var i = 0, ii = styles.length; i < ii; ++i) {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles[i], squaredTolerance,
+ this.handleStyleImageChange_, this) || loading;
+ }
+ } else {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles, squaredTolerance,
+ this.handleStyleImageChange_, this) || loading;
+ }
+ return loading;
+};
+
+
+/**
+ * @param {ol.VectorTile} tile Tile.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @private
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function(
+ tile, frameState, layerState) {
+ var layer = this.getLayer();
+ var replayState = tile.getReplayState();
+ var revision = layer.getRevision();
+ var replays = ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS[layer.getRenderMode()];
+ if (replays && replayState.renderedTileRevision !== revision) {
+ replayState.renderedTileRevision = revision;
+ var tileCoord = tile.tileCoord;
+ var z = tile.tileCoord[0];
+ var pixelRatio = frameState.pixelRatio;
+ var source = layer.getSource();
+ var tileGrid = source.getTileGrid();
+ var tilePixelRatio = source.getTilePixelRatio();
+ var transform = ol.transform.reset(this.tmpTransform_);
+ if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) {
+ var renderPixelRatio = pixelRatio / tilePixelRatio;
+ ol.transform.scale(transform, renderPixelRatio, renderPixelRatio);
+ } else {
+ var resolution = tileGrid.getResolution(z);
+ var pixelScale = pixelRatio / resolution;
+ var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
+ ol.transform.scale(transform, pixelScale, -pixelScale);
+ ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]);
+ }
+
+ var context = tile.getContext();
+ var size = source.getTilePixelSize(z, pixelRatio, frameState.viewState.projection);
+ context.canvas.width = size[0];
+ context.canvas.height = size[1];
+ replayState.replayGroup.replay(context, pixelRatio, transform, 0, {}, replays);
+ }
+};
+
+// FIXME offset panning
+
+goog.provide('ol.renderer.canvas.Map');
+
+goog.require('ol.transform');
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.css');
+goog.require('ol.dom');
+goog.require('ol.layer.Image');
+goog.require('ol.layer.Layer');
+goog.require('ol.layer.Tile');
+goog.require('ol.layer.Vector');
+goog.require('ol.layer.VectorTile');
+goog.require('ol.render.Event');
+goog.require('ol.render.canvas');
+goog.require('ol.render.canvas.Immediate');
+goog.require('ol.renderer.Map');
+goog.require('ol.renderer.Type');
+goog.require('ol.renderer.canvas.ImageLayer');
+goog.require('ol.renderer.canvas.TileLayer');
+goog.require('ol.renderer.canvas.VectorLayer');
+goog.require('ol.renderer.canvas.VectorTileLayer');
+goog.require('ol.source.State');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.Map}
+ * @param {Element} container Container.
+ * @param {ol.Map} map Map.
+ */
+ol.renderer.canvas.Map = function(container, map) {
+
+ ol.renderer.Map.call(this, container, map);
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.context_ = ol.dom.createCanvasContext2D();
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = this.context_.canvas;
+
+ this.canvas_.style.width = '100%';
+ this.canvas_.style.height = '100%';
+ this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
+ container.insertBefore(this.canvas_, container.childNodes[0] || null);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.renderedVisible_ = true;
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.transform_ = ol.transform.create();
+
+};
+ol.inherits(ol.renderer.canvas.Map, ol.renderer.Map);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) {
+ if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
+ return new ol.renderer.canvas.ImageLayer(layer);
+ } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
+ return new ol.renderer.canvas.TileLayer(layer);
+ } else if (ol.ENABLE_VECTOR_TILE && layer instanceof ol.layer.VectorTile) {
+ return new ol.renderer.canvas.VectorTileLayer(layer);
+ } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
+ return new ol.renderer.canvas.VectorLayer(layer);
+ } else {
+ ol.DEBUG && console.assert(false, 'unexpected layer configuration');
+ return null;
+ }
+};
+
+
+/**
+ * @param {ol.render.Event.Type} type Event type.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = function(type, frameState) {
+ var map = this.getMap();
+ var context = this.context_;
+ if (map.hasListener(type)) {
+ var extent = frameState.extent;
+ var pixelRatio = frameState.pixelRatio;
+ var viewState = frameState.viewState;
+ var rotation = viewState.rotation;
+
+ var transform = this.getTransform(frameState);
+
+ var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio,
+ extent, transform, rotation);
+ var composeEvent = new ol.render.Event(type, vectorContext,
+ frameState, context, null);
+ map.dispatchEvent(composeEvent);
+ }
+};
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @protected
+ * @return {!ol.Transform} Transform.
+ */
+ol.renderer.canvas.Map.prototype.getTransform = function(frameState) {
+ var viewState = frameState.viewState;
+ var dx1 = this.canvas_.width / 2;
+ var dy1 = this.canvas_.height / 2;
+ var sx = frameState.pixelRatio / viewState.resolution;
+ var sy = -sx;
+ var angle = -viewState.rotation;
+ var dx2 = -viewState.center[0];
+ var dy2 = -viewState.center[1];
+ return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.Map.prototype.getType = function() {
+ return ol.renderer.Type.CANVAS;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
+
+ if (!frameState) {
+ if (this.renderedVisible_) {
+ this.canvas_.style.display = 'none';
+ this.renderedVisible_ = false;
+ }
+ return;
+ }
+
+ var context = this.context_;
+ var pixelRatio = frameState.pixelRatio;
+ var width = Math.round(frameState.size[0] * pixelRatio);
+ var height = Math.round(frameState.size[1] * pixelRatio);
+ if (this.canvas_.width != width || this.canvas_.height != height) {
+ this.canvas_.width = width;
+ this.canvas_.height = height;
+ } else {
+ context.clearRect(0, 0, width, height);
+ }
+
+ var rotation = frameState.viewState.rotation;
+
+ this.calculateMatrices2D(frameState);
+
+ this.dispatchComposeEvent_(ol.render.Event.Type.PRECOMPOSE, frameState);
+
+ var layerStatesArray = frameState.layerStatesArray;
+ ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex);
+
+ ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2);
+
+ var viewResolution = frameState.viewState.resolution;
+ var i, ii, layer, layerRenderer, layerState;
+ for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
+ layerState = layerStatesArray[i];
+ layer = layerState.layer;
+ layerRenderer = /** @type {ol.renderer.canvas.Layer} */ (this.getLayerRenderer(layer));
+ if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) ||
+ layerState.sourceState != ol.source.State.READY) {
+ continue;
+ }
+ if (layerRenderer.prepareFrame(frameState, layerState)) {
+ layerRenderer.composeFrame(frameState, layerState, context);
+ }
+ }
+
+ ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2);
+
+ this.dispatchComposeEvent_(
+ ol.render.Event.Type.POSTCOMPOSE, frameState);
+
+ if (!this.renderedVisible_) {
+ this.canvas_.style.display = '';
+ this.renderedVisible_ = true;
+ }
+
+ this.scheduleRemoveUnusedLayerRenderers(frameState);
+ this.scheduleExpireIconCache(frameState);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg,
+ layerFilter, thisArg2) {
+ var result;
+ var viewState = frameState.viewState;
+ var viewResolution = viewState.resolution;
+
+ var layerStates = frameState.layerStatesArray;
+ var numLayers = layerStates.length;
+
+ var coordinate = ol.transform.apply(
+ frameState.pixelToCoordinateTransform, pixel.slice());
+
+ var i;
+ for (i = numLayers - 1; i >= 0; --i) {
+ var layerState = layerStates[i];
+ var layer = layerState.layer;
+ if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
+ layerFilter.call(thisArg2, layer)) {
+ var layerRenderer = /** @type {ol.renderer.canvas.Layer} */ (this.getLayerRenderer(layer));
+ result = layerRenderer.forEachLayerAtCoordinate(
+ coordinate, frameState, callback, thisArg);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return undefined;
+};
+
+goog.provide('ol.render.webgl');
+
+/**
+ * @const
+ * @type {ol.Color}
+ */
+ol.render.webgl.defaultFillStyle = [0.0, 0.0, 0.0, 1.0];
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.defaultLineCap = 'round';
+
+
+/**
+ * @const
+ * @type {Array.<number>}
+ */
+ol.render.webgl.defaultLineDash = [];
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.defaultLineJoin = 'round';
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.render.webgl.defaultMiterLimit = 10;
+
+/**
+ * @const
+ * @type {ol.Color}
+ */
+ol.render.webgl.defaultStrokeStyle = [0.0, 0.0, 0.0, 1.0];
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.render.webgl.defaultLineWidth = 1;
+
+/**
+ * @enum {number}
+ */
+ol.render.webgl.lineStringInstruction = {
+ ROUND: 2,
+ BEGIN_LINE: 3,
+ END_LINE: 5,
+ BEGIN_LINE_CAP: 7,
+ END_LINE_CAP : 11,
+ BEVEL_FIRST: 13,
+ BEVEL_SECOND: 17,
+ MITER_BOTTOM: 19,
+ MITER_TOP: 23
+};
+
+/**
+ * Calculates the orientation of a triangle based on the determinant method.
+ * @param {number} x1 First X coordinate.
+ * @param {number} y1 First Y coordinate.
+ * @param {number} x2 Second X coordinate.
+ * @param {number} y2 Second Y coordinate.
+ * @param {number} x3 Third X coordinate.
+ * @param {number} y3 Third Y coordinate.
+ * @return {boolean|undefined} Triangle is clockwise.
+ */
+ol.render.webgl.triangleIsCounterClockwise = function(x1, y1, x2, y2, x3, y3) {
+ var area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
+ return (area <= ol.render.webgl.EPSILON && area >= -ol.render.webgl.EPSILON) ?
+ undefined : area > 0;
+};
+
+/**
+ * @const
+ * @type {number}
+ */
+ol.render.webgl.EPSILON = Number.EPSILON || 2.220446049250313e-16;
+
+goog.provide('ol.webgl.Shader');
+
+goog.require('ol.functions');
+goog.require('ol.webgl');
+
+
+/**
+ * @constructor
+ * @param {string} source Source.
+ * @struct
+ */
+ol.webgl.Shader = function(source) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.source_ = source;
+
+};
+
+
+/**
+ * @abstract
+ * @return {number} Type.
+ */
+ol.webgl.Shader.prototype.getType = function() {};
+
+
+/**
+ * @return {string} Source.
+ */
+ol.webgl.Shader.prototype.getSource = function() {
+ return this.source_;
+};
+
+
+/**
+ * @return {boolean} Is animated?
+ */
+ol.webgl.Shader.prototype.isAnimated = ol.functions.FALSE;
+
+goog.provide('ol.webgl.Fragment');
+
+goog.require('ol');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Shader');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Shader}
+ * @param {string} source Source.
+ * @struct
+ */
+ol.webgl.Fragment = function(source) {
+ ol.webgl.Shader.call(this, source);
+};
+ol.inherits(ol.webgl.Fragment, ol.webgl.Shader);
+
+
+/**
+ * @inheritDoc
+ */
+ol.webgl.Fragment.prototype.getType = function() {
+ return ol.webgl.FRAGMENT_SHADER;
+};
+
+goog.provide('ol.webgl.Vertex');
+
+goog.require('ol');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Shader');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Shader}
+ * @param {string} source Source.
+ * @struct
+ */
+ol.webgl.Vertex = function(source) {
+ ol.webgl.Shader.call(this, source);
+};
+ol.inherits(ol.webgl.Vertex, ol.webgl.Shader);
+
+
+/**
+ * @inheritDoc
+ */
+ol.webgl.Vertex.prototype.getType = function() {
+ return ol.webgl.VERTEX_SHADER;
+};
+
+// This file is automatically generated, do not edit
+goog.provide('ol.render.webgl.circlereplay.defaultshader');
+
+goog.require('ol');
+goog.require('ol.webgl.Fragment');
+goog.require('ol.webgl.Vertex');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Fragment}
+ * @struct
+ */
+ol.render.webgl.circlereplay.defaultshader.Fragment = function() {
+ ol.webgl.Fragment.call(this, ol.render.webgl.circlereplay.defaultshader.Fragment.SOURCE);
+};
+ol.inherits(ol.render.webgl.circlereplay.defaultshader.Fragment, ol.webgl.Fragment);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.circlereplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_center;\nvarying vec2 v_offset;\nvarying float v_halfWidth;\nvarying float v_pixelRatio;\n\n\n\nuniform float u_opacity;\nuniform vec4 u_fillColor;\nuniform vec4 u_strokeColor;\nuniform vec2 u_size;\n\nvoid main(void) {\n vec2 windowCenter = vec2((v_center.x + 1.0) / 2.0 * u_size.x * v_pixelRatio,\n (v_center.y + 1.0) / 2.0 * u_size.y * v_pixelRatio);\n vec2 windowOffset = vec2((v_offset.x + 1.0) / 2.0 * u_size.x * v_pixelRatio,\n (v_offset.y + 1.0) / 2.0 * u_size.y * v_pixelRatio);\n float radius = length(windowCenter - windowOffset);\n float dist = length(windowCenter - gl_FragCoord.xy);\n if (dist > radius + v_halfWidth) {\n if (u_strokeColor.a == 0.0) {\n gl_FragColor = u_fillColor;\n } else {\n gl_FragColor = u_strokeColor;\n }\n gl_FragColor.a = gl_FragColor.a - (dist - (radius + v_halfWidth));\n } else if (u_fillColor.a == 0.0) {\n // Hooray, no fill, just stroke. We can use real antialiasing.\n gl_FragColor = u_strokeColor;\n if (dist < radius - v_halfWidth) {\n gl_FragColor.a = gl_FragColor.a - (radius - v_halfWidth - dist);\n }\n } else {\n gl_FragColor = u_fillColor;\n float strokeDist = radius - v_halfWidth;\n float antialias = 2.0 * v_pixelRatio;\n if (dist > strokeDist) {\n gl_FragColor = u_strokeColor;\n } else if (dist >= strokeDist - antialias) {\n float step = smoothstep(strokeDist - antialias, strokeDist, dist);\n gl_FragColor = mix(u_fillColor, u_strokeColor, step);\n }\n }\n gl_FragColor.a = gl_FragColor.a * u_opacity;\n if (gl_FragColor.a <= 0.0) {\n discard;\n }\n}\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.circlereplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying vec2 b;varying float c;varying float d;uniform float m;uniform vec4 n;uniform vec4 o;uniform vec2 p;void main(void){vec2 windowCenter=vec2((a.x+1.0)/2.0*p.x*d,(a.y+1.0)/2.0*p.y*d);vec2 windowOffset=vec2((b.x+1.0)/2.0*p.x*d,(b.y+1.0)/2.0*p.y*d);float radius=length(windowCenter-windowOffset);float dist=length(windowCenter-gl_FragCoord.xy);if(dist>radius+c){if(o.a==0.0){gl_FragColor=n;}else{gl_FragColor=o;}gl_FragColor.a=gl_FragColor.a-(dist-(radius+c));}else if(n.a==0.0){gl_FragColor=o;if(dist<radius-c){gl_FragColor.a=gl_FragColor.a-(radius-c-dist);}} else{gl_FragColor=n;float strokeDist=radius-c;float antialias=2.0*d;if(dist>strokeDist){gl_FragColor=o;}else if(dist>=strokeDist-antialias){float step=smoothstep(strokeDist-antialias,strokeDist,dist);gl_FragColor=mix(n,o,step);}} gl_FragColor.a=gl_FragColor.a*m;if(gl_FragColor.a<=0.0){discard;}}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.circlereplay.defaultshader.Fragment.SOURCE = ol.DEBUG ?
+ ol.render.webgl.circlereplay.defaultshader.Fragment.DEBUG_SOURCE :
+ ol.render.webgl.circlereplay.defaultshader.Fragment.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.circlereplay.defaultshader.fragment = new ol.render.webgl.circlereplay.defaultshader.Fragment();
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Vertex}
+ * @struct
+ */
+ol.render.webgl.circlereplay.defaultshader.Vertex = function() {
+ ol.webgl.Vertex.call(this, ol.render.webgl.circlereplay.defaultshader.Vertex.SOURCE);
+};
+ol.inherits(ol.render.webgl.circlereplay.defaultshader.Vertex, ol.webgl.Vertex);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.circlereplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_center;\nvarying vec2 v_offset;\nvarying float v_halfWidth;\nvarying float v_pixelRatio;\n\n\nattribute vec2 a_position;\nattribute float a_instruction;\nattribute float a_radius;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\nuniform float u_lineWidth;\nuniform float u_pixelRatio;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n v_center = vec4(u_projectionMatrix * vec4(a_position, 0.0, 1.0)).xy;\n v_pixelRatio = u_pixelRatio;\n float lineWidth = u_lineWidth * u_pixelRatio;\n v_halfWidth = lineWidth / 2.0;\n if (lineWidth == 0.0) {\n lineWidth = 2.0 * u_pixelRatio;\n }\n vec2 offset;\n // Radius with anitaliasing (roughly).\n float radius = a_radius + 3.0 * u_pixelRatio;\n // Until we get gl_VertexID in WebGL, we store an instruction.\n if (a_instruction == 0.0) {\n // Offsetting the edges of the triangle by lineWidth / 2 is necessary, however\n // we should also leave some space for the antialiasing, thus we offset by lineWidth.\n offset = vec2(-1.0, 1.0);\n } else if (a_instruction == 1.0) {\n offset = vec2(-1.0, -1.0);\n } else if (a_instruction == 2.0) {\n offset = vec2(1.0, -1.0);\n } else {\n offset = vec2(1.0, 1.0);\n }\n\n gl_Position = u_projectionMatrix * vec4(a_position + offset * radius, 0.0, 1.0) +\n offsetMatrix * vec4(offset * lineWidth, 0.0, 0.0);\n v_offset = vec4(u_projectionMatrix * vec4(a_position.x + a_radius, a_position.y,\n 0.0, 1.0)).xy;\n\n if (distance(v_center, v_offset) > 20000.0) {\n gl_Position = vec4(v_center, 0.0, 1.0);\n }\n}\n\n\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.circlereplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying vec2 b;varying float c;varying float d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;void main(void){mat4 offsetMatrix=i*j;a=vec4(h*vec4(e,0.0,1.0)).xy;d=l;float lineWidth=k*l;c=lineWidth/2.0;if(lineWidth==0.0){lineWidth=2.0*l;}vec2 offset;float radius=g+3.0*l;if(f==0.0){offset=vec2(-1.0,1.0);}else if(f==1.0){offset=vec2(-1.0,-1.0);}else if(f==2.0){offset=vec2(1.0,-1.0);}else{offset=vec2(1.0,1.0);}gl_Position=h*vec4(e+offset*radius,0.0,1.0)+offsetMatrix*vec4(offset*lineWidth,0.0,0.0);b=vec4(h*vec4(e.x+g,e.y,0.0,1.0)).xy;if(distance(a,b)>20000.0){gl_Position=vec4(a,0.0,1.0);}}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.circlereplay.defaultshader.Vertex.SOURCE = ol.DEBUG ?
+ ol.render.webgl.circlereplay.defaultshader.Vertex.DEBUG_SOURCE :
+ ol.render.webgl.circlereplay.defaultshader.Vertex.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.circlereplay.defaultshader.vertex = new ol.render.webgl.circlereplay.defaultshader.Vertex();
+
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ * @struct
+ */
+ol.render.webgl.circlereplay.defaultshader.Locations = function(gl, program) {
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_fillColor = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_fillColor' : 'n');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_lineWidth = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_lineWidth' : 'k');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetRotateMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetRotateMatrix' : 'j');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetScaleMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetScaleMatrix' : 'i');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_opacity = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_opacity' : 'm');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_pixelRatio = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_pixelRatio' : 'l');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_projectionMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_projectionMatrix' : 'h');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_size = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_size' : 'p');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_strokeColor = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_strokeColor' : 'o');
+
+ /**
+ * @type {number}
+ */
+ this.a_instruction = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_instruction' : 'f');
+
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_position' : 'e');
+
+ /**
+ * @type {number}
+ */
+ this.a_radius = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_radius' : 'g');
+};
+
+goog.provide('ol.vec.Mat4');
+
+
+/**
+ * @return {Array.<number>} 4x4 matrix representing a 3D identity transform.
+ */
+ol.vec.Mat4.create = function() {
+ return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+};
+
+
+/**
+ * @param {Array.<number>} mat4 Flattened 4x4 matrix receiving the result.
+ * @param {ol.Transform} transform Transformation matrix.
+ * @return {Array.<number>} 2D transformation matrix as flattened 4x4 matrix.
+ */
+ol.vec.Mat4.fromTransform = function(mat4, transform) {
+ mat4[0] = transform[0];
+ mat4[1] = transform[1];
+ mat4[4] = transform[2];
+ mat4[5] = transform[3];
+ mat4[12] = transform[4];
+ mat4[13] = transform[5];
+ return mat4;
+};
+
+goog.provide('ol.render.webgl.Replay');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.render.VectorContext');
+goog.require('ol.transform');
+goog.require('ol.vec.Mat4');
+goog.require('ol.webgl');
+
+/**
+ * @constructor
+ * @extends {ol.render.VectorContext}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @struct
+ */
+ol.render.webgl.Replay = function(tolerance, maxExtent) {
+ ol.render.VectorContext.call(this);
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.tolerance = tolerance;
+
+ /**
+ * @protected
+ * @const
+ * @type {ol.Extent}
+ */
+ this.maxExtent = maxExtent;
+
+ /**
+ * The origin of the coordinate system for the point coordinates sent to
+ * the GPU. To eliminate jitter caused by precision problems in the GPU
+ * we use the "Rendering Relative to Eye" technique described in the "3D
+ * Engine Design for Virtual Globes" book.
+ * @protected
+ * @type {ol.Coordinate}
+ */
+ this.origin = ol.extent.getCenter(maxExtent);
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.projectionMatrix_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.offsetRotateMatrix_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.offsetScaleMatrix_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.tmpMat4_ = ol.vec.Mat4.create();
+
+ /**
+ * @protected
+ * @type {Array.<number>}
+ */
+ this.indices = [];
+
+ /**
+ * @protected
+ * @type {?ol.webgl.Buffer}
+ */
+ this.indicesBuffer = null;
+
+ /**
+ * Start index per feature (the index).
+ * @protected
+ * @type {Array.<number>}
+ */
+ this.startIndices = [];
+
+ /**
+ * Start index per feature (the feature).
+ * @protected
+ * @type {Array.<ol.Feature|ol.render.Feature>}
+ */
+ this.startIndicesFeature = [];
+
+ /**
+ * @protected
+ * @type {Array.<number>}
+ */
+ this.vertices = [];
+
+ /**
+ * @protected
+ * @type {?ol.webgl.Buffer}
+ */
+ this.verticesBuffer = null;
+
+ /**
+ * Optional parameter for PolygonReplay instances.
+ * @protected
+ * @type {ol.render.webgl.LineStringReplay|undefined}
+ */
+ this.lineStringReplay = undefined;
+
+};
+ol.inherits(ol.render.webgl.Replay, ol.render.VectorContext);
+
+
+/**
+ * @abstract
+ * @param {ol.webgl.Context} context WebGL context.
+ * @return {function()} Delete resources function.
+ */
+ol.render.webgl.Replay.prototype.getDeleteResourcesFunction = function(context) {};
+
+
+/**
+ * @abstract
+ * @param {ol.webgl.Context} context Context.
+ */
+ol.render.webgl.Replay.prototype.finish = function(context) {};
+
+
+/**
+ * @abstract
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {ol.render.webgl.circlereplay.defaultshader.Locations|
+ ol.render.webgl.imagereplay.defaultshader.Locations|
+ ol.render.webgl.linestringreplay.defaultshader.Locations|
+ ol.render.webgl.polygonreplay.defaultshader.Locations} Locations.
+ */
+ol.render.webgl.Replay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {};
+
+
+/**
+ * @abstract
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.render.webgl.circlereplay.defaultshader.Locations|
+ ol.render.webgl.imagereplay.defaultshader.Locations|
+ ol.render.webgl.linestringreplay.defaultshader.Locations|
+ ol.render.webgl.polygonreplay.defaultshader.Locations} locations Locations.
+ */
+ol.render.webgl.Replay.prototype.shutDownProgram = function(gl, locations) {};
+
+
+/**
+ * @abstract
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {boolean} hitDetection Hit detection mode.
+ */
+ol.render.webgl.Replay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {};
+
+
+/**
+ * @abstract
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
+ * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
+ * this extent are checked.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.Replay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {};
+
+
+/**
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
+ * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
+ * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
+ * this extent are checked.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.Replay.prototype.drawHitDetectionReplay = function(gl, context, skippedFeaturesHash,
+ featureCallback, oneByOne, opt_hitExtent) {
+ if (!oneByOne) {
+ // draw all hit-detection features in "once" (by texture group)
+ return this.drawHitDetectionReplayAll(gl, context,
+ skippedFeaturesHash, featureCallback);
+ } else {
+ // draw hit-detection features one by one
+ return this.drawHitDetectionReplayOneByOne(gl, context,
+ skippedFeaturesHash, featureCallback, opt_hitExtent);
+ }
+};
+
+
+/**
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.Replay.prototype.drawHitDetectionReplayAll = function(gl, context, skippedFeaturesHash,
+ featureCallback) {
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ this.drawReplay(gl, context, skippedFeaturesHash, true);
+
+ var result = featureCallback(null);
+ if (result) {
+ return result;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} opacity Global opacity.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
+ * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
+ * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
+ * this extent are checked.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.Replay.prototype.replay = function(context,
+ center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash,
+ featureCallback, oneByOne, opt_hitExtent) {
+ var gl = context.getGL();
+ var tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask,
+ tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail;
+
+ if (this.lineStringReplay) {
+ tmpStencil = gl.isEnabled(gl.STENCIL_TEST);
+ tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC);
+ tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK);
+ tmpStencilRef = gl.getParameter(gl.STENCIL_REF);
+ tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK);
+ tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL);
+ tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS);
+ tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL);
+
+ gl.enable(gl.STENCIL_TEST);
+ gl.clear(gl.STENCIL_BUFFER_BIT);
+ gl.stencilMask(255);
+ gl.stencilFunc(gl.ALWAYS, 1, 255);
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
+
+ this.lineStringReplay.replay(context,
+ center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash,
+ featureCallback, oneByOne, opt_hitExtent);
+
+ gl.stencilMask(0);
+ gl.stencilFunc(gl.NOTEQUAL, 1, 255);
+ }
+
+ // bind the vertices buffer
+ ol.DEBUG && console.assert(this.verticesBuffer,
+ 'verticesBuffer must not be null');
+ context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.verticesBuffer);
+
+ // bind the indices buffer
+ ol.DEBUG && console.assert(this.indicesBuffer,
+ 'indicesBuffer must not be null');
+ context.bindBuffer(ol.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
+
+ var locations = this.setUpProgram(gl, context, size, pixelRatio);
+
+ // set the "uniform" values
+ var projectionMatrix = ol.transform.reset(this.projectionMatrix_);
+ ol.transform.scale(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1]));
+ ol.transform.rotate(projectionMatrix, -rotation);
+ ol.transform.translate(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1]));
+
+ var offsetScaleMatrix = ol.transform.reset(this.offsetScaleMatrix_);
+ ol.transform.scale(offsetScaleMatrix, 2 / size[0], 2 / size[1]);
+
+ var offsetRotateMatrix = ol.transform.reset(this.offsetRotateMatrix_);
+ if (rotation !== 0) {
+ ol.transform.rotate(offsetRotateMatrix, -rotation);
+ }
+
+ gl.uniformMatrix4fv(locations.u_projectionMatrix, false,
+ ol.vec.Mat4.fromTransform(this.tmpMat4_, projectionMatrix));
+ gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false,
+ ol.vec.Mat4.fromTransform(this.tmpMat4_, offsetScaleMatrix));
+ gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false,
+ ol.vec.Mat4.fromTransform(this.tmpMat4_, offsetRotateMatrix));
+ gl.uniform1f(locations.u_opacity, opacity);
+
+ // draw!
+ var result;
+ if (featureCallback === undefined) {
+ this.drawReplay(gl, context, skippedFeaturesHash, false);
+ } else {
+ // draw feature by feature for the hit-detection
+ result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash,
+ featureCallback, oneByOne, opt_hitExtent);
+ }
+
+ // disable the vertex attrib arrays
+ this.shutDownProgram(gl, locations);
+
+ if (this.lineStringReplay) {
+ if (!tmpStencil) {
+ gl.disable(gl.STENCIL_TEST);
+ }
+ gl.clear(gl.STENCIL_BUFFER_BIT);
+ gl.stencilFunc(/** @type {number} */ (tmpStencilFunc),
+ /** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal));
+ gl.stencilMask(/** @type {number} */ (tmpStencilMask));
+ gl.stencilOp(/** @type {number} */ (tmpStencilOpFail),
+ /** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass));
+ }
+
+ return result;
+};
+
+/**
+ * @protected
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {number} start Start index.
+ * @param {number} end End index.
+ */
+ol.render.webgl.Replay.prototype.drawElements = function(
+ gl, context, start, end) {
+ var elementType = context.hasOESElementIndexUint ?
+ ol.webgl.UNSIGNED_INT : ol.webgl.UNSIGNED_SHORT;
+ var elementSize = context.hasOESElementIndexUint ? 4 : 2;
+
+ var numItems = end - start;
+ var offsetInBytes = start * elementSize;
+ gl.drawElements(ol.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
+};
+
+goog.provide('ol.webgl.Buffer');
+
+goog.require('ol');
+goog.require('ol.webgl');
+
+
+/**
+ * @constructor
+ * @param {Array.<number>=} opt_arr Array.
+ * @param {number=} opt_usage Usage.
+ * @struct
+ */
+ol.webgl.Buffer = function(opt_arr, opt_usage) {
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.arr_ = opt_arr !== undefined ? opt_arr : [];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.usage_ = opt_usage !== undefined ?
+ opt_usage : ol.webgl.Buffer.Usage.STATIC_DRAW;
+
+};
+
+
+/**
+ * @return {Array.<number>} Array.
+ */
+ol.webgl.Buffer.prototype.getArray = function() {
+ return this.arr_;
+};
+
+
+/**
+ * @return {number} Usage.
+ */
+ol.webgl.Buffer.prototype.getUsage = function() {
+ return this.usage_;
+};
+
+
+/**
+ * @enum {number}
+ */
+ol.webgl.Buffer.Usage = {
+ STATIC_DRAW: ol.webgl.STATIC_DRAW,
+ STREAM_DRAW: ol.webgl.STREAM_DRAW,
+ DYNAMIC_DRAW: ol.webgl.DYNAMIC_DRAW
+};
+
+goog.provide('ol.render.webgl.CircleReplay');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.color');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.render.webgl.circlereplay.defaultshader');
+goog.require('ol.render.webgl.Replay');
+goog.require('ol.render.webgl');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Buffer');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.webgl.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @struct
+ */
+ol.render.webgl.CircleReplay = function(tolerance, maxExtent) {
+ ol.render.webgl.Replay.call(this, tolerance, maxExtent);
+
+ /**
+ * @private
+ * @type {ol.render.webgl.circlereplay.defaultshader.Locations}
+ */
+ this.defaultLocations_ = null;
+
+ /**
+ * @private
+ * @type {Array.<Array.<Array.<number>|number>>}
+ */
+ this.styles_ = [];
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.styleIndices_ = [];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.radius_ = 0;
+
+ /**
+ * @private
+ * @type {{fillColor: (Array.<number>|null),
+ * strokeColor: (Array.<number>|null),
+ * lineDash: Array.<number>,
+ * lineWidth: (number|undefined),
+ * changed: boolean}|null}
+ */
+ this.state_ = {
+ fillColor: null,
+ strokeColor: null,
+ lineDash: null,
+ lineWidth: undefined,
+ changed: false
+ };
+
+};
+ol.inherits(ol.render.webgl.CircleReplay, ol.render.webgl.Replay);
+
+
+/**
+ * @private
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ */
+ol.render.webgl.CircleReplay.prototype.drawCoordinates_ = function(
+ flatCoordinates, offset, end, stride) {
+ var numVertices = this.vertices.length;
+ var numIndices = this.indices.length;
+ var n = numVertices / 4;
+ var i, ii;
+ for (i = offset, ii = end; i < ii; i += stride) {
+ this.vertices[numVertices++] = flatCoordinates[i];
+ this.vertices[numVertices++] = flatCoordinates[i + 1];
+ this.vertices[numVertices++] = 0;
+ this.vertices[numVertices++] = this.radius_;
+
+ this.vertices[numVertices++] = flatCoordinates[i];
+ this.vertices[numVertices++] = flatCoordinates[i + 1];
+ this.vertices[numVertices++] = 1;
+ this.vertices[numVertices++] = this.radius_;
+
+ this.vertices[numVertices++] = flatCoordinates[i];
+ this.vertices[numVertices++] = flatCoordinates[i + 1];
+ this.vertices[numVertices++] = 2;
+ this.vertices[numVertices++] = this.radius_;
+
+ this.vertices[numVertices++] = flatCoordinates[i];
+ this.vertices[numVertices++] = flatCoordinates[i + 1];
+ this.vertices[numVertices++] = 3;
+ this.vertices[numVertices++] = this.radius_;
+
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n + 2;
+
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n + 3;
+ this.indices[numIndices++] = n;
+
+ n += 4;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.drawCircle = function(circleGeometry, feature) {
+ var radius = circleGeometry.getRadius();
+ var stride = circleGeometry.getStride();
+ if (radius) {
+ this.startIndices.push(this.indices.length);
+ this.startIndicesFeature.push(feature);
+ if (this.state_.changed) {
+ this.styleIndices_.push(this.indices.length);
+ this.state_.changed = false;
+ }
+
+ this.radius_ = radius;
+ var flatCoordinates = circleGeometry.getFlatCoordinates();
+ flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, 2,
+ stride, -this.origin[0], -this.origin[1]);
+ this.drawCoordinates_(flatCoordinates, 0, 2, stride);
+ } else {
+ if (this.state_.changed) {
+ this.styles_.pop();
+ if (this.styles_.length) {
+ var lastState = this.styles_[this.styles_.length - 1];
+ this.state_.fillColor = /** @type {Array.<number>} */ (lastState[0]);
+ this.state_.strokeColor = /** @type {Array.<number>} */ (lastState[1]);
+ this.state_.lineWidth = /** @type {number} */ (lastState[2]);
+ this.state_.changed = false;
+ }
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ **/
+ol.render.webgl.CircleReplay.prototype.finish = function(context) {
+ // create, bind, and populate the vertices buffer
+ this.verticesBuffer = new ol.webgl.Buffer(this.vertices);
+
+ // create, bind, and populate the indices buffer
+ this.indicesBuffer = new ol.webgl.Buffer(this.indices);
+
+ this.startIndices.push(this.indices.length);
+
+ //Clean up, if there is nothing to draw
+ if (this.styleIndices_.length === 0 && this.styles_.length > 0) {
+ this.styles_ = [];
+ }
+
+ this.vertices = null;
+ this.indices = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.getDeleteResourcesFunction = function(context) {
+ // We only delete our stuff here. The shaders and the program may
+ // be used by other CircleReplay instances (for other layers). And
+ // they will be deleted when disposing of the ol.webgl.Context
+ // object.
+ ol.DEBUG && console.assert(this.verticesBuffer,
+ 'verticesBuffer must not be null');
+ ol.DEBUG && console.assert(this.indicesBuffer,
+ 'indicesBuffer must not be null');
+ var verticesBuffer = this.verticesBuffer;
+ var indicesBuffer = this.indicesBuffer;
+ return function() {
+ context.deleteBuffer(verticesBuffer);
+ context.deleteBuffer(indicesBuffer);
+ };
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {
+ // get the program
+ var fragmentShader, vertexShader;
+ fragmentShader = ol.render.webgl.circlereplay.defaultshader.fragment;
+ vertexShader = ol.render.webgl.circlereplay.defaultshader.vertex;
+ var program = context.getProgram(fragmentShader, vertexShader);
+
+ // get the locations
+ var locations;
+ if (!this.defaultLocations_) {
+ locations =
+ new ol.render.webgl.circlereplay.defaultshader.Locations(gl, program);
+ this.defaultLocations_ = locations;
+ } else {
+ locations = this.defaultLocations_;
+ }
+
+ context.useProgram(program);
+
+ // enable the vertex attrib arrays
+ gl.enableVertexAttribArray(locations.a_position);
+ gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT,
+ false, 16, 0);
+
+ gl.enableVertexAttribArray(locations.a_instruction);
+ gl.vertexAttribPointer(locations.a_instruction, 1, ol.webgl.FLOAT,
+ false, 16, 8);
+
+ gl.enableVertexAttribArray(locations.a_radius);
+ gl.vertexAttribPointer(locations.a_radius, 1, ol.webgl.FLOAT,
+ false, 16, 12);
+
+ // Enable renderer specific uniforms.
+ gl.uniform2fv(locations.u_size, size);
+ gl.uniform1f(locations.u_pixelRatio, pixelRatio);
+
+ return locations;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.shutDownProgram = function(gl, locations) {
+ gl.disableVertexAttribArray(locations.a_position);
+ gl.disableVertexAttribArray(locations.a_instruction);
+ gl.disableVertexAttribArray(locations.a_radius);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {
+ if (!ol.obj.isEmpty(skippedFeaturesHash)) {
+ this.drawReplaySkipping_(gl, context, skippedFeaturesHash);
+ } else {
+ ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
+ 'number of styles and styleIndices match');
+
+ //Draw by style groups to minimize drawElements() calls.
+ var i, start, end, nextStyle;
+ end = this.startIndices[this.startIndices.length - 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ start = this.styleIndices_[i];
+ nextStyle = this.styles_[i];
+ this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0]));
+ this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]),
+ /** @type {number} */ (nextStyle[2]));
+ this.drawElements(gl, context, start, end);
+ end = start;
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash,
+ featureCallback, opt_hitExtent) {
+ ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
+ 'number of styles and styleIndices match');
+ ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
+ 'number of startIndices and startIndicesFeature match');
+
+ var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex;
+ featureIndex = this.startIndices.length - 2;
+ end = this.startIndices[featureIndex + 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ nextStyle = this.styles_[i];
+ this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0]));
+ this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]),
+ /** @type {number} */ (nextStyle[2]));
+ groupStart = this.styleIndices_[i];
+
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ start = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid] === undefined &&
+ feature.getGeometry() &&
+ (opt_hitExtent === undefined || ol.extent.intersects(
+ /** @type {Array<number>} */ (opt_hitExtent),
+ feature.getGeometry().getExtent()))) {
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ this.drawElements(gl, context, start, end);
+
+ var result = featureCallback(feature);
+
+ if (result) {
+ return result;
+ }
+
+ }
+ featureIndex--;
+ end = start;
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object} skippedFeaturesHash Ids of features to skip.
+ */
+ol.render.webgl.CircleReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) {
+ ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
+ 'number of startIndices and startIndicesFeature match');
+
+ var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart;
+ featureIndex = this.startIndices.length - 2;
+ end = start = this.startIndices[featureIndex + 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ nextStyle = this.styles_[i];
+ this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0]));
+ this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]),
+ /** @type {number} */ (nextStyle[2]));
+ groupStart = this.styleIndices_[i];
+
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ featureStart = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid]) {
+ if (start !== end) {
+ this.drawElements(gl, context, start, end);
+ }
+ end = featureStart;
+ }
+ featureIndex--;
+ start = featureStart;
+ }
+ if (start !== end) {
+ this.drawElements(gl, context, start, end);
+ }
+ start = end = groupStart;
+ }
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {Array.<number>} color Color.
+ */
+ol.render.webgl.CircleReplay.prototype.setFillStyle_ = function(gl, color) {
+ gl.uniform4fv(this.defaultLocations_.u_fillColor, color);
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {Array.<number>} color Color.
+ * @param {number} lineWidth Line width.
+ */
+ol.render.webgl.CircleReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth) {
+ gl.uniform4fv(this.defaultLocations_.u_strokeColor, color);
+ gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.CircleReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
+ var strokeStyleColor, strokeStyleWidth;
+ if (strokeStyle) {
+ var strokeStyleLineDash = strokeStyle.getLineDash();
+ this.state_.lineDash = strokeStyleLineDash ?
+ strokeStyleLineDash : ol.render.webgl.defaultLineDash;
+ strokeStyleColor = strokeStyle.getColor();
+ if (!(strokeStyleColor instanceof CanvasGradient) &&
+ !(strokeStyleColor instanceof CanvasPattern)) {
+ strokeStyleColor = ol.color.asArray(strokeStyleColor).map(function(c, i) {
+ return i != 3 ? c / 255 : c;
+ }) || ol.render.webgl.defaultStrokeStyle;
+ } else {
+ strokeStyleColor = ol.render.webgl.defaultStrokeStyle;
+ }
+ strokeStyleWidth = strokeStyle.getWidth();
+ strokeStyleWidth = strokeStyleWidth !== undefined ?
+ strokeStyleWidth : ol.render.webgl.defaultLineWidth;
+ } else {
+ strokeStyleColor = [0, 0, 0, 0];
+ strokeStyleWidth = 0;
+ }
+ var fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0];
+ if (!(fillStyleColor instanceof CanvasGradient) &&
+ !(fillStyleColor instanceof CanvasPattern)) {
+ fillStyleColor = ol.color.asArray(fillStyleColor).map(function(c, i) {
+ return i != 3 ? c / 255 : c;
+ }) || ol.render.webgl.defaultFillStyle;
+ } else {
+ fillStyleColor = ol.render.webgl.defaultFillStyle;
+ }
+ if (!this.state_.strokeColor || !ol.array.equals(this.state_.strokeColor, strokeStyleColor) ||
+ !this.state_.fillColor || !ol.array.equals(this.state_.fillColor, fillStyleColor) ||
+ this.state_.lineWidth !== strokeStyleWidth) {
+ this.state_.changed = true;
+ this.state_.fillColor = fillStyleColor;
+ this.state_.strokeColor = strokeStyleColor;
+ this.state_.lineWidth = strokeStyleWidth;
+ this.styles_.push([fillStyleColor, strokeStyleColor, strokeStyleWidth]);
+ }
+};
+
+// This file is automatically generated, do not edit
+goog.provide('ol.render.webgl.imagereplay.defaultshader');
+
+goog.require('ol');
+goog.require('ol.webgl.Fragment');
+goog.require('ol.webgl.Vertex');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Fragment}
+ * @struct
+ */
+ol.render.webgl.imagereplay.defaultshader.Fragment = function() {
+ ol.webgl.Fragment.call(this, ol.render.webgl.imagereplay.defaultshader.Fragment.SOURCE);
+};
+ol.inherits(ol.render.webgl.imagereplay.defaultshader.Fragment, ol.webgl.Fragment);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.imagereplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.imagereplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.imagereplay.defaultshader.Fragment.SOURCE = ol.DEBUG ?
+ ol.render.webgl.imagereplay.defaultshader.Fragment.DEBUG_SOURCE :
+ ol.render.webgl.imagereplay.defaultshader.Fragment.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.imagereplay.defaultshader.fragment = new ol.render.webgl.imagereplay.defaultshader.Fragment();
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Vertex}
+ * @struct
+ */
+ol.render.webgl.imagereplay.defaultshader.Vertex = function() {
+ ol.webgl.Vertex.call(this, ol.render.webgl.imagereplay.defaultshader.Vertex.SOURCE);
+};
+ol.inherits(ol.render.webgl.imagereplay.defaultshader.Vertex, ol.webgl.Vertex);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.imagereplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.imagereplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.0,0.0);gl_Position=h*vec4(c,0.0,1.0)+offsets;a=d;b=f;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.imagereplay.defaultshader.Vertex.SOURCE = ol.DEBUG ?
+ ol.render.webgl.imagereplay.defaultshader.Vertex.DEBUG_SOURCE :
+ ol.render.webgl.imagereplay.defaultshader.Vertex.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.imagereplay.defaultshader.vertex = new ol.render.webgl.imagereplay.defaultshader.Vertex();
+
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ * @struct
+ */
+ol.render.webgl.imagereplay.defaultshader.Locations = function(gl, program) {
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_image = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_image' : 'l');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetRotateMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetRotateMatrix' : 'j');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetScaleMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetScaleMatrix' : 'i');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_opacity = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_opacity' : 'k');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_projectionMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_projectionMatrix' : 'h');
+
+ /**
+ * @type {number}
+ */
+ this.a_offsets = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_offsets' : 'e');
+
+ /**
+ * @type {number}
+ */
+ this.a_opacity = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_opacity' : 'f');
+
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_position' : 'c');
+
+ /**
+ * @type {number}
+ */
+ this.a_rotateWithView = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_rotateWithView' : 'g');
+
+ /**
+ * @type {number}
+ */
+ this.a_texCoord = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_texCoord' : 'd');
+};
+
+goog.provide('ol.webgl.ContextEventType');
+
+
+/**
+ * @enum {string}
+ */
+ol.webgl.ContextEventType = {
+ LOST: 'webglcontextlost',
+ RESTORED: 'webglcontextrestored'
+};
+
+goog.provide('ol.webgl.Context');
+
+goog.require('ol');
+goog.require('ol.Disposable');
+goog.require('ol.array');
+goog.require('ol.events');
+goog.require('ol.obj');
+goog.require('ol.webgl');
+goog.require('ol.webgl.ContextEventType');
+
+
+/**
+ * @classdesc
+ * A WebGL context for accessing low-level WebGL capabilities.
+ *
+ * @constructor
+ * @extends {ol.Disposable}
+ * @param {HTMLCanvasElement} canvas Canvas.
+ * @param {WebGLRenderingContext} gl GL.
+ */
+ol.webgl.Context = function(canvas, gl) {
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = canvas;
+
+ /**
+ * @private
+ * @type {WebGLRenderingContext}
+ */
+ this.gl_ = gl;
+
+ /**
+ * @private
+ * @type {Object.<string, ol.WebglBufferCacheEntry>}
+ */
+ this.bufferCache_ = {};
+
+ /**
+ * @private
+ * @type {Object.<string, WebGLShader>}
+ */
+ this.shaderCache_ = {};
+
+ /**
+ * @private
+ * @type {Object.<string, WebGLProgram>}
+ */
+ this.programCache_ = {};
+
+ /**
+ * @private
+ * @type {WebGLProgram}
+ */
+ this.currentProgram_ = null;
+
+ /**
+ * @private
+ * @type {WebGLFramebuffer}
+ */
+ this.hitDetectionFramebuffer_ = null;
+
+ /**
+ * @private
+ * @type {WebGLTexture}
+ */
+ this.hitDetectionTexture_ = null;
+
+ /**
+ * @private
+ * @type {WebGLRenderbuffer}
+ */
+ this.hitDetectionRenderbuffer_ = null;
+
+ /**
+ * @type {boolean}
+ */
+ this.hasOESElementIndexUint = ol.array.includes(
+ ol.WEBGL_EXTENSIONS, 'OES_element_index_uint');
+
+ // use the OES_element_index_uint extension if available
+ if (this.hasOESElementIndexUint) {
+ var ext = gl.getExtension('OES_element_index_uint');
+ ol.DEBUG && console.assert(ext,
+ 'Failed to get extension "OES_element_index_uint"');
+ }
+
+ ol.events.listen(this.canvas_, ol.webgl.ContextEventType.LOST,
+ this.handleWebGLContextLost, this);
+ ol.events.listen(this.canvas_, ol.webgl.ContextEventType.RESTORED,
+ this.handleWebGLContextRestored, this);
+
+};
+ol.inherits(ol.webgl.Context, ol.Disposable);
+
+
+/**
+ * Just bind the buffer if it's in the cache. Otherwise create
+ * the WebGL buffer, bind it, populate it, and add an entry to
+ * the cache.
+ * @param {number} target Target.
+ * @param {ol.webgl.Buffer} buf Buffer.
+ */
+ol.webgl.Context.prototype.bindBuffer = function(target, buf) {
+ var gl = this.getGL();
+ var arr = buf.getArray();
+ var bufferKey = String(ol.getUid(buf));
+ if (bufferKey in this.bufferCache_) {
+ var bufferCacheEntry = this.bufferCache_[bufferKey];
+ gl.bindBuffer(target, bufferCacheEntry.buffer);
+ } else {
+ var buffer = gl.createBuffer();
+ gl.bindBuffer(target, buffer);
+ ol.DEBUG && console.assert(target == ol.webgl.ARRAY_BUFFER ||
+ target == ol.webgl.ELEMENT_ARRAY_BUFFER,
+ 'target is supposed to be an ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER');
+ var /** @type {ArrayBufferView} */ arrayBuffer;
+ if (target == ol.webgl.ARRAY_BUFFER) {
+ arrayBuffer = new Float32Array(arr);
+ } else if (target == ol.webgl.ELEMENT_ARRAY_BUFFER) {
+ arrayBuffer = this.hasOESElementIndexUint ?
+ new Uint32Array(arr) : new Uint16Array(arr);
+ }
+ gl.bufferData(target, arrayBuffer, buf.getUsage());
+ this.bufferCache_[bufferKey] = {
+ buf: buf,
+ buffer: buffer
+ };
+ }
+};
+
+
+/**
+ * @param {ol.webgl.Buffer} buf Buffer.
+ */
+ol.webgl.Context.prototype.deleteBuffer = function(buf) {
+ var gl = this.getGL();
+ var bufferKey = String(ol.getUid(buf));
+ ol.DEBUG && console.assert(bufferKey in this.bufferCache_,
+ 'attempted to delete uncached buffer');
+ var bufferCacheEntry = this.bufferCache_[bufferKey];
+ if (!gl.isContextLost()) {
+ gl.deleteBuffer(bufferCacheEntry.buffer);
+ }
+ delete this.bufferCache_[bufferKey];
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.webgl.Context.prototype.disposeInternal = function() {
+ ol.events.unlistenAll(this.canvas_);
+ var gl = this.getGL();
+ if (!gl.isContextLost()) {
+ var key;
+ for (key in this.bufferCache_) {
+ gl.deleteBuffer(this.bufferCache_[key].buffer);
+ }
+ for (key in this.programCache_) {
+ gl.deleteProgram(this.programCache_[key]);
+ }
+ for (key in this.shaderCache_) {
+ gl.deleteShader(this.shaderCache_[key]);
+ }
+ // delete objects for hit-detection
+ gl.deleteFramebuffer(this.hitDetectionFramebuffer_);
+ gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_);
+ gl.deleteTexture(this.hitDetectionTexture_);
+ }
+};
+
+
+/**
+ * @return {HTMLCanvasElement} Canvas.
+ */
+ol.webgl.Context.prototype.getCanvas = function() {
+ return this.canvas_;
+};
+
+
+/**
+ * Get the WebGL rendering context
+ * @return {WebGLRenderingContext} The rendering context.
+ * @api
+ */
+ol.webgl.Context.prototype.getGL = function() {
+ return this.gl_;
+};
+
+
+/**
+ * Get the frame buffer for hit detection.
+ * @return {WebGLFramebuffer} The hit detection frame buffer.
+ */
+ol.webgl.Context.prototype.getHitDetectionFramebuffer = function() {
+ if (!this.hitDetectionFramebuffer_) {
+ this.initHitDetectionFramebuffer_();
+ }
+ return this.hitDetectionFramebuffer_;
+};
+
+
+/**
+ * Get shader from the cache if it's in the cache. Otherwise, create
+ * the WebGL shader, compile it, and add entry to cache.
+ * @param {ol.webgl.Shader} shaderObject Shader object.
+ * @return {WebGLShader} Shader.
+ */
+ol.webgl.Context.prototype.getShader = function(shaderObject) {
+ var shaderKey = String(ol.getUid(shaderObject));
+ if (shaderKey in this.shaderCache_) {
+ return this.shaderCache_[shaderKey];
+ } else {
+ var gl = this.getGL();
+ var shader = gl.createShader(shaderObject.getType());
+ gl.shaderSource(shader, shaderObject.getSource());
+ gl.compileShader(shader);
+ ol.DEBUG && console.assert(
+ gl.getShaderParameter(shader, ol.webgl.COMPILE_STATUS) ||
+ gl.isContextLost(),
+ gl.getShaderInfoLog(shader) || 'illegal state, shader not compiled or context lost');
+ this.shaderCache_[shaderKey] = shader;
+ return shader;
+ }
+};
+
+
+/**
+ * Get the program from the cache if it's in the cache. Otherwise create
+ * the WebGL program, attach the shaders to it, and add an entry to the
+ * cache.
+ * @param {ol.webgl.Fragment} fragmentShaderObject Fragment shader.
+ * @param {ol.webgl.Vertex} vertexShaderObject Vertex shader.
+ * @return {WebGLProgram} Program.
+ */
+ol.webgl.Context.prototype.getProgram = function(
+ fragmentShaderObject, vertexShaderObject) {
+ var programKey =
+ ol.getUid(fragmentShaderObject) + '/' + ol.getUid(vertexShaderObject);
+ if (programKey in this.programCache_) {
+ return this.programCache_[programKey];
+ } else {
+ var gl = this.getGL();
+ var program = gl.createProgram();
+ gl.attachShader(program, this.getShader(fragmentShaderObject));
+ gl.attachShader(program, this.getShader(vertexShaderObject));
+ gl.linkProgram(program);
+ ol.DEBUG && console.assert(
+ gl.getProgramParameter(program, ol.webgl.LINK_STATUS) ||
+ gl.isContextLost(),
+ gl.getProgramInfoLog(program) || 'illegal state, shader not linked or context lost');
+ this.programCache_[programKey] = program;
+ return program;
+ }
+};
+
+
+/**
+ * FIXME empy description for jsdoc
+ */
+ol.webgl.Context.prototype.handleWebGLContextLost = function() {
+ ol.obj.clear(this.bufferCache_);
+ ol.obj.clear(this.shaderCache_);
+ ol.obj.clear(this.programCache_);
+ this.currentProgram_ = null;
+ this.hitDetectionFramebuffer_ = null;
+ this.hitDetectionTexture_ = null;
+ this.hitDetectionRenderbuffer_ = null;
+};
+
+
+/**
+ * FIXME empy description for jsdoc
+ */
+ol.webgl.Context.prototype.handleWebGLContextRestored = function() {
+};
+
+
+/**
+ * Creates a 1x1 pixel framebuffer for the hit-detection.
+ * @private
+ */
+ol.webgl.Context.prototype.initHitDetectionFramebuffer_ = function() {
+ var gl = this.gl_;
+ var framebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+
+ var texture = ol.webgl.Context.createEmptyTexture(gl, 1, 1);
+ var renderbuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1);
+ gl.framebufferTexture2D(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
+ gl.RENDERBUFFER, renderbuffer);
+
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+ this.hitDetectionFramebuffer_ = framebuffer;
+ this.hitDetectionTexture_ = texture;
+ this.hitDetectionRenderbuffer_ = renderbuffer;
+};
+
+
+/**
+ * Use a program. If the program is already in use, this will return `false`.
+ * @param {WebGLProgram} program Program.
+ * @return {boolean} Changed.
+ * @api
+ */
+ol.webgl.Context.prototype.useProgram = function(program) {
+ if (program == this.currentProgram_) {
+ return false;
+ } else {
+ var gl = this.getGL();
+ gl.useProgram(program);
+ this.currentProgram_ = program;
+ return true;
+ }
+};
+
+
+/**
+ * @param {WebGLRenderingContext} gl WebGL rendering context.
+ * @param {number=} opt_wrapS wrapS.
+ * @param {number=} opt_wrapT wrapT.
+ * @return {WebGLTexture} The texture.
+ * @private
+ */
+ol.webgl.Context.createTexture_ = function(gl, opt_wrapS, opt_wrapT) {
+ var texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+
+ if (opt_wrapS !== undefined) {
+ gl.texParameteri(
+ ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_S, opt_wrapS);
+ }
+ if (opt_wrapT !== undefined) {
+ gl.texParameteri(
+ ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_T, opt_wrapT);
+ }
+
+ return texture;
+};
+
+
+/**
+ * @param {WebGLRenderingContext} gl WebGL rendering context.
+ * @param {number} width Width.
+ * @param {number} height Height.
+ * @param {number=} opt_wrapS wrapS.
+ * @param {number=} opt_wrapT wrapT.
+ * @return {WebGLTexture} The texture.
+ */
+ol.webgl.Context.createEmptyTexture = function(
+ gl, width, height, opt_wrapS, opt_wrapT) {
+ var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT);
+ gl.texImage2D(
+ gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE,
+ null);
+
+ return texture;
+};
+
+
+/**
+ * @param {WebGLRenderingContext} gl WebGL rendering context.
+ * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
+ * @param {number=} opt_wrapS wrapS.
+ * @param {number=} opt_wrapT wrapT.
+ * @return {WebGLTexture} The texture.
+ */
+ol.webgl.Context.createTexture = function(gl, image, opt_wrapS, opt_wrapT) {
+ var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT);
+ gl.texImage2D(
+ gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+
+ return texture;
+};
+
+goog.provide('ol.render.webgl.ImageReplay');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.render.webgl.imagereplay.defaultshader');
+goog.require('ol.render.webgl.Replay');
+goog.require('ol.render.webgl');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Buffer');
+goog.require('ol.webgl.Context');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.webgl.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @struct
+ */
+ol.render.webgl.ImageReplay = function(tolerance, maxExtent) {
+ ol.render.webgl.Replay.call(this, tolerance, maxExtent);
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.anchorX_ = undefined;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.anchorY_ = undefined;
+
+ /**
+ * @type {Array.<number>}
+ * @private
+ */
+ this.groupIndices_ = [];
+
+ /**
+ * @type {Array.<number>}
+ * @private
+ */
+ this.hitDetectionGroupIndices_ = [];
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.height_ = undefined;
+
+ /**
+ * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
+ * @private
+ */
+ this.images_ = [];
+
+ /**
+ * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
+ * @private
+ */
+ this.hitDetectionImages_ = [];
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.imageHeight_ = undefined;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.imageWidth_ = undefined;
+
+ /**
+ * @private
+ * @type {ol.render.webgl.imagereplay.defaultshader.Locations}
+ */
+ this.defaultLocations_ = null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.opacity_ = undefined;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.originX_ = undefined;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.originY_ = undefined;
+
+ /**
+ * @private
+ * @type {boolean|undefined}
+ */
+ this.rotateWithView_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.rotation_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.scale_ = undefined;
+
+ /**
+ * @type {Array.<WebGLTexture>}
+ * @private
+ */
+ this.textures_ = [];
+
+ /**
+ * @type {Array.<WebGLTexture>}
+ * @private
+ */
+ this.hitDetectionTextures_ = [];
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.width_ = undefined;
+};
+ol.inherits(ol.render.webgl.ImageReplay, ol.render.webgl.Replay);
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction = function(context) {
+ // We only delete our stuff here. The shaders and the program may
+ // be used by other ImageReplay instances (for other layers). And
+ // they will be deleted when disposing of the ol.webgl.Context
+ // object.
+ ol.DEBUG && console.assert(this.verticesBuffer,
+ 'verticesBuffer must not be null');
+ ol.DEBUG && console.assert(this.indicesBuffer,
+ 'indicesBuffer must not be null');
+ var verticesBuffer = this.verticesBuffer;
+ var indicesBuffer = this.indicesBuffer;
+ var textures = this.textures_;
+ var hitDetectionTextures = this.hitDetectionTextures_;
+ var gl = context.getGL();
+ return function() {
+ if (!gl.isContextLost()) {
+ var i, ii;
+ for (i = 0, ii = textures.length; i < ii; ++i) {
+ gl.deleteTexture(textures[i]);
+ }
+ for (i = 0, ii = hitDetectionTextures.length; i < ii; ++i) {
+ gl.deleteTexture(hitDetectionTextures[i]);
+ }
+ }
+ context.deleteBuffer(verticesBuffer);
+ context.deleteBuffer(indicesBuffer);
+ };
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {number} My end.
+ * @private
+ */
+ol.render.webgl.ImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) {
+ ol.DEBUG && console.assert(this.anchorX_ !== undefined, 'anchorX is defined');
+ ol.DEBUG && console.assert(this.anchorY_ !== undefined, 'anchorY is defined');
+ ol.DEBUG && console.assert(this.height_ !== undefined, 'height is defined');
+ ol.DEBUG && console.assert(this.imageHeight_ !== undefined,
+ 'imageHeight is defined');
+ ol.DEBUG && console.assert(this.imageWidth_ !== undefined, 'imageWidth is defined');
+ ol.DEBUG && console.assert(this.opacity_ !== undefined, 'opacity is defined');
+ ol.DEBUG && console.assert(this.originX_ !== undefined, 'originX is defined');
+ ol.DEBUG && console.assert(this.originY_ !== undefined, 'originY is defined');
+ ol.DEBUG && console.assert(this.rotateWithView_ !== undefined,
+ 'rotateWithView is defined');
+ ol.DEBUG && console.assert(this.rotation_ !== undefined, 'rotation is defined');
+ ol.DEBUG && console.assert(this.scale_ !== undefined, 'scale is defined');
+ ol.DEBUG && console.assert(this.width_ !== undefined, 'width is defined');
+ var anchorX = /** @type {number} */ (this.anchorX_);
+ var anchorY = /** @type {number} */ (this.anchorY_);
+ var height = /** @type {number} */ (this.height_);
+ var imageHeight = /** @type {number} */ (this.imageHeight_);
+ var imageWidth = /** @type {number} */ (this.imageWidth_);
+ var opacity = /** @type {number} */ (this.opacity_);
+ var originX = /** @type {number} */ (this.originX_);
+ var originY = /** @type {number} */ (this.originY_);
+ var rotateWithView = this.rotateWithView_ ? 1.0 : 0.0;
+ // this.rotation_ is anti-clockwise, but rotation is clockwise
+ var rotation = /** @type {number} */ (-this.rotation_);
+ var scale = /** @type {number} */ (this.scale_);
+ var width = /** @type {number} */ (this.width_);
+ var cos = Math.cos(rotation);
+ var sin = Math.sin(rotation);
+ var numIndices = this.indices.length;
+ var numVertices = this.vertices.length;
+ var i, n, offsetX, offsetY, x, y;
+ for (i = offset; i < end; i += stride) {
+ x = flatCoordinates[i] - this.origin[0];
+ y = flatCoordinates[i + 1] - this.origin[1];
+
+ // There are 4 vertices per [x, y] point, one for each corner of the
+ // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if
+ // WebGL supported Geometry Shaders (which can emit new vertices), but that
+ // is not currently the case.
+ //
+ // And each vertex includes 8 values: the x and y coordinates, the x and
+ // y offsets used to calculate the position of the corner, the u and
+ // v texture coordinates for the corner, the opacity, and whether the
+ // the image should be rotated with the view (rotateWithView).
+
+ n = numVertices / 8;
+
+ // bottom-left corner
+ offsetX = -scale * anchorX;
+ offsetY = -scale * (height - anchorY);
+ this.vertices[numVertices++] = x;
+ this.vertices[numVertices++] = y;
+ this.vertices[numVertices++] = offsetX * cos - offsetY * sin;
+ this.vertices[numVertices++] = offsetX * sin + offsetY * cos;
+ this.vertices[numVertices++] = originX / imageWidth;
+ this.vertices[numVertices++] = (originY + height) / imageHeight;
+ this.vertices[numVertices++] = opacity;
+ this.vertices[numVertices++] = rotateWithView;
+
+ // bottom-right corner
+ offsetX = scale * (width - anchorX);
+ offsetY = -scale * (height - anchorY);
+ this.vertices[numVertices++] = x;
+ this.vertices[numVertices++] = y;
+ this.vertices[numVertices++] = offsetX * cos - offsetY * sin;
+ this.vertices[numVertices++] = offsetX * sin + offsetY * cos;
+ this.vertices[numVertices++] = (originX + width) / imageWidth;
+ this.vertices[numVertices++] = (originY + height) / imageHeight;
+ this.vertices[numVertices++] = opacity;
+ this.vertices[numVertices++] = rotateWithView;
+
+ // top-right corner
+ offsetX = scale * (width - anchorX);
+ offsetY = scale * anchorY;
+ this.vertices[numVertices++] = x;
+ this.vertices[numVertices++] = y;
+ this.vertices[numVertices++] = offsetX * cos - offsetY * sin;
+ this.vertices[numVertices++] = offsetX * sin + offsetY * cos;
+ this.vertices[numVertices++] = (originX + width) / imageWidth;
+ this.vertices[numVertices++] = originY / imageHeight;
+ this.vertices[numVertices++] = opacity;
+ this.vertices[numVertices++] = rotateWithView;
+
+ // top-left corner
+ offsetX = -scale * anchorX;
+ offsetY = scale * anchorY;
+ this.vertices[numVertices++] = x;
+ this.vertices[numVertices++] = y;
+ this.vertices[numVertices++] = offsetX * cos - offsetY * sin;
+ this.vertices[numVertices++] = offsetX * sin + offsetY * cos;
+ this.vertices[numVertices++] = originX / imageWidth;
+ this.vertices[numVertices++] = originY / imageHeight;
+ this.vertices[numVertices++] = opacity;
+ this.vertices[numVertices++] = rotateWithView;
+
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n + 3;
+ }
+
+ return numVertices;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) {
+ this.startIndices.push(this.indices.length);
+ this.startIndicesFeature.push(feature);
+ var flatCoordinates = multiPointGeometry.getFlatCoordinates();
+ var stride = multiPointGeometry.getStride();
+ this.drawCoordinates_(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.drawPoint = function(pointGeometry, feature) {
+ this.startIndices.push(this.indices.length);
+ this.startIndicesFeature.push(feature);
+ var flatCoordinates = pointGeometry.getFlatCoordinates();
+ var stride = pointGeometry.getStride();
+ this.drawCoordinates_(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.finish = function(context) {
+ var gl = context.getGL();
+
+ this.groupIndices_.push(this.indices.length);
+ ol.DEBUG && console.assert(this.images_.length === this.groupIndices_.length,
+ 'number of images and groupIndices match');
+ this.hitDetectionGroupIndices_.push(this.indices.length);
+ ol.DEBUG && console.assert(this.hitDetectionImages_.length ===
+ this.hitDetectionGroupIndices_.length,
+ 'number of hitDetectionImages and hitDetectionGroupIndices match');
+
+ // create, bind, and populate the vertices buffer
+ this.verticesBuffer = new ol.webgl.Buffer(this.vertices);
+
+ var indices = this.indices;
+ var bits = context.hasOESElementIndexUint ? 32 : 16;
+ ol.DEBUG && console.assert(indices[indices.length - 1] < Math.pow(2, bits),
+ 'Too large element index detected [%s] (OES_element_index_uint "%s")',
+ indices[indices.length - 1], context.hasOESElementIndexUint);
+
+ // create, bind, and populate the indices buffer
+ this.indicesBuffer = new ol.webgl.Buffer(indices);
+
+ // create textures
+ /** @type {Object.<string, WebGLTexture>} */
+ var texturePerImage = {};
+
+ this.createTextures_(this.textures_, this.images_, texturePerImage, gl);
+ ol.DEBUG && console.assert(this.textures_.length === this.groupIndices_.length,
+ 'number of textures and groupIndices match');
+
+ this.createTextures_(this.hitDetectionTextures_, this.hitDetectionImages_,
+ texturePerImage, gl);
+ ol.DEBUG && console.assert(this.hitDetectionTextures_.length ===
+ this.hitDetectionGroupIndices_.length,
+ 'number of hitDetectionTextures and hitDetectionGroupIndices match');
+
+ this.anchorX_ = undefined;
+ this.anchorY_ = undefined;
+ this.height_ = undefined;
+ this.images_ = null;
+ this.hitDetectionImages_ = null;
+ this.imageHeight_ = undefined;
+ this.imageWidth_ = undefined;
+ this.indices = null;
+ this.opacity_ = undefined;
+ this.originX_ = undefined;
+ this.originY_ = undefined;
+ this.rotateWithView_ = undefined;
+ this.rotation_ = undefined;
+ this.scale_ = undefined;
+ this.vertices = null;
+ this.width_ = undefined;
+};
+
+
+/**
+ * @private
+ * @param {Array.<WebGLTexture>} textures Textures.
+ * @param {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} images
+ * Images.
+ * @param {Object.<string, WebGLTexture>} texturePerImage Texture cache.
+ * @param {WebGLRenderingContext} gl Gl.
+ */
+ol.render.webgl.ImageReplay.prototype.createTextures_ = function(textures, images, texturePerImage, gl) {
+ ol.DEBUG && console.assert(textures.length === 0,
+ 'upon creation, textures is empty');
+
+ var texture, image, uid, i;
+ var ii = images.length;
+ for (i = 0; i < ii; ++i) {
+ image = images[i];
+
+ uid = ol.getUid(image).toString();
+ if (uid in texturePerImage) {
+ texture = texturePerImage[uid];
+ } else {
+ texture = ol.webgl.Context.createTexture(
+ gl, image, ol.webgl.CLAMP_TO_EDGE, ol.webgl.CLAMP_TO_EDGE);
+ texturePerImage[uid] = texture;
+ }
+ textures[i] = texture;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {
+ // get the program
+ var fragmentShader = ol.render.webgl.imagereplay.defaultshader.fragment;
+ var vertexShader = ol.render.webgl.imagereplay.defaultshader.vertex;
+ var program = context.getProgram(fragmentShader, vertexShader);
+
+ // get the locations
+ var locations;
+ if (!this.defaultLocations_) {
+ locations =
+ new ol.render.webgl.imagereplay.defaultshader.Locations(gl, program);
+ this.defaultLocations_ = locations;
+ } else {
+ locations = this.defaultLocations_;
+ }
+
+ // use the program (FIXME: use the return value)
+ context.useProgram(program);
+
+ // enable the vertex attrib arrays
+ gl.enableVertexAttribArray(locations.a_position);
+ gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT,
+ false, 32, 0);
+
+ gl.enableVertexAttribArray(locations.a_offsets);
+ gl.vertexAttribPointer(locations.a_offsets, 2, ol.webgl.FLOAT,
+ false, 32, 8);
+
+ gl.enableVertexAttribArray(locations.a_texCoord);
+ gl.vertexAttribPointer(locations.a_texCoord, 2, ol.webgl.FLOAT,
+ false, 32, 16);
+
+ gl.enableVertexAttribArray(locations.a_opacity);
+ gl.vertexAttribPointer(locations.a_opacity, 1, ol.webgl.FLOAT,
+ false, 32, 24);
+
+ gl.enableVertexAttribArray(locations.a_rotateWithView);
+ gl.vertexAttribPointer(locations.a_rotateWithView, 1, ol.webgl.FLOAT,
+ false, 32, 28);
+
+ return locations;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.shutDownProgram = function(gl, locations) {
+ gl.disableVertexAttribArray(locations.a_position);
+ gl.disableVertexAttribArray(locations.a_offsets);
+ gl.disableVertexAttribArray(locations.a_texCoord);
+ gl.disableVertexAttribArray(locations.a_opacity);
+ gl.disableVertexAttribArray(locations.a_rotateWithView);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {
+ var textures = hitDetection ? this.hitDetectionTextures_ : this.textures_;
+ var groupIndices = hitDetection ? this.hitDetectionGroupIndices_ : this.groupIndices_;
+ ol.DEBUG && console.assert(textures.length === groupIndices.length,
+ 'number of textures and groupIndeces match');
+
+ if (!ol.obj.isEmpty(skippedFeaturesHash)) {
+ this.drawReplaySkipping_(
+ gl, context, skippedFeaturesHash, textures, groupIndices);
+ } else {
+ var i, ii, start;
+ for (i = 0, ii = textures.length, start = 0; i < ii; ++i) {
+ gl.bindTexture(ol.webgl.TEXTURE_2D, textures[i]);
+ var end = groupIndices[i];
+ this.drawElements(gl, context, start, end);
+ start = end;
+ }
+ }
+};
+
+
+/**
+ * Draw the replay while paying attention to skipped features.
+ *
+ * This functions creates groups of features that can be drawn to together,
+ * so that the number of `drawElements` calls is minimized.
+ *
+ * For example given the following texture groups:
+ *
+ * Group 1: A B C
+ * Group 2: D [E] F G
+ *
+ * If feature E should be skipped, the following `drawElements` calls will be
+ * made:
+ *
+ * drawElements with feature A, B and C
+ * drawElements with feature D
+ * drawElements with feature F and G
+ *
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {Array.<WebGLTexture>} textures Textures.
+ * @param {Array.<number>} groupIndices Texture group indices.
+ */
+ol.render.webgl.ImageReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash, textures,
+ groupIndices) {
+ var featureIndex = 0;
+
+ var i, ii;
+ for (i = 0, ii = textures.length; i < ii; ++i) {
+ gl.bindTexture(ol.webgl.TEXTURE_2D, textures[i]);
+ var groupStart = (i > 0) ? groupIndices[i - 1] : 0;
+ var groupEnd = groupIndices[i];
+
+ var start = groupStart;
+ var end = groupStart;
+ while (featureIndex < this.startIndices.length &&
+ this.startIndices[featureIndex] <= groupEnd) {
+ var feature = this.startIndicesFeature[featureIndex];
+
+ var featureUid = ol.getUid(feature).toString();
+ if (skippedFeaturesHash[featureUid] !== undefined) {
+ // feature should be skipped
+ if (start !== end) {
+ // draw the features so far
+ this.drawElements(gl, context, start, end);
+ }
+ // continue with the next feature
+ start = (featureIndex === this.startIndices.length - 1) ?
+ groupEnd : this.startIndices[featureIndex + 1];
+ end = start;
+ } else {
+ // the feature is not skipped, augment the end index
+ end = (featureIndex === this.startIndices.length - 1) ?
+ groupEnd : this.startIndices[featureIndex + 1];
+ }
+ featureIndex++;
+ }
+
+ if (start !== end) {
+ // draw the remaining features (in case there was no skipped feature
+ // in this texture group, all features of a group are drawn together)
+ this.drawElements(gl, context, start, end);
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash,
+ featureCallback, opt_hitExtent) {
+ ol.DEBUG && console.assert(this.hitDetectionTextures_.length ===
+ this.hitDetectionGroupIndices_.length,
+ 'number of hitDetectionTextures and hitDetectionGroupIndices match');
+
+ var i, groupStart, start, end, feature, featureUid;
+ var featureIndex = this.startIndices.length - 1;
+ for (i = this.hitDetectionTextures_.length - 1; i >= 0; --i) {
+ gl.bindTexture(ol.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]);
+ groupStart = (i > 0) ? this.hitDetectionGroupIndices_[i - 1] : 0;
+ end = this.hitDetectionGroupIndices_[i];
+
+ // draw all features for this texture group
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ start = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid] === undefined &&
+ feature.getGeometry() &&
+ (opt_hitExtent === undefined || ol.extent.intersects(
+ /** @type {Array<number>} */ (opt_hitExtent),
+ feature.getGeometry().getExtent()))) {
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ this.drawElements(gl, context, start, end);
+
+ var result = featureCallback(feature);
+ if (result) {
+ return result;
+ }
+ }
+
+ end = start;
+ featureIndex--;
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) {
+ var anchor = imageStyle.getAnchor();
+ var image = imageStyle.getImage(1);
+ var imageSize = imageStyle.getImageSize();
+ var hitDetectionImage = imageStyle.getHitDetectionImage(1);
+ var hitDetectionImageSize = imageStyle.getHitDetectionImageSize();
+ var opacity = imageStyle.getOpacity();
+ var origin = imageStyle.getOrigin();
+ var rotateWithView = imageStyle.getRotateWithView();
+ var rotation = imageStyle.getRotation();
+ var size = imageStyle.getSize();
+ var scale = imageStyle.getScale();
+ ol.DEBUG && console.assert(anchor, 'imageStyle anchor is not null');
+ ol.DEBUG && console.assert(image, 'imageStyle image is not null');
+ ol.DEBUG && console.assert(imageSize,
+ 'imageStyle imageSize is not null');
+ ol.DEBUG && console.assert(hitDetectionImage,
+ 'imageStyle hitDetectionImage is not null');
+ ol.DEBUG && console.assert(hitDetectionImageSize,
+ 'imageStyle hitDetectionImageSize is not null');
+ ol.DEBUG && console.assert(opacity !== undefined, 'imageStyle opacity is defined');
+ ol.DEBUG && console.assert(origin, 'imageStyle origin is not null');
+ ol.DEBUG && console.assert(rotateWithView !== undefined,
+ 'imageStyle rotateWithView is defined');
+ ol.DEBUG && console.assert(rotation !== undefined, 'imageStyle rotation is defined');
+ ol.DEBUG && console.assert(size, 'imageStyle size is not null');
+ ol.DEBUG && console.assert(scale !== undefined, 'imageStyle scale is defined');
+
+ var currentImage;
+ if (this.images_.length === 0) {
+ this.images_.push(image);
+ } else {
+ currentImage = this.images_[this.images_.length - 1];
+ if (ol.getUid(currentImage) != ol.getUid(image)) {
+ this.groupIndices_.push(this.indices.length);
+ ol.DEBUG && console.assert(this.groupIndices_.length === this.images_.length,
+ 'number of groupIndices and images match');
+ this.images_.push(image);
+ }
+ }
+
+ if (this.hitDetectionImages_.length === 0) {
+ this.hitDetectionImages_.push(hitDetectionImage);
+ } else {
+ currentImage =
+ this.hitDetectionImages_[this.hitDetectionImages_.length - 1];
+ if (ol.getUid(currentImage) != ol.getUid(hitDetectionImage)) {
+ this.hitDetectionGroupIndices_.push(this.indices.length);
+ ol.DEBUG && console.assert(this.hitDetectionGroupIndices_.length ===
+ this.hitDetectionImages_.length,
+ 'number of hitDetectionGroupIndices and hitDetectionImages match');
+ this.hitDetectionImages_.push(hitDetectionImage);
+ }
+ }
+
+ this.anchorX_ = anchor[0];
+ this.anchorY_ = anchor[1];
+ this.height_ = size[1];
+ this.imageHeight_ = imageSize[1];
+ this.imageWidth_ = imageSize[0];
+ this.opacity_ = opacity;
+ this.originX_ = origin[0];
+ this.originY_ = origin[1];
+ this.rotation_ = rotation;
+ this.rotateWithView_ = rotateWithView;
+ this.scale_ = scale;
+ this.width_ = size[0];
+};
+
+goog.provide('ol.geom.flat.topology');
+
+goog.require('ol.geom.flat.area');
+
+/**
+ * Check if the linestring is a boundary.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {boolean} The linestring is a boundary.
+ */
+ol.geom.flat.topology.lineStringIsClosed = function(flatCoordinates, offset, end, stride) {
+ var lastCoord = end - stride;
+ if (flatCoordinates[offset] === flatCoordinates[lastCoord] &&
+ flatCoordinates[offset + 1] === flatCoordinates[lastCoord + 1] && (end - offset) / stride > 3) {
+ return !!ol.geom.flat.area.linearRing(flatCoordinates, offset, end, stride);
+ }
+ return false;
+};
+
+// This file is automatically generated, do not edit
+goog.provide('ol.render.webgl.linestringreplay.defaultshader');
+
+goog.require('ol');
+goog.require('ol.webgl.Fragment');
+goog.require('ol.webgl.Vertex');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Fragment}
+ * @struct
+ */
+ol.render.webgl.linestringreplay.defaultshader.Fragment = function() {
+ ol.webgl.Fragment.call(this, ol.render.webgl.linestringreplay.defaultshader.Fragment.SOURCE);
+};
+ol.inherits(ol.render.webgl.linestringreplay.defaultshader.Fragment, ol.webgl.Fragment);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.linestringreplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying float v_round;\nvarying vec2 v_roundVertex;\nvarying float v_halfWidth;\n\n\n\nuniform float u_opacity;\nuniform vec4 u_color;\nuniform vec2 u_size;\nuniform float u_pixelRatio;\n\nvoid main(void) {\n if (v_round > 0.0) {\n vec2 windowCoords = vec2((v_roundVertex.x + 1.0) / 2.0 * u_size.x * u_pixelRatio,\n (v_roundVertex.y + 1.0) / 2.0 * u_size.y * u_pixelRatio);\n if (length(windowCoords - gl_FragCoord.xy) > v_halfWidth * u_pixelRatio) {\n discard;\n }\n }\n gl_FragColor = u_color;\n float alpha = u_color.a * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.linestringreplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying float a;varying vec2 b;varying float c;uniform float m;uniform vec4 n;uniform vec2 o;uniform float p;void main(void){if(a>0.0){vec2 windowCoords=vec2((b.x+1.0)/2.0*o.x*p,(b.y+1.0)/2.0*o.y*p);if(length(windowCoords-gl_FragCoord.xy)>c*p){discard;}} gl_FragColor=n;float alpha=n.a*m;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.linestringreplay.defaultshader.Fragment.SOURCE = ol.DEBUG ?
+ ol.render.webgl.linestringreplay.defaultshader.Fragment.DEBUG_SOURCE :
+ ol.render.webgl.linestringreplay.defaultshader.Fragment.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.linestringreplay.defaultshader.fragment = new ol.render.webgl.linestringreplay.defaultshader.Fragment();
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Vertex}
+ * @struct
+ */
+ol.render.webgl.linestringreplay.defaultshader.Vertex = function() {
+ ol.webgl.Vertex.call(this, ol.render.webgl.linestringreplay.defaultshader.Vertex.SOURCE);
+};
+ol.inherits(ol.render.webgl.linestringreplay.defaultshader.Vertex, ol.webgl.Vertex);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.linestringreplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying float v_round;\nvarying vec2 v_roundVertex;\nvarying float v_halfWidth;\n\n\nattribute vec2 a_lastPos;\nattribute vec2 a_position;\nattribute vec2 a_nextPos;\nattribute float a_direction;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\nuniform float u_lineWidth;\nuniform float u_miterLimit;\n\nbool nearlyEquals(in float value, in float ref) {\n float epsilon = 0.000000000001;\n return value >= ref - epsilon && value <= ref + epsilon;\n}\n\nvoid alongNormal(out vec2 offset, in vec2 nextP, in float turnDir, in float direction) {\n vec2 dirVect = nextP - a_position;\n vec2 normal = normalize(vec2(-turnDir * dirVect.y, turnDir * dirVect.x));\n offset = u_lineWidth / 2.0 * normal * direction;\n}\n\nvoid miterUp(out vec2 offset, out float round, in bool isRound, in float direction) {\n float halfWidth = u_lineWidth / 2.0;\n vec2 tangent = normalize(normalize(a_nextPos - a_position) + normalize(a_position - a_lastPos));\n vec2 normal = vec2(-tangent.y, tangent.x);\n vec2 dirVect = a_nextPos - a_position;\n vec2 tmpNormal = normalize(vec2(-dirVect.y, dirVect.x));\n float miterLength = abs(halfWidth / dot(normal, tmpNormal));\n offset = normal * direction * miterLength;\n round = 0.0;\n if (isRound) {\n round = 1.0;\n } else if (miterLength > u_miterLimit + u_lineWidth) {\n offset = halfWidth * tmpNormal * direction;\n }\n}\n\nbool miterDown(out vec2 offset, in vec4 projPos, in mat4 offsetMatrix, in float direction) {\n bool degenerate = false;\n vec2 tangent = normalize(normalize(a_nextPos - a_position) + normalize(a_position - a_lastPos));\n vec2 normal = vec2(-tangent.y, tangent.x);\n vec2 dirVect = a_lastPos - a_position;\n vec2 tmpNormal = normalize(vec2(-dirVect.y, dirVect.x));\n vec2 longOffset, shortOffset, longVertex;\n vec4 shortProjVertex;\n float halfWidth = u_lineWidth / 2.0;\n if (length(a_nextPos - a_position) > length(a_lastPos - a_position)) {\n longOffset = tmpNormal * direction * halfWidth;\n shortOffset = normalize(vec2(dirVect.y, -dirVect.x)) * direction * halfWidth;\n longVertex = a_nextPos;\n shortProjVertex = u_projectionMatrix * vec4(a_lastPos, 0.0, 1.0);\n } else {\n shortOffset = tmpNormal * direction * halfWidth;\n longOffset = normalize(vec2(dirVect.y, -dirVect.x)) * direction * halfWidth;\n longVertex = a_lastPos;\n shortProjVertex = u_projectionMatrix * vec4(a_nextPos, 0.0, 1.0);\n }\n //Intersection algorithm based on theory by Paul Bourke (http://paulbourke.net/geometry/pointlineplane/).\n vec4 p1 = u_projectionMatrix * vec4(longVertex, 0.0, 1.0) + offsetMatrix * vec4(longOffset, 0.0, 0.0);\n vec4 p2 = projPos + offsetMatrix * vec4(longOffset, 0.0, 0.0);\n vec4 p3 = shortProjVertex + offsetMatrix * vec4(-shortOffset, 0.0, 0.0);\n vec4 p4 = shortProjVertex + offsetMatrix * vec4(shortOffset, 0.0, 0.0);\n float denom = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);\n float firstU = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;\n float secondU = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;\n float epsilon = 0.000000000001;\n if (firstU > epsilon && firstU < 1.0 - epsilon && secondU > epsilon && secondU < 1.0 - epsilon) {\n shortProjVertex.x = p1.x + firstU * (p2.x - p1.x);\n shortProjVertex.y = p1.y + firstU * (p2.y - p1.y);\n offset = shortProjVertex.xy;\n degenerate = true;\n } else {\n float miterLength = abs(halfWidth / dot(normal, tmpNormal));\n offset = normal * direction * miterLength;\n }\n return degenerate;\n}\n\nvoid squareCap(out vec2 offset, out float round, in bool isRound, in vec2 nextP,\n in float turnDir, in float direction) {\n round = 0.0;\n vec2 dirVect = a_position - nextP;\n vec2 firstNormal = normalize(dirVect);\n vec2 secondNormal = vec2(turnDir * firstNormal.y * direction, -turnDir * firstNormal.x * direction);\n vec2 hypotenuse = normalize(firstNormal - secondNormal);\n vec2 normal = vec2(turnDir * hypotenuse.y * direction, -turnDir * hypotenuse.x * direction);\n float length = sqrt(v_halfWidth * v_halfWidth * 2.0);\n offset = normal * length;\n if (isRound) {\n round = 1.0;\n }\n}\n\nvoid main(void) {\n bool degenerate = false;\n float direction = float(sign(a_direction));\n mat4 offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n vec2 offset;\n vec4 projPos = u_projectionMatrix * vec4(a_position, 0.0, 1.0);\n bool round = nearlyEquals(mod(a_direction, 2.0), 0.0);\n\n v_round = 0.0;\n v_halfWidth = u_lineWidth / 2.0;\n v_roundVertex = projPos.xy;\n\n if (nearlyEquals(mod(a_direction, 3.0), 0.0) || nearlyEquals(mod(a_direction, 17.0), 0.0)) {\n alongNormal(offset, a_nextPos, 1.0, direction);\n } else if (nearlyEquals(mod(a_direction, 5.0), 0.0) || nearlyEquals(mod(a_direction, 13.0), 0.0)) {\n alongNormal(offset, a_lastPos, -1.0, direction);\n } else if (nearlyEquals(mod(a_direction, 23.0), 0.0)) {\n miterUp(offset, v_round, round, direction);\n } else if (nearlyEquals(mod(a_direction, 19.0), 0.0)) {\n degenerate = miterDown(offset, projPos, offsetMatrix, direction);\n } else if (nearlyEquals(mod(a_direction, 7.0), 0.0)) {\n squareCap(offset, v_round, round, a_nextPos, 1.0, direction);\n } else if (nearlyEquals(mod(a_direction, 11.0), 0.0)) {\n squareCap(offset, v_round, round, a_lastPos, -1.0, direction);\n }\n if (!degenerate) {\n vec4 offsets = offsetMatrix * vec4(offset, 0.0, 0.0);\n gl_Position = projPos + offsets;\n } else {\n gl_Position = vec4(offset, 0.0, 1.0);\n }\n}\n\n\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.linestringreplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying float a;varying vec2 b;varying float c;attribute vec2 d;attribute vec2 e;attribute vec2 f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;bool nearlyEquals(in float value,in float ref){float epsilon=0.000000000001;return value>=ref-epsilon&&value<=ref+epsilon;}void alongNormal(out vec2 offset,in vec2 nextP,in float turnDir,in float direction){vec2 dirVect=nextP-e;vec2 normal=normalize(vec2(-turnDir*dirVect.y,turnDir*dirVect.x));offset=k/2.0*normal*direction;}void miterUp(out vec2 offset,out float round,in bool isRound,in float direction){float halfWidth=k/2.0;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=f-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;round=0.0;if(isRound){round=1.0;}else if(miterLength>l+k){offset=halfWidth*tmpNormal*direction;}} bool miterDown(out vec2 offset,in vec4 projPos,in mat4 offsetMatrix,in float direction){bool degenerate=false;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=d-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));vec2 longOffset,shortOffset,longVertex;vec4 shortProjVertex;float halfWidth=k/2.0;if(length(f-e)>length(d-e)){longOffset=tmpNormal*direction*halfWidth;shortOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=f;shortProjVertex=h*vec4(d,0.0,1.0);}else{shortOffset=tmpNormal*direction*halfWidth;longOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=d;shortProjVertex=h*vec4(f,0.0,1.0);}vec4 p1=h*vec4(longVertex,0.0,1.0)+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p2=projPos+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p3=shortProjVertex+offsetMatrix*vec4(-shortOffset,0.0,0.0);vec4 p4=shortProjVertex+offsetMatrix*vec4(shortOffset,0.0,0.0);float denom=(p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y);float firstU=((p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x))/denom;float secondU=((p2.x-p1.x)*(p1.y-p3.y)-(p2.y-p1.y)*(p1.x-p3.x))/denom;float epsilon=0.000000000001;if(firstU>epsilon&&firstU<1.0-epsilon&&secondU>epsilon&&secondU<1.0-epsilon){shortProjVertex.x=p1.x+firstU*(p2.x-p1.x);shortProjVertex.y=p1.y+firstU*(p2.y-p1.y);offset=shortProjVertex.xy;degenerate=true;}else{float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;}return degenerate;}void squareCap(out vec2 offset,out float round,in bool isRound,in vec2 nextP,in float turnDir,in float direction){round=0.0;vec2 dirVect=e-nextP;vec2 firstNormal=normalize(dirVect);vec2 secondNormal=vec2(turnDir*firstNormal.y*direction,-turnDir*firstNormal.x*direction);vec2 hypotenuse=normalize(firstNormal-secondNormal);vec2 normal=vec2(turnDir*hypotenuse.y*direction,-turnDir*hypotenuse.x*direction);float length=sqrt(c*c*2.0);offset=normal*length;if(isRound){round=1.0;}} void main(void){bool degenerate=false;float direction=float(sign(g));mat4 offsetMatrix=i*j;vec2 offset;vec4 projPos=h*vec4(e,0.0,1.0);bool round=nearlyEquals(mod(g,2.0),0.0);a=0.0;c=k/2.0;b=projPos.xy;if(nearlyEquals(mod(g,3.0),0.0)||nearlyEquals(mod(g,17.0),0.0)){alongNormal(offset,f,1.0,direction);}else if(nearlyEquals(mod(g,5.0),0.0)||nearlyEquals(mod(g,13.0),0.0)){alongNormal(offset,d,-1.0,direction);}else if(nearlyEquals(mod(g,23.0),0.0)){miterUp(offset,a,round,direction);}else if(nearlyEquals(mod(g,19.0),0.0)){degenerate=miterDown(offset,projPos,offsetMatrix,direction);}else if(nearlyEquals(mod(g,7.0),0.0)){squareCap(offset,a,round,f,1.0,direction);}else if(nearlyEquals(mod(g,11.0),0.0)){squareCap(offset,a,round,d,-1.0,direction);}if(!degenerate){vec4 offsets=offsetMatrix*vec4(offset,0.0,0.0);gl_Position=projPos+offsets;}else{gl_Position=vec4(offset,0.0,1.0);}}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.linestringreplay.defaultshader.Vertex.SOURCE = ol.DEBUG ?
+ ol.render.webgl.linestringreplay.defaultshader.Vertex.DEBUG_SOURCE :
+ ol.render.webgl.linestringreplay.defaultshader.Vertex.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.linestringreplay.defaultshader.vertex = new ol.render.webgl.linestringreplay.defaultshader.Vertex();
+
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ * @struct
+ */
+ol.render.webgl.linestringreplay.defaultshader.Locations = function(gl, program) {
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_color = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_color' : 'n');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_lineWidth = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_lineWidth' : 'k');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_miterLimit = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_miterLimit' : 'l');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetRotateMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetRotateMatrix' : 'j');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetScaleMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetScaleMatrix' : 'i');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_opacity = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_opacity' : 'm');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_pixelRatio = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_pixelRatio' : 'p');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_projectionMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_projectionMatrix' : 'h');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_size = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_size' : 'o');
+
+ /**
+ * @type {number}
+ */
+ this.a_direction = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_direction' : 'g');
+
+ /**
+ * @type {number}
+ */
+ this.a_lastPos = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_lastPos' : 'd');
+
+ /**
+ * @type {number}
+ */
+ this.a_nextPos = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_nextPos' : 'f');
+
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_position' : 'e');
+};
+
+goog.provide('ol.render.webgl.LineStringReplay');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.color');
+goog.require('ol.extent');
+goog.require('ol.geom.flat.orient');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.geom.flat.topology');
+goog.require('ol.obj');
+goog.require('ol.render.webgl');
+goog.require('ol.render.webgl.Replay');
+goog.require('ol.render.webgl.linestringreplay.defaultshader');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Buffer');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.webgl.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @struct
+ */
+ol.render.webgl.LineStringReplay = function(tolerance, maxExtent) {
+ ol.render.webgl.Replay.call(this, tolerance, maxExtent);
+
+ /**
+ * @private
+ * @type {ol.render.webgl.linestringreplay.defaultshader.Locations}
+ */
+ this.defaultLocations_ = null;
+
+ /**
+ * @private
+ * @type {Array.<Array.<?>>}
+ */
+ this.styles_ = [];
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.styleIndices_ = [];
+
+ /**
+ * @private
+ * @type {{strokeColor: (Array.<number>|null),
+ * lineCap: (string|undefined),
+ * lineDash: Array.<number>,
+ * lineJoin: (string|undefined),
+ * lineWidth: (number|undefined),
+ * miterLimit: (number|undefined),
+ * changed: boolean}|null}
+ */
+ this.state_ = {
+ strokeColor: null,
+ lineCap: undefined,
+ lineDash: null,
+ lineJoin: undefined,
+ lineWidth: undefined,
+ miterLimit: undefined,
+ changed: false
+ };
+
+};
+ol.inherits(ol.render.webgl.LineStringReplay, ol.render.webgl.Replay);
+
+
+/**
+ * Draw one segment.
+ * @private
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ */
+ol.render.webgl.LineStringReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) {
+
+ var i, ii;
+ var numVertices = this.vertices.length;
+ var numIndices = this.indices.length;
+ //To save a vertex, the direction of a point is a product of the sign (1 or -1), a prime from
+ //ol.render.webgl.lineStringInstruction, and a rounding factor (1 or 2). If the product is even,
+ //we round it. If it is odd, we don't.
+ var lineJoin = this.state_.lineJoin === 'bevel' ? 0 :
+ this.state_.lineJoin === 'miter' ? 1 : 2;
+ var lineCap = this.state_.lineCap === 'butt' ? 0 :
+ this.state_.lineCap === 'square' ? 1 : 2;
+ var closed = ol.geom.flat.topology.lineStringIsClosed(flatCoordinates, offset, end, stride);
+ var startCoords, sign, n;
+ var lastIndex = numIndices;
+ var lastSign = 1;
+ //We need the adjacent vertices to define normals in joins. p0 = last, p1 = current, p2 = next.
+ var p0, p1, p2;
+
+ for (i = offset, ii = end; i < ii; i += stride) {
+
+ n = numVertices / 7;
+
+ p0 = p1;
+ p1 = p2 || [flatCoordinates[i], flatCoordinates[i + 1]];
+ //First vertex.
+ if (i === offset) {
+ p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]];
+ if (end - offset === stride * 2 && ol.array.equals(p1, p2)) {
+ break;
+ }
+ if (closed) {
+ //A closed line! Complete the circle.
+ p0 = [flatCoordinates[end - stride * 2],
+ flatCoordinates[end - stride * 2 + 1]];
+
+ startCoords = p2;
+ } else {
+ //Add the first two/four vertices.
+
+ if (lineCap) {
+ numVertices = this.addVertices_([0, 0], p1, p2,
+ lastSign * ol.render.webgl.lineStringInstruction.BEGIN_LINE_CAP * lineCap, numVertices);
+
+ numVertices = this.addVertices_([0, 0], p1, p2,
+ -lastSign * ol.render.webgl.lineStringInstruction.BEGIN_LINE_CAP * lineCap, numVertices);
+
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = n + 1;
+
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n + 3;
+ this.indices[numIndices++] = n + 2;
+
+ }
+
+ numVertices = this.addVertices_([0, 0], p1, p2,
+ lastSign * ol.render.webgl.lineStringInstruction.BEGIN_LINE * (lineCap || 1), numVertices);
+
+ numVertices = this.addVertices_([0, 0], p1, p2,
+ -lastSign * ol.render.webgl.lineStringInstruction.BEGIN_LINE * (lineCap || 1), numVertices);
+
+ lastIndex = numVertices / 7 - 1;
+
+ continue;
+ }
+ } else if (i === end - stride) {
+ //Last vertex.
+ if (closed) {
+ //Same as the first vertex.
+ p2 = startCoords;
+ break;
+ } else {
+ //For the compiler not to complain. This will never be [0, 0].
+ ol.DEBUG && console.assert(p0, 'p0 should be defined');
+ p0 = p0 || [0, 0];
+
+ numVertices = this.addVertices_(p0, p1, [0, 0],
+ lastSign * ol.render.webgl.lineStringInstruction.END_LINE * (lineCap || 1), numVertices);
+
+ numVertices = this.addVertices_(p0, p1, [0, 0],
+ -lastSign * ol.render.webgl.lineStringInstruction.END_LINE * (lineCap || 1), numVertices);
+
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = lastIndex - 1;
+ this.indices[numIndices++] = lastIndex;
+
+ this.indices[numIndices++] = lastIndex;
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n;
+
+ if (lineCap) {
+ numVertices = this.addVertices_(p0, p1, [0, 0],
+ lastSign * ol.render.webgl.lineStringInstruction.END_LINE_CAP * lineCap, numVertices);
+
+ numVertices = this.addVertices_(p0, p1, [0, 0],
+ -lastSign * ol.render.webgl.lineStringInstruction.END_LINE_CAP * lineCap, numVertices);
+
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = n + 1;
+
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n + 3;
+ this.indices[numIndices++] = n + 2;
+
+ }
+
+ break;
+ }
+ } else {
+ p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]];
+ }
+
+ // We group CW and straight lines, thus the not so inituitive CCW checking function.
+ sign = ol.render.webgl.triangleIsCounterClockwise(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1])
+ ? -1 : 1;
+
+ numVertices = this.addVertices_(p0, p1, p2,
+ sign * ol.render.webgl.lineStringInstruction.BEVEL_FIRST * (lineJoin || 1), numVertices);
+
+ numVertices = this.addVertices_(p0, p1, p2,
+ sign * ol.render.webgl.lineStringInstruction.BEVEL_SECOND * (lineJoin || 1), numVertices);
+
+ numVertices = this.addVertices_(p0, p1, p2,
+ -sign * ol.render.webgl.lineStringInstruction.MITER_BOTTOM * (lineJoin || 1), numVertices);
+
+ if (i > offset) {
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = lastIndex - 1;
+ this.indices[numIndices++] = lastIndex;
+
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1;
+ }
+
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = n + 2;
+ this.indices[numIndices++] = n + 1;
+
+ lastIndex = n + 2;
+ lastSign = sign;
+
+ //Add miter
+ if (lineJoin) {
+ numVertices = this.addVertices_(p0, p1, p2,
+ sign * ol.render.webgl.lineStringInstruction.MITER_TOP * lineJoin, numVertices);
+
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n + 3;
+ this.indices[numIndices++] = n;
+ }
+ }
+
+ if (closed) {
+ //Link the last triangle/rhombus to the first one.
+ //n will never be numVertices / 7 here. However, the compiler complains otherwise.
+ ol.DEBUG && console.assert(n, 'n should be defined');
+ n = n || numVertices / 7;
+ sign = ol.geom.flat.orient.linearRingIsClockwise([p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]], 0, 6, 2)
+ ? 1 : -1;
+
+ numVertices = this.addVertices_(p0, p1, p2,
+ sign * ol.render.webgl.lineStringInstruction.BEVEL_FIRST * (lineJoin || 1), numVertices);
+
+ numVertices = this.addVertices_(p0, p1, p2,
+ -sign * ol.render.webgl.lineStringInstruction.MITER_BOTTOM * (lineJoin || 1), numVertices);
+
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = lastIndex - 1;
+ this.indices[numIndices++] = lastIndex;
+
+ this.indices[numIndices++] = n + 1;
+ this.indices[numIndices++] = n;
+ this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1;
+ }
+};
+
+/**
+ * @param {Array.<number>} p0 Last coordinates.
+ * @param {Array.<number>} p1 Current coordinates.
+ * @param {Array.<number>} p2 Next coordinates.
+ * @param {number} product Sign, instruction, and rounding product.
+ * @param {number} numVertices Vertex counter.
+ * @return {number} Vertex counter.
+ * @private
+ */
+ol.render.webgl.LineStringReplay.prototype.addVertices_ = function(p0, p1, p2, product, numVertices) {
+ this.vertices[numVertices++] = p0[0];
+ this.vertices[numVertices++] = p0[1];
+ this.vertices[numVertices++] = p1[0];
+ this.vertices[numVertices++] = p1[1];
+ this.vertices[numVertices++] = p2[0];
+ this.vertices[numVertices++] = p2[1];
+ this.vertices[numVertices++] = product;
+
+ return numVertices;
+};
+
+/**
+ * Check if the linestring can be drawn (i. e. valid).
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {boolean} The linestring can be drawn.
+ * @private
+ */
+ol.render.webgl.LineStringReplay.prototype.isValid_ = function(flatCoordinates, offset, end, stride) {
+ var range = end - offset;
+ if (range < stride * 2) {
+ return false;
+ } else if (range === stride * 2) {
+ var firstP = [flatCoordinates[offset], flatCoordinates[offset + 1]];
+ var lastP = [flatCoordinates[offset + stride], flatCoordinates[offset + stride + 1]];
+ return !ol.array.equals(firstP, lastP);
+ }
+
+ return true;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) {
+ var flatCoordinates = lineStringGeometry.getFlatCoordinates();
+ var stride = lineStringGeometry.getStride();
+ if (this.isValid_(flatCoordinates, 0, flatCoordinates.length, stride)) {
+ flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length,
+ stride, -this.origin[0], -this.origin[1]);
+ if (this.state_.changed) {
+ this.styleIndices_.push(this.indices.length);
+ this.state_.changed = false;
+ }
+ this.startIndices.push(this.indices.length);
+ this.startIndicesFeature.push(feature);
+ this.drawCoordinates_(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {
+ var indexCount = this.indices.length;
+ var lineStringGeometries = multiLineStringGeometry.getLineStrings();
+ var i, ii;
+ for (i = 0, ii = lineStringGeometries.length; i < ii; ++i) {
+ var flatCoordinates = lineStringGeometries[i].getFlatCoordinates();
+ var stride = lineStringGeometries[i].getStride();
+ if (this.isValid_(flatCoordinates, 0, flatCoordinates.length, stride)) {
+ flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length,
+ stride, -this.origin[0], -this.origin[1]);
+ this.drawCoordinates_(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+ }
+ }
+ if (this.indices.length > indexCount) {
+ this.startIndices.push(indexCount);
+ this.startIndicesFeature.push(feature);
+ if (this.state_.changed) {
+ this.styleIndices_.push(indexCount);
+ this.state_.changed = false;
+ }
+ }
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {Array.<Array.<number>>} holeFlatCoordinates Hole flat coordinates.
+ * @param {number} stride Stride.
+ */
+ol.render.webgl.LineStringReplay.prototype.drawPolygonCoordinates = function(
+ flatCoordinates, holeFlatCoordinates, stride) {
+ if (!ol.geom.flat.topology.lineStringIsClosed(flatCoordinates, 0,
+ flatCoordinates.length, stride)) {
+ flatCoordinates.push(flatCoordinates[0]);
+ flatCoordinates.push(flatCoordinates[1]);
+ }
+ this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
+ if (holeFlatCoordinates.length) {
+ var i, ii;
+ for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) {
+ if (!ol.geom.flat.topology.lineStringIsClosed(holeFlatCoordinates[i], 0,
+ holeFlatCoordinates[i].length, stride)) {
+ holeFlatCoordinates[i].push(holeFlatCoordinates[i][0]);
+ holeFlatCoordinates[i].push(holeFlatCoordinates[i][1]);
+ }
+ this.drawCoordinates_(holeFlatCoordinates[i], 0,
+ holeFlatCoordinates[i].length, stride);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {number=} opt_index Index count.
+ */
+ol.render.webgl.LineStringReplay.prototype.setPolygonStyle = function(feature, opt_index) {
+ var index = opt_index === undefined ? this.indices.length : opt_index;
+ this.startIndices.push(index);
+ this.startIndicesFeature.push(feature);
+ if (this.state_.changed) {
+ this.styleIndices_.push(index);
+ this.state_.changed = false;
+ }
+};
+
+
+/**
+ * @return {number} Current index.
+ */
+ol.render.webgl.LineStringReplay.prototype.getCurrentIndex = function() {
+ return this.indices.length;
+};
+
+
+/**
+ * @inheritDoc
+ **/
+ol.render.webgl.LineStringReplay.prototype.finish = function(context) {
+ // create, bind, and populate the vertices buffer
+ this.verticesBuffer = new ol.webgl.Buffer(this.vertices);
+
+ // create, bind, and populate the indices buffer
+ this.indicesBuffer = new ol.webgl.Buffer(this.indices);
+
+ this.startIndices.push(this.indices.length);
+
+ //Clean up, if there is nothing to draw
+ if (this.styleIndices_.length === 0 && this.styles_.length > 0) {
+ this.styles_ = [];
+ }
+
+ this.vertices = null;
+ this.indices = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.getDeleteResourcesFunction = function(context) {
+ // We only delete our stuff here. The shaders and the program may
+ // be used by other LineStringReplay instances (for other layers). And
+ // they will be deleted when disposing of the ol.webgl.Context
+ // object.
+ ol.DEBUG && console.assert(this.verticesBuffer, 'verticesBuffer must not be null');
+ var verticesBuffer = this.verticesBuffer;
+ var indicesBuffer = this.indicesBuffer;
+ return function() {
+ context.deleteBuffer(verticesBuffer);
+ context.deleteBuffer(indicesBuffer);
+ };
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {
+ // get the program
+ var fragmentShader, vertexShader;
+ fragmentShader = ol.render.webgl.linestringreplay.defaultshader.fragment;
+ vertexShader = ol.render.webgl.linestringreplay.defaultshader.vertex;
+ var program = context.getProgram(fragmentShader, vertexShader);
+
+ // get the locations
+ var locations;
+ if (!this.defaultLocations_) {
+ locations =
+ new ol.render.webgl.linestringreplay.defaultshader.Locations(gl, program);
+ this.defaultLocations_ = locations;
+ } else {
+ locations = this.defaultLocations_;
+ }
+
+ context.useProgram(program);
+
+ // enable the vertex attrib arrays
+ gl.enableVertexAttribArray(locations.a_lastPos);
+ gl.vertexAttribPointer(locations.a_lastPos, 2, ol.webgl.FLOAT,
+ false, 28, 0);
+
+ gl.enableVertexAttribArray(locations.a_position);
+ gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT,
+ false, 28, 8);
+
+ gl.enableVertexAttribArray(locations.a_nextPos);
+ gl.vertexAttribPointer(locations.a_nextPos, 2, ol.webgl.FLOAT,
+ false, 28, 16);
+
+ gl.enableVertexAttribArray(locations.a_direction);
+ gl.vertexAttribPointer(locations.a_direction, 1, ol.webgl.FLOAT,
+ false, 28, 24);
+
+ // Enable renderer specific uniforms.
+ gl.uniform2fv(locations.u_size, size);
+ gl.uniform1f(locations.u_pixelRatio, pixelRatio);
+
+ return locations;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.shutDownProgram = function(gl, locations) {
+ gl.disableVertexAttribArray(locations.a_lastPos);
+ gl.disableVertexAttribArray(locations.a_position);
+ gl.disableVertexAttribArray(locations.a_nextPos);
+ gl.disableVertexAttribArray(locations.a_direction);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {
+ //Save GL parameters.
+ var tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC));
+ var tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK));
+
+ if (!hitDetection) {
+ gl.enable(gl.DEPTH_TEST);
+ gl.depthMask(true);
+ gl.depthFunc(gl.NOTEQUAL);
+ }
+
+ if (!ol.obj.isEmpty(skippedFeaturesHash)) {
+ this.drawReplaySkipping_(gl, context, skippedFeaturesHash);
+ } else {
+ ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
+ 'number of styles and styleIndices match');
+
+ //Draw by style groups to minimize drawElements() calls.
+ var i, start, end, nextStyle;
+ end = this.startIndices[this.startIndices.length - 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ start = this.styleIndices_[i];
+ nextStyle = this.styles_[i];
+ this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]);
+ this.drawElements(gl, context, start, end);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ end = start;
+ }
+ }
+ if (!hitDetection) {
+ gl.disable(gl.DEPTH_TEST);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ //Restore GL parameters.
+ gl.depthMask(tmpDepthMask);
+ gl.depthFunc(tmpDepthFunc);
+ }
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object} skippedFeaturesHash Ids of features to skip.
+ */
+ol.render.webgl.LineStringReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) {
+ ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
+ 'number of startIndices and startIndicesFeature match');
+
+ var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart;
+ featureIndex = this.startIndices.length - 2;
+ end = start = this.startIndices[featureIndex + 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ nextStyle = this.styles_[i];
+ this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]);
+ groupStart = this.styleIndices_[i];
+
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ featureStart = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid]) {
+ if (start !== end) {
+ this.drawElements(gl, context, start, end);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ end = featureStart;
+ }
+ featureIndex--;
+ start = featureStart;
+ }
+ if (start !== end) {
+ this.drawElements(gl, context, start, end);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ start = end = groupStart;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash,
+ featureCallback, opt_hitExtent) {
+ ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
+ 'number of styles and styleIndices match');
+ ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
+ 'number of startIndices and startIndicesFeature match');
+
+ var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex;
+ featureIndex = this.startIndices.length - 2;
+ end = this.startIndices[featureIndex + 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ nextStyle = this.styles_[i];
+ this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]);
+ groupStart = this.styleIndices_[i];
+
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ start = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid] === undefined &&
+ feature.getGeometry() &&
+ (opt_hitExtent === undefined || ol.extent.intersects(
+ /** @type {Array<number>} */ (opt_hitExtent),
+ feature.getGeometry().getExtent()))) {
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ this.drawElements(gl, context, start, end);
+
+ var result = featureCallback(feature);
+
+ if (result) {
+ return result;
+ }
+
+ }
+ featureIndex--;
+ end = start;
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {Array.<number>} color Color.
+ * @param {number} lineWidth Line width.
+ * @param {number} miterLimit Miter limit.
+ */
+ol.render.webgl.LineStringReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth, miterLimit) {
+ gl.uniform4fv(this.defaultLocations_.u_color, color);
+ gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth);
+ gl.uniform1f(this.defaultLocations_.u_miterLimit, miterLimit);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.LineStringReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
+ var strokeStyleLineCap = strokeStyle.getLineCap();
+ this.state_.lineCap = strokeStyleLineCap !== undefined ?
+ strokeStyleLineCap : ol.render.webgl.defaultLineCap;
+ var strokeStyleLineDash = strokeStyle.getLineDash();
+ this.state_.lineDash = strokeStyleLineDash ?
+ strokeStyleLineDash : ol.render.webgl.defaultLineDash;
+ var strokeStyleLineJoin = strokeStyle.getLineJoin();
+ this.state_.lineJoin = strokeStyleLineJoin !== undefined ?
+ strokeStyleLineJoin : ol.render.webgl.defaultLineJoin;
+ var strokeStyleColor = strokeStyle.getColor();
+ if (!(strokeStyleColor instanceof CanvasGradient) &&
+ !(strokeStyleColor instanceof CanvasPattern)) {
+ strokeStyleColor = ol.color.asArray(strokeStyleColor).map(function(c, i) {
+ return i != 3 ? c / 255 : c;
+ }) || ol.render.webgl.defaultStrokeStyle;
+ } else {
+ strokeStyleColor = ol.render.webgl.defaultStrokeStyle;
+ }
+ var strokeStyleWidth = strokeStyle.getWidth();
+ strokeStyleWidth = strokeStyleWidth !== undefined ?
+ strokeStyleWidth : ol.render.webgl.defaultLineWidth;
+ var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
+ strokeStyleMiterLimit = strokeStyleMiterLimit !== undefined ?
+ strokeStyleMiterLimit : ol.render.webgl.defaultMiterLimit;
+ if (!this.state_.strokeColor || !ol.array.equals(this.state_.strokeColor, strokeStyleColor) ||
+ this.state_.lineWidth !== strokeStyleWidth || this.state_.miterLimit !== strokeStyleMiterLimit) {
+ this.state_.changed = true;
+ this.state_.strokeColor = strokeStyleColor;
+ this.state_.lineWidth = strokeStyleWidth;
+ this.state_.miterLimit = strokeStyleMiterLimit;
+ this.styles_.push([strokeStyleColor, strokeStyleWidth, strokeStyleMiterLimit]);
+ }
+};
+
+// This file is automatically generated, do not edit
+goog.provide('ol.render.webgl.polygonreplay.defaultshader');
+
+goog.require('ol');
+goog.require('ol.webgl.Fragment');
+goog.require('ol.webgl.Vertex');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Fragment}
+ * @struct
+ */
+ol.render.webgl.polygonreplay.defaultshader.Fragment = function() {
+ ol.webgl.Fragment.call(this, ol.render.webgl.polygonreplay.defaultshader.Fragment.SOURCE);
+};
+ol.inherits(ol.render.webgl.polygonreplay.defaultshader.Fragment, ol.webgl.Fragment);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.polygonreplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\n\n\n\nuniform vec4 u_color;\nuniform float u_opacity;\n\nvoid main(void) {\n gl_FragColor = u_color;\n float alpha = u_color.a * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.polygonreplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;uniform vec4 e;uniform float f;void main(void){gl_FragColor=e;float alpha=e.a*f;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.polygonreplay.defaultshader.Fragment.SOURCE = ol.DEBUG ?
+ ol.render.webgl.polygonreplay.defaultshader.Fragment.DEBUG_SOURCE :
+ ol.render.webgl.polygonreplay.defaultshader.Fragment.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.polygonreplay.defaultshader.fragment = new ol.render.webgl.polygonreplay.defaultshader.Fragment();
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Vertex}
+ * @struct
+ */
+ol.render.webgl.polygonreplay.defaultshader.Vertex = function() {
+ ol.webgl.Vertex.call(this, ol.render.webgl.polygonreplay.defaultshader.Vertex.SOURCE);
+};
+ol.inherits(ol.render.webgl.polygonreplay.defaultshader.Vertex, ol.webgl.Vertex);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.polygonreplay.defaultshader.Vertex.DEBUG_SOURCE = '\n\nattribute vec2 a_position;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0);\n}\n\n\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.polygonreplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'attribute vec2 a;uniform mat4 b;uniform mat4 c;uniform mat4 d;void main(void){gl_Position=b*vec4(a,0.0,1.0);}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.render.webgl.polygonreplay.defaultshader.Vertex.SOURCE = ol.DEBUG ?
+ ol.render.webgl.polygonreplay.defaultshader.Vertex.DEBUG_SOURCE :
+ ol.render.webgl.polygonreplay.defaultshader.Vertex.OPTIMIZED_SOURCE;
+
+
+ol.render.webgl.polygonreplay.defaultshader.vertex = new ol.render.webgl.polygonreplay.defaultshader.Vertex();
+
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ * @struct
+ */
+ol.render.webgl.polygonreplay.defaultshader.Locations = function(gl, program) {
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_color = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_color' : 'e');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetRotateMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetRotateMatrix' : 'd');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_offsetScaleMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_offsetScaleMatrix' : 'c');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_opacity = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_opacity' : 'f');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_projectionMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_projectionMatrix' : 'b');
+
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_position' : 'a');
+};
+
+goog.provide('ol.structs.LinkedList');
+
+/**
+ * Creates an empty linked list structure.
+ *
+ * @constructor
+ * @struct
+ * @param {boolean=} opt_circular The last item is connected to the first one,
+ * and the first item to the last one. Default is true.
+ */
+ol.structs.LinkedList = function(opt_circular) {
+
+ /**
+ * @private
+ * @type {ol.LinkedListItem|undefined}
+ */
+ this.first_ = undefined;
+
+ /**
+ * @private
+ * @type {ol.LinkedListItem|undefined}
+ */
+ this.last_ = undefined;
+
+ /**
+ * @private
+ * @type {ol.LinkedListItem|undefined}
+ */
+ this.head_ = undefined;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.circular_ = opt_circular === undefined ? true : opt_circular;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.length_ = 0;
+};
+
+/**
+ * Inserts an item into the linked list right after the current one.
+ *
+ * @param {?} data Item data.
+ */
+ol.structs.LinkedList.prototype.insertItem = function(data) {
+
+ /** @type {ol.LinkedListItem} */
+ var item = {
+ prev: undefined,
+ next: undefined,
+ data: data
+ };
+
+ var head = this.head_;
+
+ //Initialize the list.
+ if (!head) {
+ this.first_ = item;
+ this.last_ = item;
+ if (this.circular_) {
+ item.next = item;
+ item.prev = item;
+ }
+ } else {
+ //Link the new item to the adjacent ones.
+ var next = head.next;
+ item.prev = head;
+ item.next = next;
+ head.next = item;
+ if (next) {
+ next.prev = item;
+ }
+
+ if (head === this.last_) {
+ this.last_ = item;
+ }
+ }
+ this.head_ = item;
+ this.length_++;
+};
+
+/**
+ * Removes the current item from the list. Sets the cursor to the next item,
+ * if possible.
+ */
+ol.structs.LinkedList.prototype.removeItem = function() {
+ var head = this.head_;
+ if (head) {
+ var next = head.next;
+ var prev = head.prev;
+ if (next) {
+ next.prev = prev;
+ }
+ if (prev) {
+ prev.next = next;
+ }
+ this.head_ = next || prev;
+
+ if (this.first_ === this.last_) {
+ this.head_ = undefined;
+ this.first_ = undefined;
+ this.last_ = undefined;
+ } else if (this.first_ === head) {
+ this.first_ = this.head_;
+ } else if (this.last_ === head) {
+ this.last_ = prev ? this.head_.prev : this.head_;
+ }
+ this.length_--;
+ }
+};
+
+/**
+ * Sets the cursor to the first item, and returns the associated data.
+ *
+ * @return {?} Item data.
+ */
+ol.structs.LinkedList.prototype.firstItem = function() {
+ this.head_ = this.first_;
+ if (this.head_) {
+ return this.head_.data;
+ }
+ return undefined;
+};
+
+/**
+* Sets the cursor to the last item, and returns the associated data.
+*
+* @return {?} Item data.
+*/
+ol.structs.LinkedList.prototype.lastItem = function() {
+ this.head_ = this.last_;
+ if (this.head_) {
+ return this.head_.data;
+ }
+ return undefined;
+};
+
+/**
+ * Sets the cursor to the next item, and returns the associated data.
+ *
+ * @return {?} Item data.
+ */
+ol.structs.LinkedList.prototype.nextItem = function() {
+ if (this.head_ && this.head_.next) {
+ this.head_ = this.head_.next;
+ return this.head_.data;
+ }
+ return undefined;
+};
+
+/**
+ * Returns the next item's data without moving the cursor.
+ *
+ * @return {?} Item data.
+ */
+ol.structs.LinkedList.prototype.getNextItem = function() {
+ if (this.head_ && this.head_.next) {
+ return this.head_.next.data;
+ }
+ return undefined;
+};
+
+/**
+ * Sets the cursor to the previous item, and returns the associated data.
+ *
+ * @return {?} Item data.
+ */
+ol.structs.LinkedList.prototype.prevItem = function() {
+ if (this.head_ && this.head_.prev) {
+ this.head_ = this.head_.prev;
+ return this.head_.data;
+ }
+ return undefined;
+};
+
+/**
+ * Returns the previous item's data without moving the cursor.
+ *
+ * @return {?} Item data.
+ */
+ol.structs.LinkedList.prototype.getPrevItem = function() {
+ if (this.head_ && this.head_.prev) {
+ return this.head_.prev.data;
+ }
+ return undefined;
+};
+
+/**
+ * Returns the current item's data.
+ *
+ * @return {?} Item data.
+ */
+ol.structs.LinkedList.prototype.getCurrItem = function() {
+ if (this.head_) {
+ return this.head_.data;
+ }
+ return undefined;
+};
+
+/**
+ * Sets the first item of the list. This only works for circular lists, and sets
+ * the last item accordingly.
+ */
+ol.structs.LinkedList.prototype.setFirstItem = function() {
+ if (this.circular_ && this.head_) {
+ this.first_ = this.head_;
+ this.last_ = this.head_.prev;
+ }
+};
+
+/**
+ * Concatenates two lists.
+ * @param {ol.structs.LinkedList} list List to merge into the current list.
+ */
+ol.structs.LinkedList.prototype.concat = function(list) {
+ if (list.head_) {
+ if (this.head_) {
+ var end = this.head_.next;
+ this.head_.next = list.first_;
+ list.first_.prev = this.head_;
+ end.prev = list.last_;
+ list.last_.next = end;
+ this.length_ += list.length_;
+ } else {
+ this.head_ = list.head_;
+ this.first_ = list.first_;
+ this.last_ = list.last_;
+ this.length_ = list.length_;
+ }
+ list.head_ = undefined;
+ list.first_ = undefined;
+ list.last_ = undefined;
+ list.length_ = 0;
+ }
+};
+
+/**
+ * Returns the current length of the list.
+ *
+ * @return {number} Length.
+ */
+ol.structs.LinkedList.prototype.getLength = function() {
+ return this.length_;
+};
+
+goog.provide('ol.ext.rbush');
+/** @typedef {function(*)} */
+ol.ext.rbush;
+(function() {
+var exports = {};
+var module = {exports: exports};
+var define;
+/**
+ * @fileoverview
+ * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility}
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rbush = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+'use strict';
+
+module.exports = partialSort;
+
+// Floyd-Rivest selection algorithm:
+// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
+// The k-th element will have the (k - left + 1)th smallest value in [left, right]
+
+function partialSort(arr, k, left, right, compare) {
+ left = left || 0;
+ right = right || (arr.length - 1);
+ compare = compare || defaultCompare;
+
+ while (right > left) {
+ if (right - left > 600) {
+ var n = right - left + 1;
+ var m = k - left + 1;
+ var z = Math.log(n);
+ var s = 0.5 * Math.exp(2 * z / 3);
+ var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
+ var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
+ var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
+ partialSort(arr, k, newLeft, newRight, compare);
+ }
+
+ var t = arr[k];
+ var i = left;
+ var j = right;
+
+ swap(arr, left, k);
+ if (compare(arr[right], t) > 0) swap(arr, left, right);
+
+ while (i < j) {
+ swap(arr, i, j);
+ i++;
+ j--;
+ while (compare(arr[i], t) < 0) i++;
+ while (compare(arr[j], t) > 0) j--;
+ }
+
+ if (compare(arr[left], t) === 0) swap(arr, left, j);
+ else {
+ j++;
+ swap(arr, j, right);
+ }
+
+ if (j <= k) left = j + 1;
+ if (k <= j) right = j - 1;
+ }
+}
+
+function swap(arr, i, j) {
+ var tmp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = tmp;
+}
+
+function defaultCompare(a, b) {
+ return a < b ? -1 : a > b ? 1 : 0;
+}
+
+},{}],2:[function(_dereq_,module,exports){
+'use strict';
+
+module.exports = rbush;
+
+var quickselect = _dereq_('quickselect');
+
+function rbush(maxEntries, format) {
+ if (!(this instanceof rbush)) return new rbush(maxEntries, format);
+
+ // max entries in a node is 9 by default; min node fill is 40% for best performance
+ this._maxEntries = Math.max(4, maxEntries || 9);
+ this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
+
+ if (format) {
+ this._initFormat(format);
+ }
+
+ this.clear();
+}
+
+rbush.prototype = {
+
+ all: function () {
+ return this._all(this.data, []);
+ },
+
+ search: function (bbox) {
+
+ var node = this.data,
+ result = [],
+ toBBox = this.toBBox;
+
+ if (!intersects(bbox, node)) return result;
+
+ var nodesToSearch = [],
+ i, len, child, childBBox;
+
+ while (node) {
+ for (i = 0, len = node.children.length; i < len; i++) {
+
+ child = node.children[i];
+ childBBox = node.leaf ? toBBox(child) : child;
+
+ if (intersects(bbox, childBBox)) {
+ if (node.leaf) result.push(child);
+ else if (contains(bbox, childBBox)) this._all(child, result);
+ else nodesToSearch.push(child);
+ }
+ }
+ node = nodesToSearch.pop();
+ }
+
+ return result;
+ },
+
+ collides: function (bbox) {
+
+ var node = this.data,
+ toBBox = this.toBBox;
+
+ if (!intersects(bbox, node)) return false;
+
+ var nodesToSearch = [],
+ i, len, child, childBBox;
+
+ while (node) {
+ for (i = 0, len = node.children.length; i < len; i++) {
+
+ child = node.children[i];
+ childBBox = node.leaf ? toBBox(child) : child;
+
+ if (intersects(bbox, childBBox)) {
+ if (node.leaf || contains(bbox, childBBox)) return true;
+ nodesToSearch.push(child);
+ }
+ }
+ node = nodesToSearch.pop();
+ }
+
+ return false;
+ },
+
+ load: function (data) {
+ if (!(data && data.length)) return this;
+
+ if (data.length < this._minEntries) {
+ for (var i = 0, len = data.length; i < len; i++) {
+ this.insert(data[i]);
+ }
+ return this;
+ }
+
+ // recursively build the tree with the given data from stratch using OMT algorithm
+ var node = this._build(data.slice(), 0, data.length - 1, 0);
+
+ if (!this.data.children.length) {
+ // save as is if tree is empty
+ this.data = node;
+
+ } else if (this.data.height === node.height) {
+ // split root if trees have the same height
+ this._splitRoot(this.data, node);
+
+ } else {
+ if (this.data.height < node.height) {
+ // swap trees if inserted one is bigger
+ var tmpNode = this.data;
+ this.data = node;
+ node = tmpNode;
+ }
+
+ // insert the small tree into the large tree at appropriate level
+ this._insert(node, this.data.height - node.height - 1, true);
+ }
+
+ return this;
+ },
+
+ insert: function (item) {
+ if (item) this._insert(item, this.data.height - 1);
+ return this;
+ },
+
+ clear: function () {
+ this.data = createNode([]);
+ return this;
+ },
+
+ remove: function (item, equalsFn) {
+ if (!item) return this;
+
+ var node = this.data,
+ bbox = this.toBBox(item),
+ path = [],
+ indexes = [],
+ i, parent, index, goingUp;
+
+ // depth-first iterative tree traversal
+ while (node || path.length) {
+
+ if (!node) { // go up
+ node = path.pop();
+ parent = path[path.length - 1];
+ i = indexes.pop();
+ goingUp = true;
+ }
+
+ if (node.leaf) { // check current node
+ index = findItem(item, node.children, equalsFn);
+
+ if (index !== -1) {
+ // item found, remove the item and condense tree upwards
+ node.children.splice(index, 1);
+ path.push(node);
+ this._condense(path);
+ return this;
+ }
+ }
+
+ if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
+ path.push(node);
+ indexes.push(i);
+ i = 0;
+ parent = node;
+ node = node.children[0];
+
+ } else if (parent) { // go right
+ i++;
+ node = parent.children[i];
+ goingUp = false;
+
+ } else node = null; // nothing found
+ }
+
+ return this;
+ },
+
+ toBBox: function (item) { return item; },
+
+ compareMinX: compareNodeMinX,
+ compareMinY: compareNodeMinY,
+
+ toJSON: function () { return this.data; },
+
+ fromJSON: function (data) {
+ this.data = data;
+ return this;
+ },
+
+ _all: function (node, result) {
+ var nodesToSearch = [];
+ while (node) {
+ if (node.leaf) result.push.apply(result, node.children);
+ else nodesToSearch.push.apply(nodesToSearch, node.children);
+
+ node = nodesToSearch.pop();
+ }
+ return result;
+ },
+
+ _build: function (items, left, right, height) {
+
+ var N = right - left + 1,
+ M = this._maxEntries,
+ node;
+
+ if (N <= M) {
+ // reached leaf level; return leaf
+ node = createNode(items.slice(left, right + 1));
+ calcBBox(node, this.toBBox);
+ return node;
+ }
+
+ if (!height) {
+ // target height of the bulk-loaded tree
+ height = Math.ceil(Math.log(N) / Math.log(M));
+
+ // target number of root entries to maximize storage utilization
+ M = Math.ceil(N / Math.pow(M, height - 1));
+ }
+
+ node = createNode([]);
+ node.leaf = false;
+ node.height = height;
+
+ // split the items into M mostly square tiles
+
+ var N2 = Math.ceil(N / M),
+ N1 = N2 * Math.ceil(Math.sqrt(M)),
+ i, j, right2, right3;
+
+ multiSelect(items, left, right, N1, this.compareMinX);
+
+ for (i = left; i <= right; i += N1) {
+
+ right2 = Math.min(i + N1 - 1, right);
+
+ multiSelect(items, i, right2, N2, this.compareMinY);
+
+ for (j = i; j <= right2; j += N2) {
+
+ right3 = Math.min(j + N2 - 1, right2);
+
+ // pack each entry recursively
+ node.children.push(this._build(items, j, right3, height - 1));
+ }
+ }
+
+ calcBBox(node, this.toBBox);
+
+ return node;
+ },
+
+ _chooseSubtree: function (bbox, node, level, path) {
+
+ var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
+
+ while (true) {
+ path.push(node);
+
+ if (node.leaf || path.length - 1 === level) break;
+
+ minArea = minEnlargement = Infinity;
+
+ for (i = 0, len = node.children.length; i < len; i++) {
+ child = node.children[i];
+ area = bboxArea(child);
+ enlargement = enlargedArea(bbox, child) - area;
+
+ // choose entry with the least area enlargement
+ if (enlargement < minEnlargement) {
+ minEnlargement = enlargement;
+ minArea = area < minArea ? area : minArea;
+ targetNode = child;
+
+ } else if (enlargement === minEnlargement) {
+ // otherwise choose one with the smallest area
+ if (area < minArea) {
+ minArea = area;
+ targetNode = child;
+ }
+ }
+ }
+
+ node = targetNode || node.children[0];
+ }
+
+ return node;
+ },
+
+ _insert: function (item, level, isNode) {
+
+ var toBBox = this.toBBox,
+ bbox = isNode ? item : toBBox(item),
+ insertPath = [];
+
+ // find the best node for accommodating the item, saving all nodes along the path too
+ var node = this._chooseSubtree(bbox, this.data, level, insertPath);
+
+ // put the item into the node
+ node.children.push(item);
+ extend(node, bbox);
+
+ // split on node overflow; propagate upwards if necessary
+ while (level >= 0) {
+ if (insertPath[level].children.length > this._maxEntries) {
+ this._split(insertPath, level);
+ level--;
+ } else break;
+ }
+
+ // adjust bboxes along the insertion path
+ this._adjustParentBBoxes(bbox, insertPath, level);
+ },
+
+ // split overflowed node into two
+ _split: function (insertPath, level) {
+
+ var node = insertPath[level],
+ M = node.children.length,
+ m = this._minEntries;
+
+ this._chooseSplitAxis(node, m, M);
+
+ var splitIndex = this._chooseSplitIndex(node, m, M);
+
+ var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
+ newNode.height = node.height;
+ newNode.leaf = node.leaf;
+
+ calcBBox(node, this.toBBox);
+ calcBBox(newNode, this.toBBox);
+
+ if (level) insertPath[level - 1].children.push(newNode);
+ else this._splitRoot(node, newNode);
+ },
+
+ _splitRoot: function (node, newNode) {
+ // split root node
+ this.data = createNode([node, newNode]);
+ this.data.height = node.height + 1;
+ this.data.leaf = false;
+ calcBBox(this.data, this.toBBox);
+ },
+
+ _chooseSplitIndex: function (node, m, M) {
+
+ var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
+
+ minOverlap = minArea = Infinity;
+
+ for (i = m; i <= M - m; i++) {
+ bbox1 = distBBox(node, 0, i, this.toBBox);
+ bbox2 = distBBox(node, i, M, this.toBBox);
+
+ overlap = intersectionArea(bbox1, bbox2);
+ area = bboxArea(bbox1) + bboxArea(bbox2);
+
+ // choose distribution with minimum overlap
+ if (overlap < minOverlap) {
+ minOverlap = overlap;
+ index = i;
+
+ minArea = area < minArea ? area : minArea;
+
+ } else if (overlap === minOverlap) {
+ // otherwise choose distribution with minimum area
+ if (area < minArea) {
+ minArea = area;
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ },
+
+ // sorts node children by the best axis for split
+ _chooseSplitAxis: function (node, m, M) {
+
+ var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
+ compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
+ xMargin = this._allDistMargin(node, m, M, compareMinX),
+ yMargin = this._allDistMargin(node, m, M, compareMinY);
+
+ // if total distributions margin value is minimal for x, sort by minX,
+ // otherwise it's already sorted by minY
+ if (xMargin < yMargin) node.children.sort(compareMinX);
+ },
+
+ // total margin of all possible split distributions where each node is at least m full
+ _allDistMargin: function (node, m, M, compare) {
+
+ node.children.sort(compare);
+
+ var toBBox = this.toBBox,
+ leftBBox = distBBox(node, 0, m, toBBox),
+ rightBBox = distBBox(node, M - m, M, toBBox),
+ margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
+ i, child;
+
+ for (i = m; i < M - m; i++) {
+ child = node.children[i];
+ extend(leftBBox, node.leaf ? toBBox(child) : child);
+ margin += bboxMargin(leftBBox);
+ }
+
+ for (i = M - m - 1; i >= m; i--) {
+ child = node.children[i];
+ extend(rightBBox, node.leaf ? toBBox(child) : child);
+ margin += bboxMargin(rightBBox);
+ }
+
+ return margin;
+ },
+
+ _adjustParentBBoxes: function (bbox, path, level) {
+ // adjust bboxes along the given tree path
+ for (var i = level; i >= 0; i--) {
+ extend(path[i], bbox);
+ }
+ },
+
+ _condense: function (path) {
+ // go through the path, removing empty nodes and updating bboxes
+ for (var i = path.length - 1, siblings; i >= 0; i--) {
+ if (path[i].children.length === 0) {
+ if (i > 0) {
+ siblings = path[i - 1].children;
+ siblings.splice(siblings.indexOf(path[i]), 1);
+
+ } else this.clear();
+
+ } else calcBBox(path[i], this.toBBox);
+ }
+ },
+
+ _initFormat: function (format) {
+ // data format (minX, minY, maxX, maxY accessors)
+
+ // uses eval-type function compilation instead of just accepting a toBBox function
+ // because the algorithms are very sensitive to sorting functions performance,
+ // so they should be dead simple and without inner calls
+
+ var compareArr = ['return a', ' - b', ';'];
+
+ this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
+ this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
+
+ this.toBBox = new Function('a',
+ 'return {minX: a' + format[0] +
+ ', minY: a' + format[1] +
+ ', maxX: a' + format[2] +
+ ', maxY: a' + format[3] + '};');
+ }
+};
+
+function findItem(item, items, equalsFn) {
+ if (!equalsFn) return items.indexOf(item);
+
+ for (var i = 0; i < items.length; i++) {
+ if (equalsFn(item, items[i])) return i;
+ }
+ return -1;
+}
+
+// calculate node's bbox from bboxes of its children
+function calcBBox(node, toBBox) {
+ distBBox(node, 0, node.children.length, toBBox, node);
+}
+
+// min bounding rectangle of node children from k to p-1
+function distBBox(node, k, p, toBBox, destNode) {
+ if (!destNode) destNode = createNode(null);
+ destNode.minX = Infinity;
+ destNode.minY = Infinity;
+ destNode.maxX = -Infinity;
+ destNode.maxY = -Infinity;
+
+ for (var i = k, child; i < p; i++) {
+ child = node.children[i];
+ extend(destNode, node.leaf ? toBBox(child) : child);
+ }
+
+ return destNode;
+}
+
+function extend(a, b) {
+ a.minX = Math.min(a.minX, b.minX);
+ a.minY = Math.min(a.minY, b.minY);
+ a.maxX = Math.max(a.maxX, b.maxX);
+ a.maxY = Math.max(a.maxY, b.maxY);
+ return a;
+}
+
+function compareNodeMinX(a, b) { return a.minX - b.minX; }
+function compareNodeMinY(a, b) { return a.minY - b.minY; }
+
+function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
+function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
+
+function enlargedArea(a, b) {
+ return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
+ (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
+}
+
+function intersectionArea(a, b) {
+ var minX = Math.max(a.minX, b.minX),
+ minY = Math.max(a.minY, b.minY),
+ maxX = Math.min(a.maxX, b.maxX),
+ maxY = Math.min(a.maxY, b.maxY);
+
+ return Math.max(0, maxX - minX) *
+ Math.max(0, maxY - minY);
+}
+
+function contains(a, b) {
+ return a.minX <= b.minX &&
+ a.minY <= b.minY &&
+ b.maxX <= a.maxX &&
+ b.maxY <= a.maxY;
+}
+
+function intersects(a, b) {
+ return b.minX <= a.maxX &&
+ b.minY <= a.maxY &&
+ b.maxX >= a.minX &&
+ b.maxY >= a.minY;
+}
+
+function createNode(children) {
+ return {
+ children: children,
+ height: 1,
+ leaf: true,
+ minX: Infinity,
+ minY: Infinity,
+ maxX: -Infinity,
+ maxY: -Infinity
+ };
+}
+
+// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
+// combines selection algorithm with binary divide & conquer approach
+
+function multiSelect(arr, left, right, n, compare) {
+ var stack = [left, right],
+ mid;
+
+ while (stack.length) {
+ right = stack.pop();
+ left = stack.pop();
+
+ if (right - left <= n) continue;
+
+ mid = left + Math.ceil((right - left) / n / 2) * n;
+ quickselect(arr, mid, left, right, compare);
+
+ stack.push(left, mid, mid, right);
+ }
+}
+
+},{"quickselect":1}]},{},[2])(2)
+});
+ol.ext.rbush = module.exports;
+})();
+
+goog.provide('ol.structs.RBush');
+
+goog.require('ol');
+goog.require('ol.ext.rbush');
+goog.require('ol.extent');
+goog.require('ol.obj');
+
+
+/**
+ * Wrapper around the RBush by Vladimir Agafonkin.
+ *
+ * @constructor
+ * @param {number=} opt_maxEntries Max entries.
+ * @see https://github.com/mourner/rbush
+ * @struct
+ * @template T
+ */
+ol.structs.RBush = function(opt_maxEntries) {
+
+ /**
+ * @private
+ */
+ this.rbush_ = ol.ext.rbush(opt_maxEntries);
+
+ /**
+ * A mapping between the objects added to this rbush wrapper
+ * and the objects that are actually added to the internal rbush.
+ * @private
+ * @type {Object.<number, ol.RBushEntry>}
+ */
+ this.items_ = {};
+
+ if (ol.DEBUG) {
+ /**
+ * @private
+ * @type {number}
+ */
+ this.readers_ = 0;
+ }
+};
+
+
+/**
+ * Insert a value into the RBush.
+ * @param {ol.Extent} extent Extent.
+ * @param {T} value Value.
+ */
+ol.structs.RBush.prototype.insert = function(extent, value) {
+ if (ol.DEBUG && this.readers_) {
+ throw new Error('Can not insert value while reading');
+ }
+ /** @type {ol.RBushEntry} */
+ var item = {
+ minX: extent[0],
+ minY: extent[1],
+ maxX: extent[2],
+ maxY: extent[3],
+ value: value
+ };
+
+ this.rbush_.insert(item);
+ // remember the object that was added to the internal rbush
+ ol.DEBUG && console.assert(!(ol.getUid(value) in this.items_),
+ 'uid (%s) of value (%s) already exists', ol.getUid(value), value);
+ this.items_[ol.getUid(value)] = item;
+};
+
+
+/**
+ * Bulk-insert values into the RBush.
+ * @param {Array.<ol.Extent>} extents Extents.
+ * @param {Array.<T>} values Values.
+ */
+ol.structs.RBush.prototype.load = function(extents, values) {
+ if (ol.DEBUG && this.readers_) {
+ throw new Error('Can not insert values while reading');
+ }
+ ol.DEBUG && console.assert(extents.length === values.length,
+ 'extens and values must have same length (%s === %s)',
+ extents.length, values.length);
+
+ var items = new Array(values.length);
+ for (var i = 0, l = values.length; i < l; i++) {
+ var extent = extents[i];
+ var value = values[i];
+
+ /** @type {ol.RBushEntry} */
+ var item = {
+ minX: extent[0],
+ minY: extent[1],
+ maxX: extent[2],
+ maxY: extent[3],
+ value: value
+ };
+ items[i] = item;
+ ol.DEBUG && console.assert(!(ol.getUid(value) in this.items_),
+ 'uid (%s) of value (%s) already exists', ol.getUid(value), value);
+ this.items_[ol.getUid(value)] = item;
+ }
+ this.rbush_.load(items);
+};
+
+
+/**
+ * Remove a value from the RBush.
+ * @param {T} value Value.
+ * @return {boolean} Removed.
+ */
+ol.structs.RBush.prototype.remove = function(value) {
+ if (ol.DEBUG && this.readers_) {
+ throw new Error('Can not remove value while reading');
+ }
+ var uid = ol.getUid(value);
+ ol.DEBUG && console.assert(uid in this.items_,
+ 'uid (%s) of value (%s) does not exist', uid, value);
+
+ // get the object in which the value was wrapped when adding to the
+ // internal rbush. then use that object to do the removal.
+ var item = this.items_[uid];
+ delete this.items_[uid];
+ return this.rbush_.remove(item) !== null;
+};
+
+
+/**
+ * Update the extent of a value in the RBush.
+ * @param {ol.Extent} extent Extent.
+ * @param {T} value Value.
+ */
+ol.structs.RBush.prototype.update = function(extent, value) {
+ ol.DEBUG && console.assert(ol.getUid(value) in this.items_,
+ 'uid (%s) of value (%s) does not exist', ol.getUid(value), value);
+
+ var item = this.items_[ol.getUid(value)];
+ var bbox = [item.minX, item.minY, item.maxX, item.maxY];
+ if (!ol.extent.equals(bbox, extent)) {
+ if (ol.DEBUG && this.readers_) {
+ throw new Error('Can not update extent while reading');
+ }
+ this.remove(value);
+ this.insert(extent, value);
+ }
+};
+
+
+/**
+ * Return all values in the RBush.
+ * @return {Array.<T>} All.
+ */
+ol.structs.RBush.prototype.getAll = function() {
+ var items = this.rbush_.all();
+ return items.map(function(item) {
+ return item.value;
+ });
+};
+
+
+/**
+ * Return all values in the given extent.
+ * @param {ol.Extent} extent Extent.
+ * @return {Array.<T>} All in extent.
+ */
+ol.structs.RBush.prototype.getInExtent = function(extent) {
+ /** @type {ol.RBushEntry} */
+ var bbox = {
+ minX: extent[0],
+ minY: extent[1],
+ maxX: extent[2],
+ maxY: extent[3]
+ };
+ var items = this.rbush_.search(bbox);
+ return items.map(function(item) {
+ return item.value;
+ });
+};
+
+
+/**
+ * Calls a callback function with each value in the tree.
+ * If the callback returns a truthy value, this value is returned without
+ * checking the rest of the tree.
+ * @param {function(this: S, T): *} callback Callback.
+ * @param {S=} opt_this The object to use as `this` in `callback`.
+ * @return {*} Callback return value.
+ * @template S
+ */
+ol.structs.RBush.prototype.forEach = function(callback, opt_this) {
+ if (ol.DEBUG) {
+ ++this.readers_;
+ try {
+ return this.forEach_(this.getAll(), callback, opt_this);
+ } finally {
+ --this.readers_;
+ }
+ } else {
+ return this.forEach_(this.getAll(), callback, opt_this);
+ }
+};
+
+
+/**
+ * Calls a callback function with each value in the provided extent.
+ * @param {ol.Extent} extent Extent.
+ * @param {function(this: S, T): *} callback Callback.
+ * @param {S=} opt_this The object to use as `this` in `callback`.
+ * @return {*} Callback return value.
+ * @template S
+ */
+ol.structs.RBush.prototype.forEachInExtent = function(extent, callback, opt_this) {
+ if (ol.DEBUG) {
+ ++this.readers_;
+ try {
+ return this.forEach_(this.getInExtent(extent), callback, opt_this);
+ } finally {
+ --this.readers_;
+ }
+ } else {
+ return this.forEach_(this.getInExtent(extent), callback, opt_this);
+ }
+};
+
+
+/**
+ * @param {Array.<T>} values Values.
+ * @param {function(this: S, T): *} callback Callback.
+ * @param {S=} opt_this The object to use as `this` in `callback`.
+ * @private
+ * @return {*} Callback return value.
+ * @template S
+ */
+ol.structs.RBush.prototype.forEach_ = function(values, callback, opt_this) {
+ var result;
+ for (var i = 0, l = values.length; i < l; i++) {
+ result = callback.call(opt_this, values[i]);
+ if (result) {
+ return result;
+ }
+ }
+ return result;
+};
+
+
+/**
+ * @return {boolean} Is empty.
+ */
+ol.structs.RBush.prototype.isEmpty = function() {
+ return ol.obj.isEmpty(this.items_);
+};
+
+
+/**
+ * Remove all values from the RBush.
+ */
+ol.structs.RBush.prototype.clear = function() {
+ this.rbush_.clear();
+ this.items_ = {};
+};
+
+
+/**
+ * @param {ol.Extent=} opt_extent Extent.
+ * @return {!ol.Extent} Extent.
+ */
+ol.structs.RBush.prototype.getExtent = function(opt_extent) {
+ // FIXME add getExtent() to rbush
+ var data = this.rbush_.data;
+ return [data.minX, data.minY, data.maxX, data.maxY];
+};
+
+goog.provide('ol.render.webgl.PolygonReplay');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.color');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.geom.flat.contains');
+goog.require('ol.geom.flat.orient');
+goog.require('ol.geom.flat.transform');
+goog.require('ol.render.webgl.polygonreplay.defaultshader');
+goog.require('ol.render.webgl.LineStringReplay');
+goog.require('ol.render.webgl.Replay');
+goog.require('ol.render.webgl');
+goog.require('ol.style.Stroke');
+goog.require('ol.structs.LinkedList');
+goog.require('ol.structs.RBush');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Buffer');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.webgl.Replay}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @struct
+ */
+ol.render.webgl.PolygonReplay = function(tolerance, maxExtent) {
+ ol.render.webgl.Replay.call(this, tolerance, maxExtent);
+
+ this.lineStringReplay = new ol.render.webgl.LineStringReplay(
+ tolerance, maxExtent);
+
+ /**
+ * @private
+ * @type {ol.render.webgl.polygonreplay.defaultshader.Locations}
+ */
+ this.defaultLocations_ = null;
+
+ /**
+ * @private
+ * @type {Array.<Array.<number>>}
+ */
+ this.styles_ = [];
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.styleIndices_ = [];
+
+ /**
+ * @private
+ * @type {{fillColor: (Array.<number>|null),
+ * changed: boolean}|null}
+ */
+ this.state_ = {
+ fillColor: null,
+ changed: false
+ };
+
+};
+ol.inherits(ol.render.webgl.PolygonReplay, ol.render.webgl.Replay);
+
+
+/**
+ * Draw one polygon.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {Array.<Array.<number>>} holeFlatCoordinates Hole flat coordinates.
+ * @param {number} stride Stride.
+ * @private
+ */
+ol.render.webgl.PolygonReplay.prototype.drawCoordinates_ = function(
+ flatCoordinates, holeFlatCoordinates, stride) {
+ // Triangulate the polygon
+ var outerRing = new ol.structs.LinkedList();
+ var rtree = new ol.structs.RBush();
+ // Initialize the outer ring
+ var maxX = this.processFlatCoordinates_(flatCoordinates, stride, outerRing, rtree, true);
+
+ // Eliminate holes, if there are any
+ if (holeFlatCoordinates.length) {
+ var i, ii;
+ var holeLists = [];
+ for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) {
+ var holeList = {
+ list: new ol.structs.LinkedList(),
+ maxX: undefined
+ };
+ holeLists.push(holeList);
+ holeList.maxX = this.processFlatCoordinates_(holeFlatCoordinates[i],
+ stride, holeList.list, rtree, false);
+ }
+ holeLists.sort(function(a, b) {
+ return b.maxX - a.maxX;
+ });
+ for (i = 0; i < holeLists.length; ++i) {
+ this.bridgeHole_(holeLists[i].list, holeLists[i].maxX, outerRing, maxX, rtree);
+ }
+ }
+ this.classifyPoints_(outerRing, rtree, false);
+ this.triangulate_(outerRing, rtree);
+};
+
+
+/**
+ * Inserts flat coordinates in a linked list and adds them to the vertex buffer.
+ * @private
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} stride Stride.
+ * @param {ol.structs.LinkedList} list Linked list.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @param {boolean} clockwise Coordinate order should be clockwise.
+ * @return {number} Maximum X value.
+ */
+ol.render.webgl.PolygonReplay.prototype.processFlatCoordinates_ = function(
+ flatCoordinates, stride, list, rtree, clockwise) {
+ var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates,
+ 0, flatCoordinates.length, stride);
+ var i, ii, maxX;
+ var n = this.vertices.length / 2;
+ /** @type {ol.WebglPolygonVertex} */
+ var start;
+ /** @type {ol.WebglPolygonVertex} */
+ var p0;
+ /** @type {ol.WebglPolygonVertex} */
+ var p1;
+ var extents = [];
+ var segments = [];
+ if (clockwise === isClockwise) {
+ start = this.createPoint_(flatCoordinates[0], flatCoordinates[1], n++);
+ p0 = start;
+ maxX = flatCoordinates[0];
+ for (i = stride, ii = flatCoordinates.length; i < ii; i += stride) {
+ p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++);
+ segments.push(this.insertItem_(p0, p1, list));
+ extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x),
+ Math.max(p0.y, p1.y)]);
+ maxX = flatCoordinates[i] > maxX ? flatCoordinates[i] : maxX;
+ p0 = p1;
+ }
+ segments.push(this.insertItem_(p1, start, list));
+ extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x),
+ Math.max(p0.y, p1.y)]);
+ } else {
+ var end = flatCoordinates.length - stride;
+ start = this.createPoint_(flatCoordinates[end], flatCoordinates[end + 1], n++);
+ p0 = start;
+ maxX = flatCoordinates[end];
+ for (i = end - stride, ii = 0; i >= ii; i -= stride) {
+ p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++);
+ segments.push(this.insertItem_(p0, p1, list));
+ extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x),
+ Math.max(p0.y, p1.y)]);
+ maxX = flatCoordinates[i] > maxX ? flatCoordinates[i] : maxX;
+ p0 = p1;
+ }
+ segments.push(this.insertItem_(p1, start, list));
+ extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x),
+ Math.max(p0.y, p1.y)]);
+ }
+ rtree.load(extents, segments);
+
+ return maxX;
+};
+
+
+/**
+ * Classifies the points of a polygon list as convex, reflex. Removes collinear vertices.
+ * @private
+ * @param {ol.structs.LinkedList} list Polygon ring.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @param {boolean} ccw The orientation of the polygon is counter-clockwise.
+ * @return {boolean} There were reclassified points.
+ */
+ol.render.webgl.PolygonReplay.prototype.classifyPoints_ = function(list, rtree, ccw) {
+ var start = list.firstItem();
+ var s0 = start;
+ var s1 = list.nextItem();
+ var pointsReclassified = false;
+ do {
+ var reflex = ccw ? ol.render.webgl.triangleIsCounterClockwise(s1.p1.x,
+ s1.p1.y, s0.p1.x, s0.p1.y, s0.p0.x, s0.p0.y) :
+ ol.render.webgl.triangleIsCounterClockwise(s0.p0.x, s0.p0.y, s0.p1.x,
+ s0.p1.y, s1.p1.x, s1.p1.y);
+ if (reflex === undefined) {
+ this.removeItem_(s0, s1, list, rtree);
+ pointsReclassified = true;
+ if (s1 === start) {
+ start = list.getNextItem();
+ }
+ s1 = s0;
+ list.prevItem();
+ } else if (s0.p1.reflex !== reflex) {
+ s0.p1.reflex = reflex;
+ pointsReclassified = true;
+ }
+ s0 = s1;
+ s1 = list.nextItem();
+ } while (s0 !== start);
+ return pointsReclassified;
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} hole Linked list of the hole.
+ * @param {number} holeMaxX Maximum X value of the hole.
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @param {number} listMaxX Maximum X value of the polygon.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ */
+ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX,
+ list, listMaxX, rtree) {
+ this.classifyPoints_(hole, rtree, true);
+ var seg = hole.firstItem();
+ while (seg.p1.x !== holeMaxX) {
+ seg = hole.nextItem();
+ }
+
+ var p1 = seg.p1;
+ /** @type {ol.WebglPolygonVertex} */
+ var p2 = {x: listMaxX, y: p1.y, i: -1};
+ var minDist = Infinity;
+ var i, ii, bestPoint;
+ /** @type {ol.WebglPolygonVertex} */
+ var p5;
+
+ var intersectingSegments = this.getIntersections_({p0: p1, p1: p2}, rtree, true);
+ for (i = 0, ii = intersectingSegments.length; i < ii; ++i) {
+ var currSeg = intersectingSegments[i];
+ if (currSeg.p0.reflex === undefined) {
+ var intersection = this.calculateIntersection_(p1, p2, currSeg.p0,
+ currSeg.p1, true);
+ var dist = Math.abs(p1.x - intersection[0]);
+ if (dist < minDist) {
+ minDist = dist;
+ p5 = {x: intersection[0], y: intersection[1], i: -1};
+ seg = currSeg;
+ }
+ }
+ }
+ if (minDist === Infinity) {
+ return;
+ }
+ bestPoint = seg.p1;
+
+ if (minDist > 0) {
+ var pointsInTriangle = this.getPointsInTriangle_(p1, p5, seg.p1, rtree);
+ if (pointsInTriangle.length) {
+ var theta = Infinity;
+ for (i = 0, ii = pointsInTriangle.length; i < ii; ++i) {
+ var currPoint = pointsInTriangle[i];
+ var currTheta = Math.atan2(p1.y - currPoint.y, p2.x - currPoint.x);
+ if (currTheta < theta || (currTheta === theta && currPoint.x < bestPoint.x)) {
+ theta = currTheta;
+ bestPoint = currPoint;
+ }
+ }
+ }
+ }
+
+ seg = list.firstItem();
+ while (seg.p1 !== bestPoint) {
+ seg = list.nextItem();
+ }
+
+ //We clone the bridge points as they can have different convexity.
+ var p0Bridge = {x: p1.x, y: p1.y, i: p1.i, reflex: undefined};
+ var p1Bridge = {x: seg.p1.x, y: seg.p1.y, i: seg.p1.i, reflex: undefined};
+
+ hole.getNextItem().p0 = p0Bridge;
+ this.insertItem_(p1, seg.p1, hole, rtree);
+ this.insertItem_(p1Bridge, p0Bridge, hole, rtree);
+ seg.p1 = p1Bridge;
+ hole.setFirstItem();
+ list.concat(hole);
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ */
+ol.render.webgl.PolygonReplay.prototype.triangulate_ = function(list, rtree) {
+ var ccw = false;
+ var simple = this.isSimple_(list, rtree);
+
+ // Start clipping ears
+ while (list.getLength() > 3) {
+ if (simple) {
+ if (!this.clipEars_(list, rtree, simple, ccw)) {
+ if (!this.classifyPoints_(list, rtree, ccw)) {
+ // Due to the behavior of OL's PIP algorithm, the ear clipping cannot
+ // introduce touching segments. However, the original data may have some.
+ if (!this.resolveLocalSelfIntersections_(list, rtree, true)) {
+ // Something went wrong.
+ ol.DEBUG && console.assert(false, 'Unexpected simple polygon geometry');
+ break;
+ }
+ }
+ }
+ } else {
+ if (!this.clipEars_(list, rtree, simple, ccw)) {
+ // We ran out of ears, try to reclassify.
+ if (!this.classifyPoints_(list, rtree, ccw)) {
+ // We have a bad polygon, try to resolve local self-intersections.
+ if (!this.resolveLocalSelfIntersections_(list, rtree)) {
+ simple = this.isSimple_(list, rtree);
+ if (!simple) {
+ // We have a really bad polygon, try more time consuming methods.
+ this.splitPolygon_(list, rtree);
+ break;
+ } else {
+ ccw = !this.isClockwise_(list);
+ this.classifyPoints_(list, rtree, ccw);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (list.getLength() === 3) {
+ var numIndices = this.indices.length;
+ this.indices[numIndices++] = list.getPrevItem().p0.i;
+ this.indices[numIndices++] = list.getCurrItem().p0.i;
+ this.indices[numIndices++] = list.getNextItem().p0.i;
+ }
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @param {boolean} simple The polygon is simple.
+ * @param {boolean} ccw Orientation of the polygon is counter-clockwise.
+ * @return {boolean} There were processed ears.
+ */
+ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, rtree, simple, ccw) {
+ var numIndices = this.indices.length;
+ var start = list.firstItem();
+ var s0 = list.getPrevItem();
+ var s1 = start;
+ var s2 = list.nextItem();
+ var s3 = list.getNextItem();
+ var p0, p1, p2;
+ var processedEars = false;
+ do {
+ p0 = s1.p0;
+ p1 = s1.p1;
+ p2 = s2.p1;
+ if (p1.reflex === false) {
+ // We might have a valid ear
+ var diagonalIsInside = ccw ? this.diagonalIsInside_(s3.p1, p2, p1, p0,
+ s0.p0) : this.diagonalIsInside_(s0.p0, p0, p1, p2, s3.p1);
+ if ((simple || this.getIntersections_({p0: p0, p1: p2}, rtree).length === 0) &&
+ diagonalIsInside && this.getPointsInTriangle_(p0, p1, p2, rtree, true).length === 0) {
+ //The diagonal is completely inside the polygon
+ if (simple || p0.reflex === false || p2.reflex === false ||
+ ol.geom.flat.orient.linearRingIsClockwise([s0.p0.x, s0.p0.y, p0.x,
+ p0.y, p1.x, p1.y, p2.x, p2.y, s3.p1.x, s3.p1.y], 0, 10, 2) === !ccw) {
+ //The diagonal is persumably valid, we have an ear
+ this.indices[numIndices++] = p0.i;
+ this.indices[numIndices++] = p1.i;
+ this.indices[numIndices++] = p2.i;
+ this.removeItem_(s1, s2, list, rtree);
+ if (s2 === start) {
+ start = s3;
+ }
+ processedEars = true;
+ }
+ }
+ }
+ // Else we have a reflex point.
+ s0 = list.getPrevItem();
+ s1 = list.getCurrItem();
+ s2 = list.nextItem();
+ s3 = list.getNextItem();
+ } while (s1 !== start && list.getLength() > 3);
+
+ return processedEars;
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @param {boolean=} opt_touch Resolve touching segments.
+ * @return {boolean} There were resolved intersections.
+*/
+ol.render.webgl.PolygonReplay.prototype.resolveLocalSelfIntersections_ = function(
+ list, rtree, opt_touch) {
+ var start = list.firstItem();
+ list.nextItem();
+ var s0 = start;
+ var s1 = list.nextItem();
+ var resolvedIntersections = false;
+
+ do {
+ var intersection = this.calculateIntersection_(s0.p0, s0.p1, s1.p0, s1.p1,
+ opt_touch);
+ if (intersection) {
+ var breakCond = false;
+ var numVertices = this.vertices.length;
+ var numIndices = this.indices.length;
+ var n = numVertices / 2;
+ var seg = list.prevItem();
+ list.removeItem();
+ rtree.remove(seg);
+ breakCond = (seg === start);
+ var p;
+ if (opt_touch) {
+ if (intersection[0] === s0.p0.x && intersection[1] === s0.p0.y) {
+ list.prevItem();
+ p = s0.p0;
+ s1.p0 = p;
+ rtree.remove(s0);
+ breakCond = breakCond || (s0 === start);
+ } else {
+ p = s1.p1;
+ s0.p1 = p;
+ rtree.remove(s1);
+ breakCond = breakCond || (s1 === start);
+ }
+ list.removeItem();
+ } else {
+ p = this.createPoint_(intersection[0], intersection[1], n);
+ s0.p1 = p;
+ s1.p0 = p;
+ rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y),
+ Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0);
+ rtree.update([Math.min(s1.p0.x, s1.p1.x), Math.min(s1.p0.y, s1.p1.y),
+ Math.max(s1.p0.x, s1.p1.x), Math.max(s1.p0.y, s1.p1.y)], s1);
+ }
+
+ this.indices[numIndices++] = seg.p0.i;
+ this.indices[numIndices++] = seg.p1.i;
+ this.indices[numIndices++] = p.i;
+
+ resolvedIntersections = true;
+ if (breakCond) {
+ break;
+ }
+ }
+
+ s0 = list.getPrevItem();
+ s1 = list.nextItem();
+ } while (s0 !== start);
+ return resolvedIntersections;
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @return {boolean} The polygon is simple.
+ */
+ol.render.webgl.PolygonReplay.prototype.isSimple_ = function(list, rtree) {
+ var start = list.firstItem();
+ var seg = start;
+ do {
+ if (this.getIntersections_(seg, rtree).length) {
+ return false;
+ }
+ seg = list.nextItem();
+ } while (seg !== start);
+ return true;
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @return {boolean} Orientation is clockwise.
+ */
+ol.render.webgl.PolygonReplay.prototype.isClockwise_ = function(list) {
+ var length = list.getLength() * 2;
+ var flatCoordinates = new Array(length);
+ var start = list.firstItem();
+ var seg = start;
+ var i = 0;
+ do {
+ flatCoordinates[i++] = seg.p0.x;
+ flatCoordinates[i++] = seg.p0.y;
+ seg = list.nextItem();
+ } while (seg !== start);
+ return ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates, 0, length, 2);
+};
+
+
+/**
+ * @private
+ * @param {ol.structs.LinkedList} list Linked list of the polygon.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ */
+ol.render.webgl.PolygonReplay.prototype.splitPolygon_ = function(list, rtree) {
+ var start = list.firstItem();
+ var s0 = start;
+ do {
+ var intersections = this.getIntersections_(s0, rtree);
+ if (intersections.length) {
+ var s1 = intersections[0];
+ var n = this.vertices.length / 2;
+ var intersection = this.calculateIntersection_(s0.p0,
+ s0.p1, s1.p0, s1.p1);
+ var p = this.createPoint_(intersection[0], intersection[1], n);
+ var newPolygon = new ol.structs.LinkedList();
+ var newRtree = new ol.structs.RBush();
+ this.insertItem_(p, s0.p1, newPolygon, newRtree);
+ s0.p1 = p;
+ rtree.update([Math.min(s0.p0.x, p.x), Math.min(s0.p0.y, p.y),
+ Math.max(s0.p0.x, p.x), Math.max(s0.p0.y, p.y)], s0);
+ var currItem = list.nextItem();
+ while (currItem !== s1) {
+ this.insertItem_(currItem.p0, currItem.p1, newPolygon, newRtree);
+ rtree.remove(currItem);
+ list.removeItem();
+ currItem = list.getCurrItem();
+ }
+ this.insertItem_(s1.p0, p, newPolygon, newRtree);
+ s1.p0 = p;
+ rtree.update([Math.min(s1.p1.x, p.x), Math.min(s1.p1.y, p.y),
+ Math.max(s1.p1.x, p.x), Math.max(s1.p1.y, p.y)], s1);
+ this.classifyPoints_(list, rtree, false);
+ this.triangulate_(list, rtree);
+ this.classifyPoints_(newPolygon, newRtree, false);
+ this.triangulate_(newPolygon, newRtree);
+ break;
+ }
+ s0 = list.nextItem();
+ } while (s0 !== start);
+};
+
+
+/**
+ * @private
+ * @param {number} x X coordinate.
+ * @param {number} y Y coordinate.
+ * @param {number} i Index.
+ * @return {ol.WebglPolygonVertex} List item.
+ */
+ol.render.webgl.PolygonReplay.prototype.createPoint_ = function(x, y, i) {
+ var numVertices = this.vertices.length;
+ this.vertices[numVertices++] = x;
+ this.vertices[numVertices++] = y;
+ /** @type {ol.WebglPolygonVertex} */
+ var p = {
+ x: x,
+ y: y,
+ i: i,
+ reflex: undefined
+ };
+ return p;
+};
+
+
+/**
+ * @private
+ * @param {ol.WebglPolygonVertex} p0 First point of segment.
+ * @param {ol.WebglPolygonVertex} p1 Second point of segment.
+ * @param {ol.structs.LinkedList} list Polygon ring.
+ * @param {ol.structs.RBush=} opt_rtree Insert the segment into the R-Tree.
+ * @return {ol.WebglPolygonSegment} segment.
+ */
+ol.render.webgl.PolygonReplay.prototype.insertItem_ = function(p0, p1, list, opt_rtree) {
+ var seg = {
+ p0: p0,
+ p1: p1
+ };
+ list.insertItem(seg);
+ if (opt_rtree) {
+ opt_rtree.insert([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y),
+ Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)], seg);
+ }
+ return seg;
+};
+
+
+ /**
+ * @private
+ * @param {ol.WebglPolygonSegment} s0 Segment before the remove candidate.
+ * @param {ol.WebglPolygonSegment} s1 Remove candidate segment.
+ * @param {ol.structs.LinkedList} list Polygon ring.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ */
+ol.render.webgl.PolygonReplay.prototype.removeItem_ = function(s0, s1, list, rtree) {
+ if (list.getCurrItem() === s1) {
+ list.removeItem();
+ s0.p1 = s1.p1;
+ rtree.remove(s1);
+ rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y),
+ Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0);
+ }
+};
+
+
+/**
+ * @private
+ * @param {ol.WebglPolygonVertex} p0 First point.
+ * @param {ol.WebglPolygonVertex} p1 Second point.
+ * @param {ol.WebglPolygonVertex} p2 Third point.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @param {boolean=} opt_reflex Only include reflex points.
+ * @return {Array.<ol.WebglPolygonVertex>} Points in the triangle.
+ */
+ol.render.webgl.PolygonReplay.prototype.getPointsInTriangle_ = function(p0, p1,
+ p2, rtree, opt_reflex) {
+ var i, ii, j, p;
+ var result = [];
+ var segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x, p2.x),
+ Math.min(p0.y, p1.y, p2.y), Math.max(p0.x, p1.x, p2.x), Math.max(p0.y,
+ p1.y, p2.y)]);
+ for (i = 0, ii = segmentsInExtent.length; i < ii; ++i) {
+ for (j in segmentsInExtent[i]) {
+ p = segmentsInExtent[i][j];
+ if (typeof p === 'object' && (!opt_reflex || p.reflex)) {
+ if ((p.x !== p0.x || p.y !== p0.y) && (p.x !== p1.x || p.y !== p1.y) &&
+ (p.x !== p2.x || p.y !== p2.y) && result.indexOf(p) === -1 &&
+ ol.geom.flat.contains.linearRingContainsXY([p0.x, p0.y, p1.x, p1.y,
+ p2.x, p2.y], 0, 6, 2, p.x, p.y)) {
+ result.push(p);
+ }
+ }
+ }
+ }
+ return result;
+};
+
+
+/**
+ * @private
+ * @param {ol.WebglPolygonSegment} segment Segment.
+ * @param {ol.structs.RBush} rtree R-Tree of the polygon.
+ * @param {boolean=} opt_touch Touching segments should be considered an intersection.
+ * @return {Array.<ol.WebglPolygonSegment>} Intersecting segments.
+ */
+ol.render.webgl.PolygonReplay.prototype.getIntersections_ = function(segment, rtree, opt_touch) {
+ var p0 = segment.p0;
+ var p1 = segment.p1;
+ var segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x),
+ Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)]);
+ var result = [];
+ var i, ii;
+ for (i = 0, ii = segmentsInExtent.length; i < ii; ++i) {
+ var currSeg = segmentsInExtent[i];
+ if (segment !== currSeg && (opt_touch || currSeg.p0 !== p1 || currSeg.p1 !== p0) &&
+ this.calculateIntersection_(p0, p1, currSeg.p0, currSeg.p1, opt_touch)) {
+ result.push(currSeg);
+ }
+ }
+ return result;
+};
+
+
+/**
+ * Line intersection algorithm by Paul Bourke.
+ * @see http://paulbourke.net/geometry/pointlineplane/
+ *
+ * @private
+ * @param {ol.WebglPolygonVertex} p0 First point.
+ * @param {ol.WebglPolygonVertex} p1 Second point.
+ * @param {ol.WebglPolygonVertex} p2 Third point.
+ * @param {ol.WebglPolygonVertex} p3 Fourth point.
+ * @param {boolean=} opt_touch Touching segments should be considered an intersection.
+ * @return {Array.<number>|undefined} Intersection coordinates.
+ */
+ol.render.webgl.PolygonReplay.prototype.calculateIntersection_ = function(p0,
+ p1, p2, p3, opt_touch) {
+ var denom = (p3.y - p2.y) * (p1.x - p0.x) - (p3.x - p2.x) * (p1.y - p0.y);
+ if (denom !== 0) {
+ var ua = ((p3.x - p2.x) * (p0.y - p2.y) - (p3.y - p2.y) * (p0.x - p2.x)) / denom;
+ var ub = ((p1.x - p0.x) * (p0.y - p2.y) - (p1.y - p0.y) * (p0.x - p2.x)) / denom;
+ if ((!opt_touch && ua > ol.render.webgl.EPSILON && ua < 1 - ol.render.webgl.EPSILON &&
+ ub > ol.render.webgl.EPSILON && ub < 1 - ol.render.webgl.EPSILON) || (opt_touch &&
+ ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)) {
+ return [p0.x + ua * (p1.x - p0.x), p0.y + ua * (p1.y - p0.y)];
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {ol.WebglPolygonVertex} p0 Point before the start of the diagonal.
+ * @param {ol.WebglPolygonVertex} p1 Start point of the diagonal.
+ * @param {ol.WebglPolygonVertex} p2 Ear candidate.
+ * @param {ol.WebglPolygonVertex} p3 End point of the diagonal.
+ * @param {ol.WebglPolygonVertex} p4 Point after the end of the diagonal.
+ * @return {boolean} Diagonal is inside the polygon.
+ */
+ol.render.webgl.PolygonReplay.prototype.diagonalIsInside_ = function(p0, p1, p2, p3, p4) {
+ if (p1.reflex === undefined || p3.reflex === undefined) {
+ return false;
+ }
+ var p1IsLeftOf = (p2.x - p3.x) * (p1.y - p3.y) > (p2.y - p3.y) * (p1.x - p3.x);
+ var p1IsRightOf = (p4.x - p3.x) * (p1.y - p3.y) < (p4.y - p3.y) * (p1.x - p3.x);
+ var p3IsLeftOf = (p0.x - p1.x) * (p3.y - p1.y) > (p0.y - p1.y) * (p3.x - p1.x);
+ var p3IsRightOf = (p2.x - p1.x) * (p3.y - p1.y) < (p2.y - p1.y) * (p3.x - p1.x);
+ var p1InCone = p3.reflex ? p1IsRightOf || p1IsLeftOf : p1IsRightOf && p1IsLeftOf;
+ var p3InCone = p1.reflex ? p3IsRightOf || p3IsLeftOf : p3IsRightOf && p3IsLeftOf;
+ return p1InCone && p3InCone;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {
+ var polygons = multiPolygonGeometry.getPolygons();
+ var stride = multiPolygonGeometry.getStride();
+ var currIndex = this.indices.length;
+ var currLineIndex = this.lineStringReplay.getCurrentIndex();
+ var i, ii, j, jj;
+ for (i = 0, ii = polygons.length; i < ii; ++i) {
+ var linearRings = polygons[i].getLinearRings();
+ if (linearRings.length > 0) {
+ var flatCoordinates = linearRings[0].getFlatCoordinates();
+ flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length,
+ stride, -this.origin[0], -this.origin[1]);
+ var holes = [];
+ var holeFlatCoords;
+ for (j = 1, jj = linearRings.length; j < jj; ++j) {
+ holeFlatCoords = linearRings[j].getFlatCoordinates();
+ holeFlatCoords = ol.geom.flat.transform.translate(holeFlatCoords, 0, holeFlatCoords.length,
+ stride, -this.origin[0], -this.origin[1]);
+ holes.push(holeFlatCoords);
+ }
+ this.lineStringReplay.drawPolygonCoordinates(flatCoordinates, holes, stride);
+ this.drawCoordinates_(flatCoordinates, holes, stride);
+ }
+ }
+ if (this.indices.length > currIndex) {
+ this.startIndices.push(currIndex);
+ this.startIndicesFeature.push(feature);
+ if (this.state_.changed) {
+ this.styleIndices_.push(currIndex);
+ this.state_.changed = false;
+ }
+ }
+ if (this.lineStringReplay.getCurrentIndex() > currLineIndex) {
+ this.lineStringReplay.setPolygonStyle(feature, currLineIndex);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) {
+ var linearRings = polygonGeometry.getLinearRings();
+ var stride = polygonGeometry.getStride();
+ if (linearRings.length > 0) {
+ this.startIndices.push(this.indices.length);
+ this.startIndicesFeature.push(feature);
+ if (this.state_.changed) {
+ this.styleIndices_.push(this.indices.length);
+ this.state_.changed = false;
+ }
+ this.lineStringReplay.setPolygonStyle(feature);
+
+ var flatCoordinates = linearRings[0].getFlatCoordinates();
+ flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length,
+ stride, -this.origin[0], -this.origin[1]);
+ var holes = [];
+ var i, ii, holeFlatCoords;
+ for (i = 1, ii = linearRings.length; i < ii; ++i) {
+ holeFlatCoords = linearRings[i].getFlatCoordinates();
+ holeFlatCoords = ol.geom.flat.transform.translate(holeFlatCoords, 0, holeFlatCoords.length,
+ stride, -this.origin[0], -this.origin[1]);
+ holes.push(holeFlatCoords);
+ }
+ this.lineStringReplay.drawPolygonCoordinates(flatCoordinates, holes, stride);
+ this.drawCoordinates_(flatCoordinates, holes, stride);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ **/
+ol.render.webgl.PolygonReplay.prototype.finish = function(context) {
+ // create, bind, and populate the vertices buffer
+ this.verticesBuffer = new ol.webgl.Buffer(this.vertices);
+
+ // create, bind, and populate the indices buffer
+ this.indicesBuffer = new ol.webgl.Buffer(this.indices);
+
+ this.startIndices.push(this.indices.length);
+
+ this.lineStringReplay.finish(context);
+
+ //Clean up, if there is nothing to draw
+ if (this.styleIndices_.length === 0 && this.styles_.length > 0) {
+ this.styles_ = [];
+ }
+
+ this.vertices = null;
+ this.indices = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.getDeleteResourcesFunction = function(context) {
+ // We only delete our stuff here. The shaders and the program may
+ // be used by other PolygonReplay instances (for other layers). And
+ // they will be deleted when disposing of the ol.webgl.Context
+ // object.
+ ol.DEBUG && console.assert(this.verticesBuffer,
+ 'verticesBuffer must not be null');
+ ol.DEBUG && console.assert(this.indicesBuffer,
+ 'indicesBuffer must not be null');
+ var verticesBuffer = this.verticesBuffer;
+ var indicesBuffer = this.indicesBuffer;
+ var lineDeleter = this.lineStringReplay.getDeleteResourcesFunction(context);
+ return function() {
+ context.deleteBuffer(verticesBuffer);
+ context.deleteBuffer(indicesBuffer);
+ lineDeleter();
+ };
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {
+ // get the program
+ var fragmentShader, vertexShader;
+ fragmentShader = ol.render.webgl.polygonreplay.defaultshader.fragment;
+ vertexShader = ol.render.webgl.polygonreplay.defaultshader.vertex;
+ var program = context.getProgram(fragmentShader, vertexShader);
+
+ // get the locations
+ var locations;
+ if (!this.defaultLocations_) {
+ locations =
+ new ol.render.webgl.polygonreplay.defaultshader.Locations(gl, program);
+ this.defaultLocations_ = locations;
+ } else {
+ locations = this.defaultLocations_;
+ }
+
+ context.useProgram(program);
+
+ // enable the vertex attrib arrays
+ gl.enableVertexAttribArray(locations.a_position);
+ gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT,
+ false, 8, 0);
+
+ return locations;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.shutDownProgram = function(gl, locations) {
+ gl.disableVertexAttribArray(locations.a_position);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {
+ //Save GL parameters.
+ var tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC));
+ var tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK));
+
+ if (!hitDetection) {
+ gl.enable(gl.DEPTH_TEST);
+ gl.depthMask(true);
+ gl.depthFunc(gl.NOTEQUAL);
+ }
+
+ if (!ol.obj.isEmpty(skippedFeaturesHash)) {
+ this.drawReplaySkipping_(gl, context, skippedFeaturesHash);
+ } else {
+ ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
+ 'number of styles and styleIndices match');
+
+ //Draw by style groups to minimize drawElements() calls.
+ var i, start, end, nextStyle;
+ end = this.startIndices[this.startIndices.length - 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ start = this.styleIndices_[i];
+ nextStyle = this.styles_[i];
+ this.setFillStyle_(gl, nextStyle);
+ this.drawElements(gl, context, start, end);
+ end = start;
+ }
+ }
+ if (!hitDetection) {
+ gl.disable(gl.DEPTH_TEST);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ //Restore GL parameters.
+ gl.depthMask(tmpDepthMask);
+ gl.depthFunc(tmpDepthFunc);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash,
+ featureCallback, opt_hitExtent) {
+ ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
+ 'number of styles and styleIndices match');
+ ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
+ 'number of startIndices and startIndicesFeature match');
+
+ var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex;
+ featureIndex = this.startIndices.length - 2;
+ end = this.startIndices[featureIndex + 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ nextStyle = this.styles_[i];
+ this.setFillStyle_(gl, nextStyle);
+ groupStart = this.styleIndices_[i];
+
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ start = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid] === undefined &&
+ feature.getGeometry() &&
+ (opt_hitExtent === undefined || ol.extent.intersects(
+ /** @type {Array<number>} */ (opt_hitExtent),
+ feature.getGeometry().getExtent()))) {
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ this.drawElements(gl, context, start, end);
+
+ var result = featureCallback(feature);
+
+ if (result) {
+ return result;
+ }
+
+ }
+ featureIndex--;
+ end = start;
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {ol.webgl.Context} context Context.
+ * @param {Object} skippedFeaturesHash Ids of features to skip.
+ */
+ol.render.webgl.PolygonReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) {
+ ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
+ 'number of startIndices and startIndicesFeature match');
+
+ var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart;
+ featureIndex = this.startIndices.length - 2;
+ end = start = this.startIndices[featureIndex + 1];
+ for (i = this.styleIndices_.length - 1; i >= 0; --i) {
+ nextStyle = this.styles_[i];
+ this.setFillStyle_(gl, nextStyle);
+ groupStart = this.styleIndices_[i];
+
+ while (featureIndex >= 0 &&
+ this.startIndices[featureIndex] >= groupStart) {
+ featureStart = this.startIndices[featureIndex];
+ feature = this.startIndicesFeature[featureIndex];
+ featureUid = ol.getUid(feature).toString();
+
+ if (skippedFeaturesHash[featureUid]) {
+ if (start !== end) {
+ this.drawElements(gl, context, start, end);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ end = featureStart;
+ }
+ featureIndex--;
+ start = featureStart;
+ }
+ if (start !== end) {
+ this.drawElements(gl, context, start, end);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ start = end = groupStart;
+ }
+};
+
+
+/**
+ * @private
+ * @param {WebGLRenderingContext} gl gl.
+ * @param {Array.<number>} color Color.
+ */
+ol.render.webgl.PolygonReplay.prototype.setFillStyle_ = function(gl, color) {
+ gl.uniform4fv(this.defaultLocations_.u_color, color);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.PolygonReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
+ var fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0];
+ if (!(fillStyleColor instanceof CanvasGradient) &&
+ !(fillStyleColor instanceof CanvasPattern)) {
+ fillStyleColor = ol.color.asArray(fillStyleColor).map(function(c, i) {
+ return i != 3 ? c / 255 : c;
+ }) || ol.render.webgl.defaultFillStyle;
+ } else {
+ fillStyleColor = ol.render.webgl.defaultFillStyle;
+ }
+ if (!this.state_.fillColor || !ol.array.equals(fillStyleColor, this.state_.fillColor)) {
+ this.state_.fillColor = fillStyleColor;
+ this.state_.changed = true;
+ this.styles_.push(fillStyleColor);
+ }
+ //Provide a null stroke style, if no strokeStyle is provided. Required for the draw interaction to work.
+ if (strokeStyle) {
+ this.lineStringReplay.setFillStrokeStyle(null, strokeStyle);
+ } else {
+ var nullStrokeStyle = new ol.style.Stroke({
+ color: [0, 0, 0, 0],
+ lineWidth: 0
+ });
+ this.lineStringReplay.setFillStrokeStyle(null, nullStrokeStyle);
+ }
+};
+
+goog.provide('ol.render.webgl.TextReplay');
+
+goog.require('ol');
+
+/**
+ * @constructor
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @struct
+ */
+ol.render.webgl.TextReplay = function(tolerance, maxExtent) {};
+
+/**
+ * @param {ol.style.Text} textStyle Text style.
+ */
+ol.render.webgl.TextReplay.prototype.setTextStyle = function(textStyle) {};
+
+/**
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} opacity Global opacity.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
+ * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
+ * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
+ * this extent are checked.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.TextReplay.prototype.replay = function(context,
+ center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash,
+ featureCallback, oneByOne, opt_hitExtent) {
+ return undefined;
+};
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry.
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ */
+ol.render.webgl.TextReplay.prototype.drawText = function(flatCoordinates, offset,
+ end, stride, geometry, feature) {};
+
+/**
+ * @abstract
+ * @param {ol.webgl.Context} context Context.
+ */
+ol.render.webgl.TextReplay.prototype.finish = function(context) {};
+
+/**
+ * @param {ol.webgl.Context} context WebGL context.
+ * @return {function()} Delete resources function.
+ */
+ol.render.webgl.TextReplay.prototype.getDeleteResourcesFunction = function(context) {
+ return ol.nullFunction;
+};
+
+goog.provide('ol.render.webgl.ReplayGroup');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.render.replay');
+goog.require('ol.render.ReplayGroup');
+goog.require('ol.render.webgl');
+goog.require('ol.render.webgl.CircleReplay');
+goog.require('ol.render.webgl.ImageReplay');
+goog.require('ol.render.webgl.LineStringReplay');
+goog.require('ol.render.webgl.PolygonReplay');
+goog.require('ol.render.webgl.TextReplay');
+
+/**
+ * @constructor
+ * @extends {ol.render.ReplayGroup}
+ * @param {number} tolerance Tolerance.
+ * @param {ol.Extent} maxExtent Max extent.
+ * @param {number=} opt_renderBuffer Render buffer.
+ * @struct
+ */
+ol.render.webgl.ReplayGroup = function(tolerance, maxExtent, opt_renderBuffer) {
+ ol.render.ReplayGroup.call(this);
+
+ /**
+ * @type {ol.Extent}
+ * @private
+ */
+ this.maxExtent_ = maxExtent;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.tolerance_ = tolerance;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.renderBuffer_ = opt_renderBuffer;
+
+ /**
+ * @private
+ * @type {!Object.<string,
+ * Object.<ol.render.ReplayType, ol.render.webgl.Replay>>}
+ */
+ this.replaysByZIndex_ = {};
+
+};
+ol.inherits(ol.render.webgl.ReplayGroup, ol.render.ReplayGroup);
+
+
+/**
+ * @param {ol.webgl.Context} context WebGL context.
+ * @return {function()} Delete resources function.
+ */
+ol.render.webgl.ReplayGroup.prototype.getDeleteResourcesFunction = function(context) {
+ var functions = [];
+ var zKey;
+ for (zKey in this.replaysByZIndex_) {
+ var replays = this.replaysByZIndex_[zKey];
+ var replayKey;
+ for (replayKey in replays) {
+ functions.push(
+ replays[replayKey].getDeleteResourcesFunction(context));
+ }
+ }
+ return function() {
+ var length = functions.length;
+ var result;
+ for (var i = 0; i < length; i++) {
+ result = functions[i].apply(this, arguments);
+ }
+ return result;
+ };
+};
+
+
+/**
+ * @param {ol.webgl.Context} context Context.
+ */
+ol.render.webgl.ReplayGroup.prototype.finish = function(context) {
+ var zKey;
+ for (zKey in this.replaysByZIndex_) {
+ var replays = this.replaysByZIndex_[zKey];
+ var replayKey;
+ for (replayKey in replays) {
+ replays[replayKey].finish(context);
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ReplayGroup.prototype.getReplay = function(zIndex, replayType) {
+ var zIndexKey = zIndex !== undefined ? zIndex.toString() : '0';
+ var replays = this.replaysByZIndex_[zIndexKey];
+ if (replays === undefined) {
+ replays = {};
+ this.replaysByZIndex_[zIndexKey] = replays;
+ }
+ var replay = replays[replayType];
+ if (replay === undefined) {
+ var Constructor = ol.render.webgl.ReplayGroup.BATCH_CONSTRUCTORS_[replayType];
+ ol.DEBUG && console.assert(Constructor !== undefined,
+ replayType +
+ ' constructor missing from ol.render.webgl.ReplayGroup.BATCH_CONSTRUCTORS_');
+ replay = new Constructor(this.tolerance_, this.maxExtent_);
+ replays[replayType] = replay;
+ }
+ return replay;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.ReplayGroup.prototype.isEmpty = function() {
+ return ol.obj.isEmpty(this.replaysByZIndex_);
+};
+
+
+/**
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} opacity Global opacity.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ */
+ol.render.webgl.ReplayGroup.prototype.replay = function(context,
+ center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash) {
+ /** @type {Array.<number>} */
+ var zs = Object.keys(this.replaysByZIndex_).map(Number);
+ zs.sort(ol.array.numberSafeCompareFunction);
+
+ var i, ii, j, jj, replays, replay;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ replays = this.replaysByZIndex_[zs[i].toString()];
+ for (j = 0, jj = ol.render.replay.ORDER.length; j < jj; ++j) {
+ replay = replays[ol.render.replay.ORDER[j]];
+ if (replay !== undefined) {
+ replay.replay(context,
+ center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash,
+ undefined, false);
+ }
+ }
+ }
+};
+
+
+/**
+ * @private
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} opacity Global opacity.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
+ * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
+ * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
+ * this extent are checked.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.ReplayGroup.prototype.replayHitDetection_ = function(context,
+ center, resolution, rotation, size, pixelRatio, opacity,
+ skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) {
+ /** @type {Array.<number>} */
+ var zs = Object.keys(this.replaysByZIndex_).map(Number);
+ zs.sort(function(a, b) {
+ return b - a;
+ });
+
+ var i, ii, j, replays, replay, result;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ replays = this.replaysByZIndex_[zs[i].toString()];
+ for (j = ol.render.replay.ORDER.length - 1; j >= 0; --j) {
+ replay = replays[ol.render.replay.ORDER[j]];
+ if (replay !== undefined) {
+ result = replay.replay(context,
+ center, resolution, rotation, size, pixelRatio, opacity,
+ skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} opacity Global opacity.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @param {function((ol.Feature|ol.render.Feature)): T|undefined} callback Feature callback.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.render.webgl.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
+ coordinate, context, center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash,
+ callback) {
+ var gl = context.getGL();
+ gl.bindFramebuffer(
+ gl.FRAMEBUFFER, context.getHitDetectionFramebuffer());
+
+
+ /**
+ * @type {ol.Extent}
+ */
+ var hitExtent;
+ if (this.renderBuffer_ !== undefined) {
+ // build an extent around the coordinate, so that only features that
+ // intersect this extent are checked
+ hitExtent = ol.extent.buffer(
+ ol.extent.createOrUpdateFromCoordinate(coordinate),
+ resolution * this.renderBuffer_);
+ }
+
+ return this.replayHitDetection_(context,
+ coordinate, resolution, rotation, ol.render.webgl.ReplayGroup.HIT_DETECTION_SIZE_,
+ pixelRatio, opacity, skippedFeaturesHash,
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ var imageData = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
+
+ if (imageData[3] > 0) {
+ var result = callback(feature);
+ if (result) {
+ return result;
+ }
+ }
+ }, true, hitExtent);
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} opacity Global opacity.
+ * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
+ * to skip.
+ * @return {boolean} Is there a feature at the given coordinate?
+ */
+ol.render.webgl.ReplayGroup.prototype.hasFeatureAtCoordinate = function(
+ coordinate, context, center, resolution, rotation, size, pixelRatio,
+ opacity, skippedFeaturesHash) {
+ var gl = context.getGL();
+ gl.bindFramebuffer(
+ gl.FRAMEBUFFER, context.getHitDetectionFramebuffer());
+
+ var hasFeature = this.replayHitDetection_(context,
+ coordinate, resolution, rotation, ol.render.webgl.ReplayGroup.HIT_DETECTION_SIZE_,
+ pixelRatio, opacity, skippedFeaturesHash,
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {boolean} Is there a feature?
+ */
+ function(feature) {
+ var imageData = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
+ return imageData[3] > 0;
+ }, false);
+
+ return hasFeature !== undefined;
+};
+
+/**
+ * @const
+ * @private
+ * @type {Array.<number>}
+ */
+ol.render.webgl.ReplayGroup.HIT_DETECTION_SIZE_ = [1, 1];
+
+/**
+ * @const
+ * @private
+ * @type {Object.<ol.render.ReplayType,
+ * function(new: ol.render.webgl.Replay, number,
+ * ol.Extent)>}
+ */
+ol.render.webgl.ReplayGroup.BATCH_CONSTRUCTORS_ = {
+ 'Circle': ol.render.webgl.CircleReplay,
+ 'Image': ol.render.webgl.ImageReplay,
+ 'LineString': ol.render.webgl.LineStringReplay,
+ 'Polygon': ol.render.webgl.PolygonReplay,
+ 'Text': ol.render.webgl.TextReplay
+};
+
+goog.provide('ol.render.webgl.Immediate');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.render.ReplayType');
+goog.require('ol.render.VectorContext');
+goog.require('ol.render.webgl.ReplayGroup');
+goog.require('ol.render.webgl');
+
+
+/**
+ * @constructor
+ * @extends {ol.render.VectorContext}
+ * @param {ol.webgl.Context} context Context.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {ol.Size} size Size.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} pixelRatio Pixel ratio.
+ * @struct
+ */
+ol.render.webgl.Immediate = function(context, center, resolution, rotation, size, extent, pixelRatio) {
+ ol.render.VectorContext.call(this);
+
+ /**
+ * @private
+ */
+ this.context_ = context;
+
+ /**
+ * @private
+ */
+ this.center_ = center;
+
+ /**
+ * @private
+ */
+ this.extent_ = extent;
+
+ /**
+ * @private
+ */
+ this.pixelRatio_ = pixelRatio;
+
+ /**
+ * @private
+ */
+ this.size_ = size;
+
+ /**
+ * @private
+ */
+ this.rotation_ = rotation;
+
+ /**
+ * @private
+ */
+ this.resolution_ = resolution;
+
+ /**
+ * @private
+ * @type {ol.style.Image}
+ */
+ this.imageStyle_ = null;
+
+ /**
+ * @private
+ * @type {ol.style.Fill}
+ */
+ this.fillStyle_ = null;
+
+ /**
+ * @private
+ * @type {ol.style.Stroke}
+ */
+ this.strokeStyle_ = null;
+
+};
+ol.inherits(ol.render.webgl.Immediate, ol.render.VectorContext);
+
+
+/**
+ * Set the rendering style. Note that since this is an immediate rendering API,
+ * any `zIndex` on the provided style will be ignored.
+ *
+ * @param {ol.style.Style} style The rendering style.
+ * @api
+ */
+ol.render.webgl.Immediate.prototype.setStyle = function(style) {
+ this.setFillStrokeStyle(style.getFill(), style.getStroke());
+ this.setImageStyle(style.getImage());
+};
+
+
+/**
+ * Render a geometry into the canvas. Call
+ * {@link ol.render.webgl.Immediate#setStyle} first to set the rendering style.
+ *
+ * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render.
+ * @api
+ */
+ol.render.webgl.Immediate.prototype.drawGeometry = function(geometry) {
+ var type = geometry.getType();
+ switch (type) {
+ case ol.geom.GeometryType.POINT:
+ this.drawPoint(/** @type {ol.geom.Point} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.LINE_STRING:
+ this.drawLineString(/** @type {ol.geom.LineString} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.POLYGON:
+ this.drawPolygon(/** @type {ol.geom.Polygon} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.MULTI_POINT:
+ this.drawMultiPoint(/** @type {ol.geom.MultiPoint} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.MULTI_LINE_STRING:
+ this.drawMultiLineString(/** @type {ol.geom.MultiLineString} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.MULTI_POLYGON:
+ this.drawMultiPolygon(/** @type {ol.geom.MultiPolygon} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.GEOMETRY_COLLECTION:
+ this.drawGeometryCollection(/** @type {ol.geom.GeometryCollection} */ (geometry), null);
+ break;
+ case ol.geom.GeometryType.CIRCLE:
+ this.drawCircle(/** @type {ol.geom.Circle} */ (geometry), null);
+ break;
+ default:
+ ol.DEBUG && console.assert(false, 'Unsupported geometry type: ' + type);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) {
+ var geometry = style.getGeometryFunction()(feature);
+ if (!geometry ||
+ !ol.extent.intersects(this.extent_, geometry.getExtent())) {
+ return;
+ }
+ this.setStyle(style);
+ ol.DEBUG && console.assert(geometry, 'geometry must be truthy');
+ this.drawGeometry(geometry);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawGeometryCollection = function(geometry, data) {
+ var geometries = geometry.getGeometriesArray();
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ this.drawGeometry(geometries[i]);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawPoint = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.ImageReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.IMAGE));
+ replay.setImageStyle(this.imageStyle_);
+ replay.drawPoint(geometry, data);
+ replay.finish(context);
+ // default colors
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawMultiPoint = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.ImageReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.IMAGE));
+ replay.setImageStyle(this.imageStyle_);
+ replay.drawMultiPoint(geometry, data);
+ replay.finish(context);
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawLineString = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.LineStringReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.LINE_STRING));
+ replay.setFillStrokeStyle(null, this.strokeStyle_);
+ replay.drawLineString(geometry, data);
+ replay.finish(context);
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawMultiLineString = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.LineStringReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.LINE_STRING));
+ replay.setFillStrokeStyle(null, this.strokeStyle_);
+ replay.drawMultiLineString(geometry, data);
+ replay.finish(context);
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawPolygon = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.PolygonReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.POLYGON));
+ replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_);
+ replay.drawPolygon(geometry, data);
+ replay.finish(context);
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawMultiPolygon = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.PolygonReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.POLYGON));
+ replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_);
+ replay.drawMultiPolygon(geometry, data);
+ replay.finish(context);
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.drawCircle = function(geometry, data) {
+ var context = this.context_;
+ var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
+ var replay = /** @type {ol.render.webgl.CircleReplay} */ (
+ replayGroup.getReplay(0, ol.render.ReplayType.CIRCLE));
+ replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_);
+ replay.drawCircle(geometry, data);
+ replay.finish(context);
+ var opacity = 1;
+ var skippedFeatures = {};
+ var featureCallback;
+ var oneByOne = false;
+ replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
+ this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback,
+ oneByOne);
+ replay.getDeleteResourcesFunction(context)();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) {
+ this.imageStyle_ = imageStyle;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.render.webgl.Immediate.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
+ this.fillStyle_ = fillStyle;
+ this.strokeStyle_ = strokeStyle;
+};
+
+// This file is automatically generated, do not edit
+goog.provide('ol.renderer.webgl.defaultmapshader');
+
+goog.require('ol');
+goog.require('ol.webgl.Fragment');
+goog.require('ol.webgl.Vertex');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Fragment}
+ * @struct
+ */
+ol.renderer.webgl.defaultmapshader.Fragment = function() {
+ ol.webgl.Fragment.call(this, ol.renderer.webgl.defaultmapshader.Fragment.SOURCE);
+};
+ol.inherits(ol.renderer.webgl.defaultmapshader.Fragment, ol.webgl.Fragment);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.defaultmapshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform float u_opacity;\nuniform sampler2D u_texture;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_texture, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n gl_FragColor.a = texColor.a * u_opacity;\n}\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.defaultmapshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.defaultmapshader.Fragment.SOURCE = ol.DEBUG ?
+ ol.renderer.webgl.defaultmapshader.Fragment.DEBUG_SOURCE :
+ ol.renderer.webgl.defaultmapshader.Fragment.OPTIMIZED_SOURCE;
+
+
+ol.renderer.webgl.defaultmapshader.fragment = new ol.renderer.webgl.defaultmapshader.Fragment();
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Vertex}
+ * @struct
+ */
+ol.renderer.webgl.defaultmapshader.Vertex = function() {
+ ol.webgl.Vertex.call(this, ol.renderer.webgl.defaultmapshader.Vertex.SOURCE);
+};
+ol.inherits(ol.renderer.webgl.defaultmapshader.Vertex, ol.webgl.Vertex);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.defaultmapshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\n\nuniform mat4 u_texCoordMatrix;\nuniform mat4 u_projectionMatrix;\n\nvoid main(void) {\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);\n v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;\n}\n\n\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.defaultmapshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.defaultmapshader.Vertex.SOURCE = ol.DEBUG ?
+ ol.renderer.webgl.defaultmapshader.Vertex.DEBUG_SOURCE :
+ ol.renderer.webgl.defaultmapshader.Vertex.OPTIMIZED_SOURCE;
+
+
+ol.renderer.webgl.defaultmapshader.vertex = new ol.renderer.webgl.defaultmapshader.Vertex();
+
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ * @struct
+ */
+ol.renderer.webgl.defaultmapshader.Locations = function(gl, program) {
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_opacity = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_opacity' : 'f');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_projectionMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_projectionMatrix' : 'e');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_texCoordMatrix = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_texCoordMatrix' : 'd');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_texture = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_texture' : 'g');
+
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_position' : 'b');
+
+ /**
+ * @type {number}
+ */
+ this.a_texCoord = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_texCoord' : 'c');
+};
+
+goog.provide('ol.renderer.webgl.Layer');
+
+goog.require('ol');
+goog.require('ol.render.Event');
+goog.require('ol.render.webgl.Immediate');
+goog.require('ol.renderer.Layer');
+goog.require('ol.renderer.webgl.defaultmapshader');
+goog.require('ol.transform');
+goog.require('ol.vec.Mat4');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Buffer');
+goog.require('ol.webgl.Context');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.Layer}
+ * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
+ * @param {ol.layer.Layer} layer Layer.
+ */
+ol.renderer.webgl.Layer = function(mapRenderer, layer) {
+
+ ol.renderer.Layer.call(this, layer);
+
+ /**
+ * @protected
+ * @type {ol.renderer.webgl.Map}
+ */
+ this.mapRenderer = mapRenderer;
+
+ /**
+ * @private
+ * @type {ol.webgl.Buffer}
+ */
+ this.arrayBuffer_ = new ol.webgl.Buffer([
+ -1, -1, 0, 0,
+ 1, -1, 1, 0,
+ -1, 1, 0, 1,
+ 1, 1, 1, 1
+ ]);
+
+ /**
+ * @protected
+ * @type {WebGLTexture}
+ */
+ this.texture = null;
+
+ /**
+ * @protected
+ * @type {WebGLFramebuffer}
+ */
+ this.framebuffer = null;
+
+ /**
+ * @protected
+ * @type {number|undefined}
+ */
+ this.framebufferDimension = undefined;
+
+ /**
+ * @protected
+ * @type {ol.Transform}
+ */
+ this.texCoordMatrix = ol.transform.create();
+
+ /**
+ * @protected
+ * @type {ol.Transform}
+ */
+ this.projectionMatrix = ol.transform.create();
+
+ /**
+ * @type {Array.<number>}
+ * @private
+ */
+ this.tmpMat4_ = ol.vec.Mat4.create();
+
+ /**
+ * @private
+ * @type {ol.renderer.webgl.defaultmapshader.Locations}
+ */
+ this.defaultLocations_ = null;
+
+};
+ol.inherits(ol.renderer.webgl.Layer, ol.renderer.Layer);
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {number} framebufferDimension Framebuffer dimension.
+ * @protected
+ */
+ol.renderer.webgl.Layer.prototype.bindFramebuffer = function(frameState, framebufferDimension) {
+
+ var gl = this.mapRenderer.getGL();
+
+ if (this.framebufferDimension === undefined ||
+ this.framebufferDimension != framebufferDimension) {
+ /**
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLFramebuffer} framebuffer Framebuffer.
+ * @param {WebGLTexture} texture Texture.
+ */
+ var postRenderFunction = function(gl, framebuffer, texture) {
+ if (!gl.isContextLost()) {
+ gl.deleteFramebuffer(framebuffer);
+ gl.deleteTexture(texture);
+ }
+ }.bind(null, gl, this.framebuffer, this.texture);
+
+ frameState.postRenderFunctions.push(
+ /** @type {ol.PostRenderFunction} */ (postRenderFunction)
+ );
+
+ var texture = ol.webgl.Context.createEmptyTexture(
+ gl, framebufferDimension, framebufferDimension);
+
+ var framebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(ol.webgl.FRAMEBUFFER, framebuffer);
+ gl.framebufferTexture2D(ol.webgl.FRAMEBUFFER,
+ ol.webgl.COLOR_ATTACHMENT0, ol.webgl.TEXTURE_2D, texture, 0);
+
+ this.texture = texture;
+ this.framebuffer = framebuffer;
+ this.framebufferDimension = framebufferDimension;
+
+ } else {
+ gl.bindFramebuffer(ol.webgl.FRAMEBUFFER, this.framebuffer);
+ }
+
+};
+
+
+/**
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @param {ol.webgl.Context} context Context.
+ */
+ol.renderer.webgl.Layer.prototype.composeFrame = function(frameState, layerState, context) {
+
+ this.dispatchComposeEvent_(
+ ol.render.Event.Type.PRECOMPOSE, context, frameState);
+
+ context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.arrayBuffer_);
+
+ var gl = context.getGL();
+
+ var fragmentShader = ol.renderer.webgl.defaultmapshader.fragment;
+ var vertexShader = ol.renderer.webgl.defaultmapshader.vertex;
+
+ var program = context.getProgram(fragmentShader, vertexShader);
+
+ var locations;
+ if (!this.defaultLocations_) {
+ locations =
+ new ol.renderer.webgl.defaultmapshader.Locations(gl, program);
+ this.defaultLocations_ = locations;
+ } else {
+ locations = this.defaultLocations_;
+ }
+
+ if (context.useProgram(program)) {
+ gl.enableVertexAttribArray(locations.a_position);
+ gl.vertexAttribPointer(
+ locations.a_position, 2, ol.webgl.FLOAT, false, 16, 0);
+ gl.enableVertexAttribArray(locations.a_texCoord);
+ gl.vertexAttribPointer(
+ locations.a_texCoord, 2, ol.webgl.FLOAT, false, 16, 8);
+ gl.uniform1i(locations.u_texture, 0);
+ }
+
+ gl.uniformMatrix4fv(locations.u_texCoordMatrix, false,
+ ol.vec.Mat4.fromTransform(this.tmpMat4_, this.getTexCoordMatrix()));
+ gl.uniformMatrix4fv(locations.u_projectionMatrix, false,
+ ol.vec.Mat4.fromTransform(this.tmpMat4_, this.getProjectionMatrix()));
+ gl.uniform1f(locations.u_opacity, layerState.opacity);
+ gl.bindTexture(ol.webgl.TEXTURE_2D, this.getTexture());
+ gl.drawArrays(ol.webgl.TRIANGLE_STRIP, 0, 4);
+
+ this.dispatchComposeEvent_(
+ ol.render.Event.Type.POSTCOMPOSE, context, frameState);
+
+};
+
+
+/**
+ * @param {ol.render.Event.Type} type Event type.
+ * @param {ol.webgl.Context} context WebGL context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState) {
+ var layer = this.getLayer();
+ if (layer.hasListener(type)) {
+ var viewState = frameState.viewState;
+ var resolution = viewState.resolution;
+ var pixelRatio = frameState.pixelRatio;
+ var extent = frameState.extent;
+ var center = viewState.center;
+ var rotation = viewState.rotation;
+ var size = frameState.size;
+
+ var render = new ol.render.webgl.Immediate(
+ context, center, resolution, rotation, size, extent, pixelRatio);
+ var composeEvent = new ol.render.Event(
+ type, render, frameState, null, context);
+ layer.dispatchEvent(composeEvent);
+ }
+};
+
+
+/**
+ * @return {!ol.Transform} Matrix.
+ */
+ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = function() {
+ return this.texCoordMatrix;
+};
+
+
+/**
+ * @return {WebGLTexture} Texture.
+ */
+ol.renderer.webgl.Layer.prototype.getTexture = function() {
+ return this.texture;
+};
+
+
+/**
+ * @return {!ol.Transform} Matrix.
+ */
+ol.renderer.webgl.Layer.prototype.getProjectionMatrix = function() {
+ return this.projectionMatrix;
+};
+
+
+/**
+ * Handle webglcontextlost.
+ */
+ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() {
+ this.texture = null;
+ this.framebuffer = null;
+ this.framebufferDimension = undefined;
+};
+
+
+/**
+ * @abstract
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.LayerState} layerState Layer state.
+ * @param {ol.webgl.Context} context Context.
+ * @return {boolean} whether composeFrame should be called.
+ */
+ol.renderer.webgl.Layer.prototype.prepareFrame = function(frameState, layerState, context) {};
+
+
+/**
+ * @abstract
+ * @param {ol.Pixel} pixel Pixel.
+ * @param {olx.FrameState} frameState FrameState.
+ * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer
+ * callback.
+ * @param {S} thisArg Value to use as `this` when executing `callback`.
+ * @return {T|undefined} Callback result.
+ * @template S,T,U
+ */
+ol.renderer.webgl.Layer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {};
+
+goog.provide('ol.ImageCanvas');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.ImageBase');
+
+
+/**
+ * @constructor
+ * @extends {ol.ImageBase}
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {Array.<ol.Attribution>} attributions Attributions.
+ * @param {HTMLCanvasElement} canvas Canvas.
+ * @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to
+ * support asynchronous canvas drawing.
+ */
+ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
+ canvas, opt_loader) {
+
+ /**
+ * Optional canvas loader function.
+ * @type {?ol.ImageCanvasLoader}
+ * @private
+ */
+ this.loader_ = opt_loader !== undefined ? opt_loader : null;
+
+ var state = opt_loader !== undefined ?
+ ol.Image.State.IDLE : ol.Image.State.LOADED;
+
+ ol.ImageBase.call(this, extent, resolution, pixelRatio, state, attributions);
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = canvas;
+
+ /**
+ * @private
+ * @type {Error}
+ */
+ this.error_ = null;
+
+};
+ol.inherits(ol.ImageCanvas, ol.ImageBase);
+
+
+/**
+ * Get any error associated with asynchronous rendering.
+ * @return {Error} Any error that occurred during rendering.
+ */
+ol.ImageCanvas.prototype.getError = function() {
+ return this.error_;
+};
+
+
+/**
+ * Handle async drawing complete.
+ * @param {Error} err Any error during drawing.
+ * @private
+ */
+ol.ImageCanvas.prototype.handleLoad_ = function(err) {
+ if (err) {
+ this.error_ = err;
+ this.state = ol.Image.State.ERROR;
+ } else {
+ this.state = ol.Image.State.LOADED;
+ }
+ this.changed();
+};
+
+
+/**
+ * Trigger drawing on canvas.
+ */
+ol.ImageCanvas.prototype.load = function() {
+ if (this.state == ol.Image.State.IDLE) {
+ ol.DEBUG && console.assert(this.loader_, 'this.loader_ must be set');
+ this.state = ol.Image.State.LOADING;
+ this.changed();
+ this.loader_(this.handleLoad_.bind(this));
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.ImageCanvas.prototype.getImage = function(opt_context) {
+ return this.canvas_;
+};
+
+goog.provide('ol.reproj');
+
+goog.require('ol');
+goog.require('ol.dom');
+goog.require('ol.extent');
+goog.require('ol.math');
+goog.require('ol.proj');
+
+
+/**
+ * We need to employ more sophisticated solution
+ * if the web browser antialiases clipping edges on canvas.
+ *
+ * Currently only Chrome does not antialias the edges, but this is probably
+ * going to be "fixed" in the future: http://crbug.com/424291
+ *
+ * @type {boolean}
+ * @private
+ */
+ol.reproj.browserAntialiasesClip_ = (function() {
+ // Adapted from http://stackoverflow.com/questions/4565112/javascript-how-to-find-out-if-the-user-browser-is-chrome
+ var isOpera = navigator.userAgent.indexOf('OPR') > -1;
+ var isIEedge = navigator.userAgent.indexOf('Edge') > -1;
+ return !(
+ !navigator.userAgent.match('CriOS') && // Not Chrome on iOS
+ 'chrome' in window && // Has chrome in window
+ navigator.vendor === 'Google Inc.' && // Vendor is Google.
+ isOpera == false && // Not Opera
+ isIEedge == false // Not Edge
+ );
+})();
+
+
+/**
+ * Calculates ideal resolution to use from the source in order to achieve
+ * pixel mapping as close as possible to 1:1 during reprojection.
+ * The resolution is calculated regardless of what resolutions
+ * are actually available in the dataset (TileGrid, Image, ...).
+ *
+ * @param {ol.proj.Projection} sourceProj Source projection.
+ * @param {ol.proj.Projection} targetProj Target projection.
+ * @param {ol.Coordinate} targetCenter Target center.
+ * @param {number} targetResolution Target resolution.
+ * @return {number} The best resolution to use. Can be +-Infinity, NaN or 0.
+ */
+ol.reproj.calculateSourceResolution = function(sourceProj, targetProj,
+ targetCenter, targetResolution) {
+
+ var sourceCenter = ol.proj.transform(targetCenter, targetProj, sourceProj);
+
+ // calculate the ideal resolution of the source data
+ var sourceResolution =
+ ol.proj.getPointResolution(targetProj, targetResolution, targetCenter);
+
+ var targetMetersPerUnit = targetProj.getMetersPerUnit();
+ if (targetMetersPerUnit !== undefined) {
+ sourceResolution *= targetMetersPerUnit;
+ }
+ var sourceMetersPerUnit = sourceProj.getMetersPerUnit();
+ if (sourceMetersPerUnit !== undefined) {
+ sourceResolution /= sourceMetersPerUnit;
+ }
+
+ // Based on the projection properties, the point resolution at the specified
+ // coordinates may be slightly different. We need to reverse-compensate this
+ // in order to achieve optimal results.
+
+ var compensationFactor =
+ ol.proj.getPointResolution(sourceProj, sourceResolution, sourceCenter) /
+ sourceResolution;
+
+ if (isFinite(compensationFactor) && compensationFactor > 0) {
+ sourceResolution /= compensationFactor;
+ }
+
+ return sourceResolution;
+};
+
+
+/**
+ * Enlarge the clipping triangle point by 1 pixel to ensure the edges overlap
+ * in order to mask gaps caused by antialiasing.
+ *
+ * @param {number} centroidX Centroid of the triangle (x coordinate in pixels).
+ * @param {number} centroidY Centroid of the triangle (y coordinate in pixels).
+ * @param {number} x X coordinate of the point (in pixels).
+ * @param {number} y Y coordinate of the point (in pixels).
+ * @return {ol.Coordinate} New point 1 px farther from the centroid.
+ * @private
+ */
+ol.reproj.enlargeClipPoint_ = function(centroidX, centroidY, x, y) {
+ var dX = x - centroidX, dY = y - centroidY;
+ var distance = Math.sqrt(dX * dX + dY * dY);
+ return [Math.round(x + dX / distance), Math.round(y + dY / distance)];
+};
+
+
+/**
+ * Renders the source data into new canvas based on the triangulation.
+ *
+ * @param {number} width Width of the canvas.
+ * @param {number} height Height of the canvas.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} sourceResolution Source resolution.
+ * @param {ol.Extent} sourceExtent Extent of the data source.
+ * @param {number} targetResolution Target resolution.
+ * @param {ol.Extent} targetExtent Target extent.
+ * @param {ol.reproj.Triangulation} triangulation Calculated triangulation.
+ * @param {Array.<{extent: ol.Extent,
+ * image: (HTMLCanvasElement|Image|HTMLVideoElement)}>} sources
+ * Array of sources.
+ * @param {number} gutter Gutter of the sources.
+ * @param {boolean=} opt_renderEdges Render reprojection edges.
+ * @return {HTMLCanvasElement} Canvas with reprojected data.
+ */
+ol.reproj.render = function(width, height, pixelRatio,
+ sourceResolution, sourceExtent, targetResolution, targetExtent,
+ triangulation, sources, gutter, opt_renderEdges) {
+
+ var context = ol.dom.createCanvasContext2D(Math.round(pixelRatio * width),
+ Math.round(pixelRatio * height));
+
+ if (sources.length === 0) {
+ return context.canvas;
+ }
+
+ context.scale(pixelRatio, pixelRatio);
+
+ var sourceDataExtent = ol.extent.createEmpty();
+ sources.forEach(function(src, i, arr) {
+ ol.extent.extend(sourceDataExtent, src.extent);
+ });
+
+ var canvasWidthInUnits = ol.extent.getWidth(sourceDataExtent);
+ var canvasHeightInUnits = ol.extent.getHeight(sourceDataExtent);
+ var stitchContext = ol.dom.createCanvasContext2D(
+ Math.round(pixelRatio * canvasWidthInUnits / sourceResolution),
+ Math.round(pixelRatio * canvasHeightInUnits / sourceResolution));
+
+ var stitchScale = pixelRatio / sourceResolution;
+
+ sources.forEach(function(src, i, arr) {
+ var xPos = src.extent[0] - sourceDataExtent[0];
+ var yPos = -(src.extent[3] - sourceDataExtent[3]);
+ var srcWidth = ol.extent.getWidth(src.extent);
+ var srcHeight = ol.extent.getHeight(src.extent);
+
+ stitchContext.drawImage(
+ src.image,
+ gutter, gutter,
+ src.image.width - 2 * gutter, src.image.height - 2 * gutter,
+ xPos * stitchScale, yPos * stitchScale,
+ srcWidth * stitchScale, srcHeight * stitchScale);
+ });
+
+ var targetTopLeft = ol.extent.getTopLeft(targetExtent);
+
+ triangulation.getTriangles().forEach(function(triangle, i, arr) {
+ /* Calculate affine transform (src -> dst)
+ * Resulting matrix can be used to transform coordinate
+ * from `sourceProjection` to destination pixels.
+ *
+ * To optimize number of context calls and increase numerical stability,
+ * we also do the following operations:
+ * trans(-topLeftExtentCorner), scale(1 / targetResolution), scale(1, -1)
+ * here before solving the linear system so [ui, vi] are pixel coordinates.
+ *
+ * Src points: xi, yi
+ * Dst points: ui, vi
+ * Affine coefficients: aij
+ *
+ * | x0 y0 1 0 0 0 | |a00| |u0|
+ * | x1 y1 1 0 0 0 | |a01| |u1|
+ * | x2 y2 1 0 0 0 | x |a02| = |u2|
+ * | 0 0 0 x0 y0 1 | |a10| |v0|
+ * | 0 0 0 x1 y1 1 | |a11| |v1|
+ * | 0 0 0 x2 y2 1 | |a12| |v2|
+ */
+ var source = triangle.source, target = triangle.target;
+ var x0 = source[0][0], y0 = source[0][1],
+ x1 = source[1][0], y1 = source[1][1],
+ x2 = source[2][0], y2 = source[2][1];
+ var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution,
+ v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution;
+ var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution,
+ v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution;
+ var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution,
+ v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution;
+
+ // Shift all the source points to improve numerical stability
+ // of all the subsequent calculations. The [x0, y0] is used here.
+ // This is also used to simplify the linear system.
+ var sourceNumericalShiftX = x0, sourceNumericalShiftY = y0;
+ x0 = 0;
+ y0 = 0;
+ x1 -= sourceNumericalShiftX;
+ y1 -= sourceNumericalShiftY;
+ x2 -= sourceNumericalShiftX;
+ y2 -= sourceNumericalShiftY;
+
+ var augmentedMatrix = [
+ [x1, y1, 0, 0, u1 - u0],
+ [x2, y2, 0, 0, u2 - u0],
+ [0, 0, x1, y1, v1 - v0],
+ [0, 0, x2, y2, v2 - v0]
+ ];
+ var affineCoefs = ol.math.solveLinearSystem(augmentedMatrix);
+ if (!affineCoefs) {
+ return;
+ }
+
+ context.save();
+ context.beginPath();
+ if (ol.reproj.browserAntialiasesClip_) {
+ var centroidX = (u0 + u1 + u2) / 3, centroidY = (v0 + v1 + v2) / 3;
+ var p0 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u0, v0);
+ var p1 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u1, v1);
+ var p2 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u2, v2);
+
+ context.moveTo(p1[0], p1[1]);
+ context.lineTo(p0[0], p0[1]);
+ context.lineTo(p2[0], p2[1]);
+ } else {
+ context.moveTo(u1, v1);
+ context.lineTo(u0, v0);
+ context.lineTo(u2, v2);
+ }
+ context.clip();
+
+ context.transform(
+ affineCoefs[0], affineCoefs[2], affineCoefs[1], affineCoefs[3], u0, v0);
+
+ context.translate(sourceDataExtent[0] - sourceNumericalShiftX,
+ sourceDataExtent[3] - sourceNumericalShiftY);
+
+ context.scale(sourceResolution / pixelRatio,
+ -sourceResolution / pixelRatio);
+
+ context.drawImage(stitchContext.canvas, 0, 0);
+ context.restore();
+ });
+
+ if (opt_renderEdges) {
+ context.save();
+
+ context.strokeStyle = 'black';
+ context.lineWidth = 1;
+
+ triangulation.getTriangles().forEach(function(triangle, i, arr) {
+ var target = triangle.target;
+ var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution,
+ v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution;
+ var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution,
+ v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution;
+ var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution,
+ v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution;
+
+ context.beginPath();
+ context.moveTo(u1, v1);
+ context.lineTo(u0, v0);
+ context.lineTo(u2, v2);
+ context.closePath();
+ context.stroke();
+ });
+
+ context.restore();
+ }
+ return context.canvas;
+};
+
+goog.provide('ol.reproj.Triangulation');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.math');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Class containing triangulation of the given target extent.
+ * Used for determining source data and the reprojection itself.
+ *
+ * @param {ol.proj.Projection} sourceProj Source projection.
+ * @param {ol.proj.Projection} targetProj Target projection.
+ * @param {ol.Extent} targetExtent Target extent to triangulate.
+ * @param {ol.Extent} maxSourceExtent Maximal source extent that can be used.
+ * @param {number} errorThreshold Acceptable error (in source units).
+ * @constructor
+ */
+ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent,
+ maxSourceExtent, errorThreshold) {
+
+ /**
+ * @type {ol.proj.Projection}
+ * @private
+ */
+ this.sourceProj_ = sourceProj;
+
+ /**
+ * @type {ol.proj.Projection}
+ * @private
+ */
+ this.targetProj_ = targetProj;
+
+ /** @type {!Object.<string, ol.Coordinate>} */
+ var transformInvCache = {};
+ var transformInv = ol.proj.getTransform(this.targetProj_, this.sourceProj_);
+
+ /**
+ * @param {ol.Coordinate} c A coordinate.
+ * @return {ol.Coordinate} Transformed coordinate.
+ * @private
+ */
+ this.transformInv_ = function(c) {
+ var key = c[0] + '/' + c[1];
+ if (!transformInvCache[key]) {
+ transformInvCache[key] = transformInv(c);
+ }
+ return transformInvCache[key];
+ };
+
+ /**
+ * @type {ol.Extent}
+ * @private
+ */
+ this.maxSourceExtent_ = maxSourceExtent;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.errorThresholdSquared_ = errorThreshold * errorThreshold;
+
+ /**
+ * @type {Array.<ol.ReprojTriangle>}
+ * @private
+ */
+ this.triangles_ = [];
+
+ /**
+ * Indicates that the triangulation crosses edge of the source projection.
+ * @type {boolean}
+ * @private
+ */
+ this.wrapsXInSource_ = false;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.canWrapXInSource_ = this.sourceProj_.canWrapX() &&
+ !!maxSourceExtent &&
+ !!this.sourceProj_.getExtent() &&
+ (ol.extent.getWidth(maxSourceExtent) ==
+ ol.extent.getWidth(this.sourceProj_.getExtent()));
+
+ /**
+ * @type {?number}
+ * @private
+ */
+ this.sourceWorldWidth_ = this.sourceProj_.getExtent() ?
+ ol.extent.getWidth(this.sourceProj_.getExtent()) : null;
+
+ /**
+ * @type {?number}
+ * @private
+ */
+ this.targetWorldWidth_ = this.targetProj_.getExtent() ?
+ ol.extent.getWidth(this.targetProj_.getExtent()) : null;
+
+ var destinationTopLeft = ol.extent.getTopLeft(targetExtent);
+ var destinationTopRight = ol.extent.getTopRight(targetExtent);
+ var destinationBottomRight = ol.extent.getBottomRight(targetExtent);
+ var destinationBottomLeft = ol.extent.getBottomLeft(targetExtent);
+ var sourceTopLeft = this.transformInv_(destinationTopLeft);
+ var sourceTopRight = this.transformInv_(destinationTopRight);
+ var sourceBottomRight = this.transformInv_(destinationBottomRight);
+ var sourceBottomLeft = this.transformInv_(destinationBottomLeft);
+
+ this.addQuad_(
+ destinationTopLeft, destinationTopRight,
+ destinationBottomRight, destinationBottomLeft,
+ sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft,
+ ol.RASTER_REPROJECTION_MAX_SUBDIVISION);
+
+ if (this.wrapsXInSource_) {
+ // Fix coordinates (ol.proj returns wrapped coordinates, "unwrap" here).
+ // This significantly simplifies the rest of the reprojection process.
+
+ ol.DEBUG && console.assert(this.sourceWorldWidth_ !== null);
+ var leftBound = Infinity;
+ this.triangles_.forEach(function(triangle, i, arr) {
+ leftBound = Math.min(leftBound,
+ triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
+ });
+
+ // Shift triangles to be as close to `leftBound` as possible
+ // (if the distance is more than `worldWidth / 2` it can be closer.
+ this.triangles_.forEach(function(triangle) {
+ if (Math.max(triangle.source[0][0], triangle.source[1][0],
+ triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
+ var newTriangle = [[triangle.source[0][0], triangle.source[0][1]],
+ [triangle.source[1][0], triangle.source[1][1]],
+ [triangle.source[2][0], triangle.source[2][1]]];
+ if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) {
+ newTriangle[0][0] -= this.sourceWorldWidth_;
+ }
+ if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) {
+ newTriangle[1][0] -= this.sourceWorldWidth_;
+ }
+ if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) {
+ newTriangle[2][0] -= this.sourceWorldWidth_;
+ }
+
+ // Rarely (if the extent contains both the dateline and prime meridian)
+ // the shift can in turn break some triangles.
+ // Detect this here and don't shift in such cases.
+ var minX = Math.min(
+ newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
+ var maxX = Math.max(
+ newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
+ if ((maxX - minX) < this.sourceWorldWidth_ / 2) {
+ triangle.source = newTriangle;
+ }
+ }
+ }, this);
+ }
+
+ transformInvCache = {};
+};
+
+
+/**
+ * Adds triangle to the triangulation.
+ * @param {ol.Coordinate} a The target a coordinate.
+ * @param {ol.Coordinate} b The target b coordinate.
+ * @param {ol.Coordinate} c The target c coordinate.
+ * @param {ol.Coordinate} aSrc The source a coordinate.
+ * @param {ol.Coordinate} bSrc The source b coordinate.
+ * @param {ol.Coordinate} cSrc The source c coordinate.
+ * @private
+ */
+ol.reproj.Triangulation.prototype.addTriangle_ = function(a, b, c,
+ aSrc, bSrc, cSrc) {
+ this.triangles_.push({
+ source: [aSrc, bSrc, cSrc],
+ target: [a, b, c]
+ });
+};
+
+
+/**
+ * Adds quad (points in clock-wise order) to the triangulation
+ * (and reprojects the vertices) if valid.
+ * Performs quad subdivision if needed to increase precision.
+ *
+ * @param {ol.Coordinate} a The target a coordinate.
+ * @param {ol.Coordinate} b The target b coordinate.
+ * @param {ol.Coordinate} c The target c coordinate.
+ * @param {ol.Coordinate} d The target d coordinate.
+ * @param {ol.Coordinate} aSrc The source a coordinate.
+ * @param {ol.Coordinate} bSrc The source b coordinate.
+ * @param {ol.Coordinate} cSrc The source c coordinate.
+ * @param {ol.Coordinate} dSrc The source d coordinate.
+ * @param {number} maxSubdivision Maximal allowed subdivision of the quad.
+ * @private
+ */
+ol.reproj.Triangulation.prototype.addQuad_ = function(a, b, c, d,
+ aSrc, bSrc, cSrc, dSrc, maxSubdivision) {
+
+ var sourceQuadExtent = ol.extent.boundingExtent([aSrc, bSrc, cSrc, dSrc]);
+ var sourceCoverageX = this.sourceWorldWidth_ ?
+ ol.extent.getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;
+ var sourceWorldWidth = /** @type {number} */ (this.sourceWorldWidth_);
+
+ // when the quad is wrapped in the source projection
+ // it covers most of the projection extent, but not fully
+ var wrapsX = this.sourceProj_.canWrapX() &&
+ sourceCoverageX > 0.5 && sourceCoverageX < 1;
+
+ var needsSubdivision = false;
+
+ if (maxSubdivision > 0) {
+ if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
+ var targetQuadExtent = ol.extent.boundingExtent([a, b, c, d]);
+ var targetCoverageX =
+ ol.extent.getWidth(targetQuadExtent) / this.targetWorldWidth_;
+ needsSubdivision |=
+ targetCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
+ }
+ if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
+ needsSubdivision |=
+ sourceCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
+ }
+ }
+
+ if (!needsSubdivision && this.maxSourceExtent_) {
+ if (!ol.extent.intersects(sourceQuadExtent, this.maxSourceExtent_)) {
+ // whole quad outside source projection extent -> ignore
+ return;
+ }
+ }
+
+ if (!needsSubdivision) {
+ if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ||
+ !isFinite(bSrc[0]) || !isFinite(bSrc[1]) ||
+ !isFinite(cSrc[0]) || !isFinite(cSrc[1]) ||
+ !isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
+ if (maxSubdivision > 0) {
+ needsSubdivision = true;
+ } else {
+ return;
+ }
+ }
+ }
+
+ if (maxSubdivision > 0) {
+ if (!needsSubdivision) {
+ var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2];
+ var centerSrc = this.transformInv_(center);
+
+ var dx;
+ if (wrapsX) {
+ var centerSrcEstimX =
+ (ol.math.modulo(aSrc[0], sourceWorldWidth) +
+ ol.math.modulo(cSrc[0], sourceWorldWidth)) / 2;
+ dx = centerSrcEstimX -
+ ol.math.modulo(centerSrc[0], sourceWorldWidth);
+ } else {
+ dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
+ }
+ var dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
+ var centerSrcErrorSquared = dx * dx + dy * dy;
+ needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
+ }
+ if (needsSubdivision) {
+ if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
+ // split horizontally (top & bottom)
+ var bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
+ var bcSrc = this.transformInv_(bc);
+ var da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
+ var daSrc = this.transformInv_(da);
+
+ this.addQuad_(
+ a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
+ this.addQuad_(
+ da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
+ } else {
+ // split vertically (left & right)
+ var ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
+ var abSrc = this.transformInv_(ab);
+ var cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
+ var cdSrc = this.transformInv_(cd);
+
+ this.addQuad_(
+ a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
+ this.addQuad_(
+ ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
+ }
+ return;
+ }
+ }
+
+ if (wrapsX) {
+ if (!this.canWrapXInSource_) {
+ return;
+ }
+ this.wrapsXInSource_ = true;
+ }
+
+ this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
+ this.addTriangle_(a, b, c, aSrc, bSrc, cSrc);
+};
+
+
+/**
+ * Calculates extent of the 'source' coordinates from all the triangles.
+ *
+ * @return {ol.Extent} Calculated extent.
+ */
+ol.reproj.Triangulation.prototype.calculateSourceExtent = function() {
+ var extent = ol.extent.createEmpty();
+
+ this.triangles_.forEach(function(triangle, i, arr) {
+ var src = triangle.source;
+ ol.extent.extendCoordinate(extent, src[0]);
+ ol.extent.extendCoordinate(extent, src[1]);
+ ol.extent.extendCoordinate(extent, src[2]);
+ });
+
+ return extent;
+};
+
+
+/**
+ * @return {Array.<ol.ReprojTriangle>} Array of the calculated triangles.
+ */
+ol.reproj.Triangulation.prototype.getTriangles = function() {
+ return this.triangles_;
+};
+
+goog.provide('ol.reproj.Image');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.ImageBase');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.reproj');
+goog.require('ol.reproj.Triangulation');
+
+
+/**
+ * @classdesc
+ * Class encapsulating single reprojected image.
+ * See {@link ol.source.Image}.
+ *
+ * @constructor
+ * @extends {ol.ImageBase}
+ * @param {ol.proj.Projection} sourceProj Source projection (of the data).
+ * @param {ol.proj.Projection} targetProj Target projection.
+ * @param {ol.Extent} targetExtent Target extent.
+ * @param {number} targetResolution Target resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.ReprojImageFunctionType} getImageFunction
+ * Function returning source images (extent, resolution, pixelRatio).
+ */
+ol.reproj.Image = function(sourceProj, targetProj,
+ targetExtent, targetResolution, pixelRatio, getImageFunction) {
+
+ /**
+ * @private
+ * @type {ol.proj.Projection}
+ */
+ this.targetProj_ = targetProj;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.maxSourceExtent_ = sourceProj.getExtent();
+ var maxTargetExtent = targetProj.getExtent();
+
+ var limitedTargetExtent = maxTargetExtent ?
+ ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent;
+
+ var targetCenter = ol.extent.getCenter(limitedTargetExtent);
+ var sourceResolution = ol.reproj.calculateSourceResolution(
+ sourceProj, targetProj, targetCenter, targetResolution);
+
+ var errorThresholdInPixels = ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD;
+
+ /**
+ * @private
+ * @type {!ol.reproj.Triangulation}
+ */
+ this.triangulation_ = new ol.reproj.Triangulation(
+ sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_,
+ sourceResolution * errorThresholdInPixels);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.targetResolution_ = targetResolution;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.targetExtent_ = targetExtent;
+
+ var sourceExtent = this.triangulation_.calculateSourceExtent();
+
+ /**
+ * @private
+ * @type {ol.ImageBase}
+ */
+ this.sourceImage_ =
+ getImageFunction(sourceExtent, sourceResolution, pixelRatio);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.sourcePixelRatio_ =
+ this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = null;
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.sourceListenerKey_ = null;
+
+
+ var state = ol.Image.State.LOADED;
+ var attributions = [];
+
+ if (this.sourceImage_) {
+ state = ol.Image.State.IDLE;
+ attributions = this.sourceImage_.getAttributions();
+ }
+
+ ol.ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_,
+ state, attributions);
+};
+ol.inherits(ol.reproj.Image, ol.ImageBase);
+
+
+/**
+ * @inheritDoc
+ */
+ol.reproj.Image.prototype.disposeInternal = function() {
+ if (this.state == ol.Image.State.LOADING) {
+ this.unlistenSource_();
+ }
+ ol.ImageBase.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.reproj.Image.prototype.getImage = function(opt_context) {
+ return this.canvas_;
+};
+
+
+/**
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.reproj.Image.prototype.getProjection = function() {
+ return this.targetProj_;
+};
+
+
+/**
+ * @private
+ */
+ol.reproj.Image.prototype.reproject_ = function() {
+ var sourceState = this.sourceImage_.getState();
+ if (sourceState == ol.Image.State.LOADED) {
+ var width = ol.extent.getWidth(this.targetExtent_) / this.targetResolution_;
+ var height =
+ ol.extent.getHeight(this.targetExtent_) / this.targetResolution_;
+
+ this.canvas_ = ol.reproj.render(width, height, this.sourcePixelRatio_,
+ this.sourceImage_.getResolution(), this.maxSourceExtent_,
+ this.targetResolution_, this.targetExtent_, this.triangulation_, [{
+ extent: this.sourceImage_.getExtent(),
+ image: this.sourceImage_.getImage()
+ }], 0);
+ }
+ this.state = sourceState;
+ this.changed();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.reproj.Image.prototype.load = function() {
+ if (this.state == ol.Image.State.IDLE) {
+ this.state = ol.Image.State.LOADING;
+ this.changed();
+
+ var sourceState = this.sourceImage_.getState();
+ if (sourceState == ol.Image.State.LOADED ||
+ sourceState == ol.Image.State.ERROR) {
+ this.reproject_();
+ } else {
+ this.sourceListenerKey_ = ol.events.listen(this.sourceImage_,
+ ol.events.EventType.CHANGE, function(e) {
+ var sourceState = this.sourceImage_.getState();
+ if (sourceState == ol.Image.State.LOADED ||
+ sourceState == ol.Image.State.ERROR) {
+ this.unlistenSource_();
+ this.reproject_();
+ }
+ }, this);
+ this.sourceImage_.load();
+ }
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.reproj.Image.prototype.unlistenSource_ = function() {
+ ol.DEBUG && console.assert(this.sourceListenerKey_,
+ 'this.sourceListenerKey_ should not be null');
+ ol.events.unlistenByKey(/** @type {!ol.EventsKey} */ (this.sourceListenerKey_));
+ this.sourceListenerKey_ = null;
+};
+
+goog.provide('ol.source.Source');
+
+goog.require('ol');
+goog.require('ol.Attribution');
+goog.require('ol.Object');
+goog.require('ol.proj');
+goog.require('ol.source.State');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for {@link ol.layer.Layer} sources.
+ *
+ * A generic `change` event is triggered when the state of the source changes.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {ol.SourceSourceOptions} options Source options.
+ * @api stable
+ */
+ol.source.Source = function(options) {
+
+ ol.Object.call(this);
+
+ /**
+ * @private
+ * @type {ol.proj.Projection}
+ */
+ this.projection_ = ol.proj.get(options.projection);
+
+ /**
+ * @private
+ * @type {Array.<ol.Attribution>}
+ */
+ this.attributions_ = ol.source.Source.toAttributionsArray_(options.attributions);
+
+ /**
+ * @private
+ * @type {string|olx.LogoOptions|undefined}
+ */
+ this.logo_ = options.logo;
+
+ /**
+ * @private
+ * @type {ol.source.State}
+ */
+ this.state_ = options.state !== undefined ?
+ options.state : ol.source.State.READY;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false;
+
+};
+ol.inherits(ol.source.Source, ol.Object);
+
+/**
+ * Turns various ways of defining an attribution to an array of `ol.Attributions`.
+ *
+ * @param {ol.AttributionLike|undefined}
+ * attributionLike The attributions as string, array of strings,
+ * `ol.Attribution`, array of `ol.Attribution` or undefined.
+ * @return {Array.<ol.Attribution>} The array of `ol.Attribution` or null if
+ * `undefined` was given.
+ */
+ol.source.Source.toAttributionsArray_ = function(attributionLike) {
+ if (typeof attributionLike === 'string') {
+ return [new ol.Attribution({html: attributionLike})];
+ } else if (attributionLike instanceof ol.Attribution) {
+ return [attributionLike];
+ } else if (Array.isArray(attributionLike)) {
+ var len = attributionLike.length;
+ var attributions = new Array(len);
+ for (var i = 0; i < len; i++) {
+ var item = attributionLike[i];
+ if (typeof item === 'string') {
+ attributions[i] = new ol.Attribution({html: item});
+ } else {
+ attributions[i] = item;
+ }
+ }
+ return attributions;
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @param {number} rotation Rotation.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @param {Object.<string, boolean>} skippedFeatureUids Skipped feature uids.
+ * @param {function((ol.Feature|ol.render.Feature)): T} callback Feature
+ * callback.
+ * @return {T|undefined} Callback result.
+ * @template T
+ */
+ol.source.Source.prototype.forEachFeatureAtCoordinate = ol.nullFunction;
+
+
+/**
+ * Get the attributions of the source.
+ * @return {Array.<ol.Attribution>} Attributions.
+ * @api stable
+ */
+ol.source.Source.prototype.getAttributions = function() {
+ return this.attributions_;
+};
+
+
+/**
+ * Get the logo of the source.
+ * @return {string|olx.LogoOptions|undefined} Logo.
+ * @api stable
+ */
+ol.source.Source.prototype.getLogo = function() {
+ return this.logo_;
+};
+
+
+/**
+ * Get the projection of the source.
+ * @return {ol.proj.Projection} Projection.
+ * @api
+ */
+ol.source.Source.prototype.getProjection = function() {
+ return this.projection_;
+};
+
+
+/**
+ * @abstract
+ * @return {Array.<number>|undefined} Resolutions.
+ */
+ol.source.Source.prototype.getResolutions = function() {};
+
+
+/**
+ * Get the state of the source, see {@link ol.source.State} for possible states.
+ * @return {ol.source.State} State.
+ * @api
+ */
+ol.source.Source.prototype.getState = function() {
+ return this.state_;
+};
+
+
+/**
+ * @return {boolean|undefined} Wrap X.
+ */
+ol.source.Source.prototype.getWrapX = function() {
+ return this.wrapX_;
+};
+
+
+/**
+ * Refreshes the source and finally dispatches a 'change' event.
+ * @api
+ */
+ol.source.Source.prototype.refresh = function() {
+ this.changed();
+};
+
+
+/**
+ * Set the attributions of the source.
+ * @param {ol.AttributionLike|undefined} attributions Attributions.
+ * Can be passed as `string`, `Array<string>`, `{@link ol.Attribution}`,
+ * `Array<{@link ol.Attribution}>` or `undefined`.
+ * @api
+ */
+ol.source.Source.prototype.setAttributions = function(attributions) {
+ this.attributions_ = ol.source.Source.toAttributionsArray_(attributions);
+ this.changed();
+};
+
+
+/**
+ * Set the logo of the source.
+ * @param {string|olx.LogoOptions|undefined} logo Logo.
+ */
+ol.source.Source.prototype.setLogo = function(logo) {
+ this.logo_ = logo;
+};
+
+
+/**
+ * Set the state of the source.
+ * @param {ol.source.State} state State.
+ * @protected
+ */
+ol.source.Source.prototype.setState = function(state) {
+ this.state_ = state;
+ this.changed();
+};
+
+goog.provide('ol.source.Image');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.array');
+goog.require('ol.events.Event');
+goog.require('ol.extent');
+goog.require('ol.proj');
+goog.require('ol.reproj.Image');
+goog.require('ol.source.Source');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for sources providing a single image.
+ *
+ * @constructor
+ * @extends {ol.source.Source}
+ * @param {ol.SourceImageOptions} options Single image source options.
+ * @api
+ */
+ol.source.Image = function(options) {
+
+ ol.source.Source.call(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ logo: options.logo,
+ projection: options.projection,
+ state: options.state
+ });
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.resolutions_ = options.resolutions !== undefined ?
+ options.resolutions : null;
+ ol.DEBUG && console.assert(!this.resolutions_ ||
+ ol.array.isSorted(this.resolutions_,
+ function(a, b) {
+ return b - a;
+ }, true), 'resolutions must be null or sorted in descending order');
+
+
+ /**
+ * @private
+ * @type {ol.reproj.Image}
+ */
+ this.reprojectedImage_ = null;
+
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.reprojectedRevision_ = 0;
+
+};
+ol.inherits(ol.source.Image, ol.source.Source);
+
+
+/**
+ * @return {Array.<number>} Resolutions.
+ */
+ol.source.Image.prototype.getResolutions = function() {
+ return this.resolutions_;
+};
+
+
+/**
+ * @protected
+ * @param {number} resolution Resolution.
+ * @return {number} Resolution.
+ */
+ol.source.Image.prototype.findNearestResolution = function(resolution) {
+ if (this.resolutions_) {
+ var idx = ol.array.linearFindNearest(this.resolutions_, resolution, 0);
+ resolution = this.resolutions_[idx];
+ }
+ return resolution;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {ol.ImageBase} Single image.
+ */
+ol.source.Image.prototype.getImage = function(extent, resolution, pixelRatio, projection) {
+ var sourceProjection = this.getProjection();
+ if (!ol.ENABLE_RASTER_REPROJECTION ||
+ !sourceProjection ||
+ !projection ||
+ ol.proj.equivalent(sourceProjection, projection)) {
+ if (sourceProjection) {
+ projection = sourceProjection;
+ }
+ return this.getImageInternal(extent, resolution, pixelRatio, projection);
+ } else {
+ if (this.reprojectedImage_) {
+ if (this.reprojectedRevision_ == this.getRevision() &&
+ ol.proj.equivalent(
+ this.reprojectedImage_.getProjection(), projection) &&
+ this.reprojectedImage_.getResolution() == resolution &&
+ this.reprojectedImage_.getPixelRatio() == pixelRatio &&
+ ol.extent.equals(this.reprojectedImage_.getExtent(), extent)) {
+ return this.reprojectedImage_;
+ }
+ this.reprojectedImage_.dispose();
+ this.reprojectedImage_ = null;
+ }
+
+ this.reprojectedImage_ = new ol.reproj.Image(
+ sourceProjection, projection, extent, resolution, pixelRatio,
+ function(extent, resolution, pixelRatio) {
+ return this.getImageInternal(extent, resolution,
+ pixelRatio, sourceProjection);
+ }.bind(this));
+ this.reprojectedRevision_ = this.getRevision();
+
+ return this.reprojectedImage_;
+ }
+};
+
+
+/**
+ * @abstract
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {ol.ImageBase} Single image.
+ * @protected
+ */
+ol.source.Image.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {};
+
+
+/**
+ * Handle image change events.
+ * @param {ol.events.Event} event Event.
+ * @protected
+ */
+ol.source.Image.prototype.handleImageChange = function(event) {
+ var image = /** @type {ol.Image} */ (event.target);
+ switch (image.getState()) {
+ case ol.Image.State.LOADING:
+ this.dispatchEvent(
+ new ol.source.Image.Event(ol.source.Image.EventType.IMAGELOADSTART,
+ image));
+ break;
+ case ol.Image.State.LOADED:
+ this.dispatchEvent(
+ new ol.source.Image.Event(ol.source.Image.EventType.IMAGELOADEND,
+ image));
+ break;
+ case ol.Image.State.ERROR:
+ this.dispatchEvent(
+ new ol.source.Image.Event(ol.source.Image.EventType.IMAGELOADERROR,
+ image));
+ break;
+ default:
+ // pass
+ }
+};
+
+
+/**
+ * Default image load function for image sources that use ol.Image image
+ * instances.
+ * @param {ol.Image} image Image.
+ * @param {string} src Source.
+ */
+ol.source.Image.defaultImageLoadFunction = function(image, src) {
+ image.getImage().src = src;
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.source.Image} instances are instances of this
+ * type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.source.ImageEvent}
+ * @param {string} type Type.
+ * @param {ol.Image} image The image.
+ */
+ol.source.Image.Event = function(type, image) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The image related to the event.
+ * @type {ol.Image}
+ * @api
+ */
+ this.image = image;
+
+};
+ol.inherits(ol.source.Image.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.source.Image.EventType = {
+
+ /**
+ * Triggered when an image starts loading.
+ * @event ol.source.Image.Event#imageloadstart
+ * @api
+ */
+ IMAGELOADSTART: 'imageloadstart',
+
+ /**
+ * Triggered when an image finishes loading.
+ * @event ol.source.Image.Event#imageloadend
+ * @api
+ */
+ IMAGELOADEND: 'imageloadend',
+
+ /**
+ * Triggered if image loading results in an error.
+ * @event ol.source.Image.Event#imageloaderror
+ * @api
+ */
+ IMAGELOADERROR: 'imageloaderror'
+
+};
+
+goog.provide('ol.source.ImageCanvas');
+
+goog.require('ol');
+goog.require('ol.ImageCanvas');
+goog.require('ol.extent');
+goog.require('ol.source.Image');
+
+
+/**
+ * @classdesc
+ * Base class for image sources where a canvas element is the image.
+ *
+ * @constructor
+ * @extends {ol.source.Image}
+ * @param {olx.source.ImageCanvasOptions} options Constructor options.
+ * @api
+ */
+ol.source.ImageCanvas = function(options) {
+
+ ol.source.Image.call(this, {
+ attributions: options.attributions,
+ logo: options.logo,
+ projection: options.projection,
+ resolutions: options.resolutions,
+ state: options.state
+ });
+
+ /**
+ * @private
+ * @type {ol.CanvasFunctionType}
+ */
+ this.canvasFunction_ = options.canvasFunction;
+
+ /**
+ * @private
+ * @type {ol.ImageCanvas}
+ */
+ this.canvas_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.ratio_ = options.ratio !== undefined ?
+ options.ratio : 1.5;
+
+};
+ol.inherits(ol.source.ImageCanvas, ol.source.Image);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageCanvas.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {
+ resolution = this.findNearestResolution(resolution);
+
+ var canvas = this.canvas_;
+ if (canvas &&
+ this.renderedRevision_ == this.getRevision() &&
+ canvas.getResolution() == resolution &&
+ canvas.getPixelRatio() == pixelRatio &&
+ ol.extent.containsExtent(canvas.getExtent(), extent)) {
+ return canvas;
+ }
+
+ extent = extent.slice();
+ ol.extent.scaleFromCenter(extent, this.ratio_);
+ var width = ol.extent.getWidth(extent) / resolution;
+ var height = ol.extent.getHeight(extent) / resolution;
+ var size = [width * pixelRatio, height * pixelRatio];
+
+ var canvasElement = this.canvasFunction_(
+ extent, resolution, pixelRatio, size, projection);
+ if (canvasElement) {
+ canvas = new ol.ImageCanvas(extent, resolution, pixelRatio,
+ this.getAttributions(), canvasElement);
+ }
+ this.canvas_ = canvas;
+ this.renderedRevision_ = this.getRevision();
+
+ return canvas;
+};
+
+goog.provide('ol.source.ImageVector');
+
+goog.require('ol');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.render.canvas.ReplayGroup');
+goog.require('ol.renderer.vector');
+goog.require('ol.source.ImageCanvas');
+goog.require('ol.style.Style');
+goog.require('ol.transform');
+
+
+/**
+ * @classdesc
+ * An image source whose images are canvas elements into which vector features
+ * read from a vector source (`ol.source.Vector`) are drawn. An
+ * `ol.source.ImageVector` object is to be used as the `source` of an image
+ * layer (`ol.layer.Image`). Image layers are rotated, scaled, and translated,
+ * as opposed to being re-rendered, during animations and interactions. So, like
+ * any other image layer, an image layer configured with an
+ * `ol.source.ImageVector` will exhibit this behaviour. This is in contrast to a
+ * vector layer, where vector features are re-drawn during animations and
+ * interactions.
+ *
+ * @constructor
+ * @extends {ol.source.ImageCanvas}
+ * @param {olx.source.ImageVectorOptions} options Options.
+ * @api
+ */
+ol.source.ImageVector = function(options) {
+
+ /**
+ * @private
+ * @type {ol.source.Vector}
+ */
+ this.source_ = options.source;
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.transform_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.canvasContext_ = ol.dom.createCanvasContext2D();
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.canvasSize_ = [0, 0];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderBuffer_ = options.renderBuffer == undefined ? 100 : options.renderBuffer;
+
+ /**
+ * @private
+ * @type {ol.render.canvas.ReplayGroup}
+ */
+ this.replayGroup_ = null;
+
+ ol.source.ImageCanvas.call(this, {
+ attributions: options.attributions,
+ canvasFunction: this.canvasFunctionInternal_.bind(this),
+ logo: options.logo,
+ projection: options.projection,
+ ratio: options.ratio,
+ resolutions: options.resolutions,
+ state: this.source_.getState()
+ });
+
+ /**
+ * User provided style.
+ * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction}
+ * @private
+ */
+ this.style_ = null;
+
+ /**
+ * Style function for use within the library.
+ * @type {ol.StyleFunction|undefined}
+ * @private
+ */
+ this.styleFunction_ = undefined;
+
+ this.setStyle(options.style);
+
+ ol.events.listen(this.source_, ol.events.EventType.CHANGE,
+ this.handleSourceChange_, this);
+
+};
+ol.inherits(ol.source.ImageVector, ol.source.ImageCanvas);
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Size} size Size.
+ * @param {ol.proj.Projection} projection Projection;
+ * @return {HTMLCanvasElement} Canvas element.
+ * @private
+ */
+ol.source.ImageVector.prototype.canvasFunctionInternal_ = function(extent, resolution, pixelRatio, size, projection) {
+
+ var replayGroup = new ol.render.canvas.ReplayGroup(
+ ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
+ resolution, this.source_.getOverlaps(), this.renderBuffer_);
+
+ this.source_.loadFeatures(extent, resolution, projection);
+
+ var loading = false;
+ this.source_.forEachFeatureInExtent(extent,
+ /**
+ * @param {ol.Feature} feature Feature.
+ */
+ function(feature) {
+ loading = loading ||
+ this.renderFeature_(feature, resolution, pixelRatio, replayGroup);
+ }, this);
+ replayGroup.finish();
+
+ if (loading) {
+ return null;
+ }
+
+ if (this.canvasSize_[0] != size[0] || this.canvasSize_[1] != size[1]) {
+ this.canvasContext_.canvas.width = size[0];
+ this.canvasContext_.canvas.height = size[1];
+ this.canvasSize_[0] = size[0];
+ this.canvasSize_[1] = size[1];
+ } else {
+ this.canvasContext_.clearRect(0, 0, size[0], size[1]);
+ }
+
+ var transform = this.getTransform_(ol.extent.getCenter(extent),
+ resolution, pixelRatio, size);
+ replayGroup.replay(this.canvasContext_, pixelRatio, transform, 0, {});
+
+ this.replayGroup_ = replayGroup;
+
+ return this.canvasContext_.canvas;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageVector.prototype.forEachFeatureAtCoordinate = function(
+ coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, callback) {
+ if (!this.replayGroup_) {
+ return undefined;
+ } else {
+ /** @type {Object.<string, boolean>} */
+ var features = {};
+ return this.replayGroup_.forEachFeatureAtCoordinate(
+ coordinate, resolution, 0, hitTolerance, skippedFeatureUids,
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ var key = ol.getUid(feature).toString();
+ if (!(key in features)) {
+ features[key] = true;
+ return callback(feature);
+ }
+ });
+ }
+};
+
+
+/**
+ * Get a reference to the wrapped source.
+ * @return {ol.source.Vector} Source.
+ * @api
+ */
+ol.source.ImageVector.prototype.getSource = function() {
+ return this.source_;
+};
+
+
+/**
+ * Get the style for features. This returns whatever was passed to the `style`
+ * option at construction or to the `setStyle` method.
+ * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction}
+ * Layer style.
+ * @api stable
+ */
+ol.source.ImageVector.prototype.getStyle = function() {
+ return this.style_;
+};
+
+
+/**
+ * Get the style function.
+ * @return {ol.StyleFunction|undefined} Layer style function.
+ * @api stable
+ */
+ol.source.ImageVector.prototype.getStyleFunction = function() {
+ return this.styleFunction_;
+};
+
+
+/**
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Size} size Size.
+ * @return {!ol.Transform} Transform.
+ * @private
+ */
+ol.source.ImageVector.prototype.getTransform_ = function(center, resolution, pixelRatio, size) {
+ var dx1 = size[0] / 2;
+ var dy1 = size[1] / 2;
+ var sx = pixelRatio / resolution;
+ var sy = -sx;
+ var dx2 = -center[0];
+ var dy2 = -center[1];
+
+ return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, 0, dx2, dy2);
+};
+
+
+/**
+ * Handle changes in image style state.
+ * @param {ol.events.Event} event Image style change event.
+ * @private
+ */
+ol.source.ImageVector.prototype.handleImageChange_ = function(event) {
+ this.changed();
+};
+
+
+/**
+ * @private
+ */
+ol.source.ImageVector.prototype.handleSourceChange_ = function() {
+ // setState will trigger a CHANGE event, so we always rely
+ // change events by calling setState.
+ this.setState(this.source_.getState());
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
+ * @return {boolean} `true` if an image is loading.
+ * @private
+ */
+ol.source.ImageVector.prototype.renderFeature_ = function(feature, resolution, pixelRatio, replayGroup) {
+ var styles;
+ var styleFunction = feature.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction.call(feature, resolution);
+ } else if (this.styleFunction_) {
+ styles = this.styleFunction_(feature, resolution);
+ }
+ if (!styles) {
+ return false;
+ }
+ var i, ii, loading = false;
+ if (!Array.isArray(styles)) {
+ styles = [styles];
+ }
+ for (i = 0, ii = styles.length; i < ii; ++i) {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles[i],
+ ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
+ this.handleImageChange_, this) || loading;
+ }
+ return loading;
+};
+
+
+/**
+ * Set the style for features. This can be a single style object, an array
+ * of styles, or a function that takes a feature and resolution and returns
+ * an array of styles. If it is `undefined` the default style is used. If
+ * it is `null` the layer has no style (a `null` style), so only features
+ * that have their own styles will be rendered in the layer. See
+ * {@link ol.style} for information on the default style.
+ * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|undefined}
+ * style Layer style.
+ * @api stable
+ */
+ol.source.ImageVector.prototype.setStyle = function(style) {
+ this.style_ = style !== undefined ? style : ol.style.Style.defaultFunction;
+ this.styleFunction_ = !style ?
+ undefined : ol.style.Style.createFunction(this.style_);
+ this.changed();
+};
+
+goog.provide('ol.renderer.webgl.ImageLayer');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.dom');
+goog.require('ol.extent');
+goog.require('ol.functions');
+goog.require('ol.proj');
+goog.require('ol.renderer.webgl.Layer');
+goog.require('ol.source.ImageVector');
+goog.require('ol.transform');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Context');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.webgl.Layer}
+ * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
+ * @param {ol.layer.Image} imageLayer Tile layer.
+ */
+ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) {
+
+ ol.renderer.webgl.Layer.call(this, mapRenderer, imageLayer);
+
+ /**
+ * The last rendered image.
+ * @private
+ * @type {?ol.ImageBase}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.hitCanvasContext_ = null;
+
+ /**
+ * @private
+ * @type {?ol.Transform}
+ */
+ this.hitTransformationMatrix_ = null;
+
+};
+ol.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer);
+
+
+/**
+ * @param {ol.ImageBase} image Image.
+ * @private
+ * @return {WebGLTexture} Texture.
+ */
+ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) {
+
+ // We meet the conditions to work with non-power of two textures.
+ // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support
+ // http://learningwebgl.com/blog/?p=2101
+
+ var imageElement = image.getImage();
+ var gl = this.mapRenderer.getGL();
+
+ return ol.webgl.Context.createTexture(
+ gl, imageElement, ol.webgl.CLAMP_TO_EDGE, ol.webgl.CLAMP_TO_EDGE);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) {
+ var layer = this.getLayer();
+ var source = layer.getSource();
+ var resolution = frameState.viewState.resolution;
+ var rotation = frameState.viewState.rotation;
+ var skippedFeatureUids = frameState.skippedFeatureUids;
+ return source.forEachFeatureAtCoordinate(
+ coordinate, resolution, rotation, hitTolerance, skippedFeatureUids,
+
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ return callback.call(thisArg, feature, layer);
+ });
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.prepareFrame = function(frameState, layerState, context) {
+
+ var gl = this.mapRenderer.getGL();
+
+ var pixelRatio = frameState.pixelRatio;
+ var viewState = frameState.viewState;
+ var viewCenter = viewState.center;
+ var viewResolution = viewState.resolution;
+ var viewRotation = viewState.rotation;
+
+ var image = this.image_;
+ var texture = this.texture;
+ var imageLayer = /** @type {ol.layer.Image} */ (this.getLayer());
+ var imageSource = imageLayer.getSource();
+
+ var hints = frameState.viewHints;
+
+ var renderedExtent = frameState.extent;
+ if (layerState.extent !== undefined) {
+ renderedExtent = ol.extent.getIntersection(
+ renderedExtent, layerState.extent);
+ }
+ if (!hints[ol.View.Hint.ANIMATING] && !hints[ol.View.Hint.INTERACTING] &&
+ !ol.extent.isEmpty(renderedExtent)) {
+ var projection = viewState.projection;
+ if (!ol.ENABLE_RASTER_REPROJECTION) {
+ var sourceProjection = imageSource.getProjection();
+ if (sourceProjection) {
+ ol.DEBUG && console.assert(ol.proj.equivalent(projection, sourceProjection),
+ 'projection and sourceProjection are equivalent');
+ projection = sourceProjection;
+ }
+ }
+ var image_ = imageSource.getImage(renderedExtent, viewResolution,
+ pixelRatio, projection);
+ if (image_) {
+ var loaded = this.loadImage(image_);
+ if (loaded) {
+ image = image_;
+ texture = this.createTexture_(image_);
+ if (this.texture) {
+ /**
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLTexture} texture Texture.
+ */
+ var postRenderFunction = function(gl, texture) {
+ if (!gl.isContextLost()) {
+ gl.deleteTexture(texture);
+ }
+ }.bind(null, gl, this.texture);
+ frameState.postRenderFunctions.push(
+ /** @type {ol.PostRenderFunction} */ (postRenderFunction)
+ );
+ }
+ }
+ }
+ }
+
+ if (image) {
+ ol.DEBUG && console.assert(texture, 'texture is truthy');
+
+ var canvas = this.mapRenderer.getContext().getCanvas();
+
+ this.updateProjectionMatrix_(canvas.width, canvas.height,
+ pixelRatio, viewCenter, viewResolution, viewRotation,
+ image.getExtent());
+ this.hitTransformationMatrix_ = null;
+
+ // Translate and scale to flip the Y coord.
+ var texCoordMatrix = this.texCoordMatrix;
+ ol.transform.reset(texCoordMatrix);
+ ol.transform.scale(texCoordMatrix, 1, -1);
+ ol.transform.translate(texCoordMatrix, 0, -1);
+
+ this.image_ = image;
+ this.texture = texture;
+
+ this.updateAttributions(frameState.attributions, image.getAttributions());
+ this.updateLogos(frameState, imageSource);
+ }
+
+ return !!image;
+};
+
+
+/**
+ * @param {number} canvasWidth Canvas width.
+ * @param {number} canvasHeight Canvas height.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.Coordinate} viewCenter View center.
+ * @param {number} viewResolution View resolution.
+ * @param {number} viewRotation View rotation.
+ * @param {ol.Extent} imageExtent Image extent.
+ * @private
+ */
+ol.renderer.webgl.ImageLayer.prototype.updateProjectionMatrix_ = function(canvasWidth, canvasHeight, pixelRatio,
+ viewCenter, viewResolution, viewRotation, imageExtent) {
+
+ var canvasExtentWidth = canvasWidth * viewResolution;
+ var canvasExtentHeight = canvasHeight * viewResolution;
+
+ var projectionMatrix = this.projectionMatrix;
+ ol.transform.reset(projectionMatrix);
+ ol.transform.scale(projectionMatrix,
+ pixelRatio * 2 / canvasExtentWidth,
+ pixelRatio * 2 / canvasExtentHeight);
+ ol.transform.rotate(projectionMatrix, -viewRotation);
+ ol.transform.translate(projectionMatrix,
+ imageExtent[0] - viewCenter[0],
+ imageExtent[1] - viewCenter[1]);
+ ol.transform.scale(projectionMatrix,
+ (imageExtent[2] - imageExtent[0]) / 2,
+ (imageExtent[3] - imageExtent[1]) / 2);
+ ol.transform.translate(projectionMatrix, 1, 1);
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) {
+ var hasFeature = this.forEachFeatureAtCoordinate(
+ coordinate, frameState, 0, ol.functions.TRUE, this);
+ return hasFeature !== undefined;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {
+ if (!this.image_ || !this.image_.getImage()) {
+ return undefined;
+ }
+
+ if (this.getLayer().getSource() instanceof ol.source.ImageVector) {
+ // for ImageVector sources use the original hit-detection logic,
+ // so that for example also transparent polygons are detected
+ var coordinate = ol.transform.apply(
+ frameState.pixelToCoordinateTransform, pixel.slice());
+ var hasFeature = this.forEachFeatureAtCoordinate(
+ coordinate, frameState, 0, ol.functions.TRUE, this);
+
+ if (hasFeature) {
+ return callback.call(thisArg, this.getLayer(), null);
+ } else {
+ return undefined;
+ }
+ } else {
+ var imageSize =
+ [this.image_.getImage().width, this.image_.getImage().height];
+
+ if (!this.hitTransformationMatrix_) {
+ this.hitTransformationMatrix_ = this.getHitTransformationMatrix_(
+ frameState.size, imageSize);
+ }
+
+ var pixelOnFrameBuffer = ol.transform.apply(
+ this.hitTransformationMatrix_, pixel.slice());
+
+ if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] ||
+ pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) {
+ // outside the image, no need to check
+ return undefined;
+ }
+
+ if (!this.hitCanvasContext_) {
+ this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1);
+ }
+
+ this.hitCanvasContext_.clearRect(0, 0, 1, 1);
+ this.hitCanvasContext_.drawImage(this.image_.getImage(),
+ pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1);
+
+ var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data;
+ if (imageData[3] > 0) {
+ return callback.call(thisArg, this.getLayer(), imageData);
+ } else {
+ return undefined;
+ }
+ }
+};
+
+
+/**
+ * The transformation matrix to get the pixel on the image for a
+ * pixel on the map.
+ * @param {ol.Size} mapSize The map size.
+ * @param {ol.Size} imageSize The image size.
+ * @return {ol.Transform} The transformation matrix.
+ * @private
+ */
+ol.renderer.webgl.ImageLayer.prototype.getHitTransformationMatrix_ = function(mapSize, imageSize) {
+ // the first matrix takes a map pixel, flips the y-axis and scales to
+ // a range between -1 ... 1
+ var mapCoordTransform = ol.transform.create();
+ ol.transform.translate(mapCoordTransform, -1, -1);
+ ol.transform.scale(mapCoordTransform, 2 / mapSize[0], 2 / mapSize[1]);
+ ol.transform.translate(mapCoordTransform, 0, mapSize[1]);
+ ol.transform.scale(mapCoordTransform, 1, -1);
+
+ // the second matrix is the inverse of the projection matrix used in the
+ // shader for drawing
+ var projectionMatrixInv = ol.transform.invert(this.projectionMatrix.slice());
+
+ // the third matrix scales to the image dimensions and flips the y-axis again
+ var transform = ol.transform.create();
+ ol.transform.translate(transform, 0, imageSize[1]);
+ ol.transform.scale(transform, 1, -1);
+ ol.transform.scale(transform, imageSize[0] / 2, imageSize[1] / 2);
+ ol.transform.translate(transform, 1, 1);
+
+ ol.transform.multiply(transform, projectionMatrixInv);
+ ol.transform.multiply(transform, mapCoordTransform);
+
+ return transform;
+};
+
+// This file is automatically generated, do not edit
+goog.provide('ol.renderer.webgl.tilelayershader');
+
+goog.require('ol');
+goog.require('ol.webgl.Fragment');
+goog.require('ol.webgl.Vertex');
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Fragment}
+ * @struct
+ */
+ol.renderer.webgl.tilelayershader.Fragment = function() {
+ ol.webgl.Fragment.call(this, ol.renderer.webgl.tilelayershader.Fragment.SOURCE);
+};
+ol.inherits(ol.renderer.webgl.tilelayershader.Fragment, ol.webgl.Fragment);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.tilelayershader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform sampler2D u_texture;\n\nvoid main(void) {\n gl_FragColor = texture2D(u_texture, v_texCoord);\n}\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.tilelayershader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.tilelayershader.Fragment.SOURCE = ol.DEBUG ?
+ ol.renderer.webgl.tilelayershader.Fragment.DEBUG_SOURCE :
+ ol.renderer.webgl.tilelayershader.Fragment.OPTIMIZED_SOURCE;
+
+
+ol.renderer.webgl.tilelayershader.fragment = new ol.renderer.webgl.tilelayershader.Fragment();
+
+
+/**
+ * @constructor
+ * @extends {ol.webgl.Vertex}
+ * @struct
+ */
+ol.renderer.webgl.tilelayershader.Vertex = function() {
+ ol.webgl.Vertex.call(this, ol.renderer.webgl.tilelayershader.Vertex.SOURCE);
+};
+ol.inherits(ol.renderer.webgl.tilelayershader.Vertex, ol.webgl.Vertex);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.tilelayershader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nuniform vec4 u_tileOffset;\n\nvoid main(void) {\n gl_Position = vec4(a_position * u_tileOffset.xy + u_tileOffset.zw, 0., 1.);\n v_texCoord = a_texCoord;\n}\n\n\n';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.tilelayershader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.tilelayershader.Vertex.SOURCE = ol.DEBUG ?
+ ol.renderer.webgl.tilelayershader.Vertex.DEBUG_SOURCE :
+ ol.renderer.webgl.tilelayershader.Vertex.OPTIMIZED_SOURCE;
+
+
+ol.renderer.webgl.tilelayershader.vertex = new ol.renderer.webgl.tilelayershader.Vertex();
+
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ * @struct
+ */
+ol.renderer.webgl.tilelayershader.Locations = function(gl, program) {
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_texture = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_texture' : 'e');
+
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_tileOffset = gl.getUniformLocation(
+ program, ol.DEBUG ? 'u_tileOffset' : 'd');
+
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_position' : 'b');
+
+ /**
+ * @type {number}
+ */
+ this.a_texCoord = gl.getAttribLocation(
+ program, ol.DEBUG ? 'a_texCoord' : 'c');
+};
+
+// FIXME large resolutions lead to too large framebuffers :-(
+// FIXME animated shaders! check in redraw
+
+goog.provide('ol.renderer.webgl.TileLayer');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.TileRange');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.math');
+goog.require('ol.renderer.webgl.Layer');
+goog.require('ol.renderer.webgl.tilelayershader');
+goog.require('ol.size');
+goog.require('ol.transform');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Buffer');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.webgl.Layer}
+ * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
+ * @param {ol.layer.Tile} tileLayer Tile layer.
+ */
+ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
+
+ ol.renderer.webgl.Layer.call(this, mapRenderer, tileLayer);
+
+ /**
+ * @private
+ * @type {ol.webgl.Fragment}
+ */
+ this.fragmentShader_ = ol.renderer.webgl.tilelayershader.fragment;
+
+ /**
+ * @private
+ * @type {ol.webgl.Vertex}
+ */
+ this.vertexShader_ = ol.renderer.webgl.tilelayershader.vertex;
+
+ /**
+ * @private
+ * @type {ol.renderer.webgl.tilelayershader.Locations}
+ */
+ this.locations_ = null;
+
+ /**
+ * @private
+ * @type {ol.webgl.Buffer}
+ */
+ this.renderArrayBuffer_ = new ol.webgl.Buffer([
+ 0, 0, 0, 1,
+ 1, 0, 1, 1,
+ 0, 1, 0, 0,
+ 1, 1, 1, 0
+ ]);
+
+ /**
+ * @private
+ * @type {ol.TileRange}
+ */
+ this.renderedTileRange_ = null;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.renderedFramebufferExtent_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = -1;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.tmpSize_ = [0, 0];
+
+};
+ol.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
+ var context = this.mapRenderer.getContext();
+ context.deleteBuffer(this.renderArrayBuffer_);
+ ol.renderer.webgl.Layer.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * Create a function that adds loaded tiles to the tile lookup.
+ * @param {ol.source.Tile} source Tile source.
+ * @param {ol.proj.Projection} projection Projection of the tiles.
+ * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
+ * tiles by zoom level.
+ * @return {function(number, ol.TileRange):boolean} A function that can be
+ * called with a zoom level and a tile range to add loaded tiles to the
+ * lookup.
+ * @protected
+ */
+ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder = function(source, projection, tiles) {
+ var mapRenderer = this.mapRenderer;
+
+ return (
+ /**
+ * @param {number} zoom Zoom level.
+ * @param {ol.TileRange} tileRange Tile range.
+ * @return {boolean} The tile range is fully loaded.
+ */
+ function(zoom, tileRange) {
+ function callback(tile) {
+ var loaded = mapRenderer.isTileTextureLoaded(tile);
+ if (loaded) {
+ if (!tiles[zoom]) {
+ tiles[zoom] = {};
+ }
+ tiles[zoom][tile.tileCoord.toString()] = tile;
+ }
+ return loaded;
+ }
+ return source.forEachLoadedTile(projection, zoom, tileRange, callback);
+ });
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
+ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost.call(this);
+ this.locations_ = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.TileLayer.prototype.prepareFrame = function(frameState, layerState, context) {
+
+ var mapRenderer = this.mapRenderer;
+ var gl = context.getGL();
+
+ var viewState = frameState.viewState;
+ var projection = viewState.projection;
+
+ var tileLayer = /** @type {ol.layer.Tile} */ (this.getLayer());
+ var tileSource = tileLayer.getSource();
+ var tileGrid = tileSource.getTileGridForProjection(projection);
+ var z = tileGrid.getZForResolution(viewState.resolution);
+ var tileResolution = tileGrid.getResolution(z);
+
+ var tilePixelSize =
+ tileSource.getTilePixelSize(z, frameState.pixelRatio, projection);
+ var pixelRatio = tilePixelSize[0] /
+ ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize_)[0];
+ var tilePixelResolution = tileResolution / pixelRatio;
+ var tileGutter = tileSource.getTilePixelRatio(pixelRatio) * tileSource.getGutter(projection);
+
+ var center = viewState.center;
+ var extent = frameState.extent;
+ var tileRange = tileGrid.getTileRangeForExtentAndResolution(
+ extent, tileResolution);
+
+ var framebufferExtent;
+ if (this.renderedTileRange_ &&
+ this.renderedTileRange_.equals(tileRange) &&
+ this.renderedRevision_ == tileSource.getRevision()) {
+ framebufferExtent = this.renderedFramebufferExtent_;
+ } else {
+
+ var tileRangeSize = tileRange.getSize();
+
+ var maxDimension = Math.max(
+ tileRangeSize[0] * tilePixelSize[0],
+ tileRangeSize[1] * tilePixelSize[1]);
+ var framebufferDimension = ol.math.roundUpToPowerOfTwo(maxDimension);
+ var framebufferExtentDimension = tilePixelResolution * framebufferDimension;
+ var origin = tileGrid.getOrigin(z);
+ var minX = origin[0] +
+ tileRange.minX * tilePixelSize[0] * tilePixelResolution;
+ var minY = origin[1] +
+ tileRange.minY * tilePixelSize[1] * tilePixelResolution;
+ framebufferExtent = [
+ minX, minY,
+ minX + framebufferExtentDimension, minY + framebufferExtentDimension
+ ];
+
+ this.bindFramebuffer(frameState, framebufferDimension);
+ gl.viewport(0, 0, framebufferDimension, framebufferDimension);
+
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(ol.webgl.COLOR_BUFFER_BIT);
+ gl.disable(ol.webgl.BLEND);
+
+ var program = context.getProgram(this.fragmentShader_, this.vertexShader_);
+ context.useProgram(program);
+ if (!this.locations_) {
+ this.locations_ =
+ new ol.renderer.webgl.tilelayershader.Locations(gl, program);
+ }
+
+ context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.renderArrayBuffer_);
+ gl.enableVertexAttribArray(this.locations_.a_position);
+ gl.vertexAttribPointer(
+ this.locations_.a_position, 2, ol.webgl.FLOAT, false, 16, 0);
+ gl.enableVertexAttribArray(this.locations_.a_texCoord);
+ gl.vertexAttribPointer(
+ this.locations_.a_texCoord, 2, ol.webgl.FLOAT, false, 16, 8);
+ gl.uniform1i(this.locations_.u_texture, 0);
+
+ /**
+ * @type {Object.<number, Object.<string, ol.Tile>>}
+ */
+ var tilesToDrawByZ = {};
+ tilesToDrawByZ[z] = {};
+
+ var findLoadedTiles = this.createLoadedTileFinder(
+ tileSource, projection, tilesToDrawByZ);
+
+ var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
+ var allTilesLoaded = true;
+ var tmpExtent = ol.extent.createEmpty();
+ var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
+ var childTileRange, drawable, fullyLoaded, tile, tileState;
+ var x, y, tileExtent;
+ for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
+ for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
+
+ tile = tileSource.getTile(z, x, y, pixelRatio, projection);
+ if (layerState.extent !== undefined) {
+ // ignore tiles outside layer extent
+ tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent);
+ if (!ol.extent.intersects(tileExtent, layerState.extent)) {
+ continue;
+ }
+ }
+ tileState = tile.getState();
+ drawable = tileState == ol.Tile.State.LOADED ||
+ tileState == ol.Tile.State.EMPTY ||
+ tileState == ol.Tile.State.ERROR && !useInterimTilesOnError;
+ if (!drawable) {
+ tile = tile.getInterimTile();
+ }
+ tileState = tile.getState();
+ if (tileState == ol.Tile.State.LOADED) {
+ if (mapRenderer.isTileTextureLoaded(tile)) {
+ tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
+ continue;
+ }
+ } else if (tileState == ol.Tile.State.EMPTY ||
+ (tileState == ol.Tile.State.ERROR &&
+ !useInterimTilesOnError)) {
+ continue;
+ }
+
+ allTilesLoaded = false;
+ fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
+ tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
+ if (!fullyLoaded) {
+ childTileRange = tileGrid.getTileCoordChildTileRange(
+ tile.tileCoord, tmpTileRange, tmpExtent);
+ if (childTileRange) {
+ findLoadedTiles(z + 1, childTileRange);
+ }
+ }
+
+ }
+
+ }
+
+ /** @type {Array.<number>} */
+ var zs = Object.keys(tilesToDrawByZ).map(Number);
+ zs.sort(ol.array.numberSafeCompareFunction);
+ var u_tileOffset = new Float32Array(4);
+ var i, ii, tileKey, tilesToDraw;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ tilesToDraw = tilesToDrawByZ[zs[i]];
+ for (tileKey in tilesToDraw) {
+ tile = tilesToDraw[tileKey];
+ tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent);
+ u_tileOffset[0] = 2 * (tileExtent[2] - tileExtent[0]) /
+ framebufferExtentDimension;
+ u_tileOffset[1] = 2 * (tileExtent[3] - tileExtent[1]) /
+ framebufferExtentDimension;
+ u_tileOffset[2] = 2 * (tileExtent[0] - framebufferExtent[0]) /
+ framebufferExtentDimension - 1;
+ u_tileOffset[3] = 2 * (tileExtent[1] - framebufferExtent[1]) /
+ framebufferExtentDimension - 1;
+ gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset);
+ mapRenderer.bindTileTexture(tile, tilePixelSize,
+ tileGutter * pixelRatio, ol.webgl.LINEAR, ol.webgl.LINEAR);
+ gl.drawArrays(ol.webgl.TRIANGLE_STRIP, 0, 4);
+ }
+ }
+
+ if (allTilesLoaded) {
+ this.renderedTileRange_ = tileRange;
+ this.renderedFramebufferExtent_ = framebufferExtent;
+ this.renderedRevision_ = tileSource.getRevision();
+ } else {
+ this.renderedTileRange_ = null;
+ this.renderedFramebufferExtent_ = null;
+ this.renderedRevision_ = -1;
+ frameState.animate = true;
+ }
+
+ }
+
+ this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
+ var tileTextureQueue = mapRenderer.getTileTextureQueue();
+ this.manageTilePyramid(
+ frameState, tileSource, tileGrid, pixelRatio, projection, extent, z,
+ tileLayer.getPreload(),
+ /**
+ * @param {ol.Tile} tile Tile.
+ */
+ function(tile) {
+ if (tile.getState() == ol.Tile.State.LOADED &&
+ !mapRenderer.isTileTextureLoaded(tile) &&
+ !tileTextureQueue.isKeyQueued(tile.getKey())) {
+ tileTextureQueue.enqueue([
+ tile,
+ tileGrid.getTileCoordCenter(tile.tileCoord),
+ tileGrid.getResolution(tile.tileCoord[0]),
+ tilePixelSize, tileGutter * pixelRatio
+ ]);
+ }
+ }, this);
+ this.scheduleExpireCache(frameState, tileSource);
+ this.updateLogos(frameState, tileSource);
+
+ var texCoordMatrix = this.texCoordMatrix;
+ ol.transform.reset(texCoordMatrix);
+ ol.transform.translate(texCoordMatrix,
+ (Math.round(center[0] / tileResolution) * tileResolution - framebufferExtent[0]) /
+ (framebufferExtent[2] - framebufferExtent[0]),
+ (Math.round(center[1] / tileResolution) * tileResolution - framebufferExtent[1]) /
+ (framebufferExtent[3] - framebufferExtent[1]));
+ if (viewState.rotation !== 0) {
+ ol.transform.rotate(texCoordMatrix, viewState.rotation);
+ }
+ ol.transform.scale(texCoordMatrix,
+ frameState.size[0] * viewState.resolution /
+ (framebufferExtent[2] - framebufferExtent[0]),
+ frameState.size[1] * viewState.resolution /
+ (framebufferExtent[3] - framebufferExtent[1]));
+ ol.transform.translate(texCoordMatrix, -0.5, -0.5);
+
+ return true;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.TileLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {
+ if (!this.framebuffer) {
+ return undefined;
+ }
+
+ var pixelOnMapScaled = [
+ pixel[0] / frameState.size[0],
+ (frameState.size[1] - pixel[1]) / frameState.size[1]];
+
+ var pixelOnFrameBufferScaled = ol.transform.apply(
+ this.texCoordMatrix, pixelOnMapScaled.slice());
+ var pixelOnFrameBuffer = [
+ pixelOnFrameBufferScaled[0] * this.framebufferDimension,
+ pixelOnFrameBufferScaled[1] * this.framebufferDimension];
+
+ var gl = this.mapRenderer.getContext().getGL();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+ var imageData = new Uint8Array(4);
+ gl.readPixels(pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1,
+ gl.RGBA, gl.UNSIGNED_BYTE, imageData);
+
+ if (imageData[3] > 0) {
+ return callback.call(thisArg, this.getLayer(), imageData);
+ } else {
+ return undefined;
+ }
+};
+
+goog.provide('ol.renderer.webgl.VectorLayer');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.extent');
+goog.require('ol.render.webgl.ReplayGroup');
+goog.require('ol.renderer.vector');
+goog.require('ol.renderer.webgl.Layer');
+goog.require('ol.transform');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.webgl.Layer}
+ * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
+ * @param {ol.layer.Vector} vectorLayer Vector layer.
+ */
+ol.renderer.webgl.VectorLayer = function(mapRenderer, vectorLayer) {
+
+ ol.renderer.webgl.Layer.call(this, mapRenderer, vectorLayer);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.dirty_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedResolution_ = NaN;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.renderedExtent_ = ol.extent.createEmpty();
+
+ /**
+ * @private
+ * @type {function(ol.Feature, ol.Feature): number|null}
+ */
+ this.renderedRenderOrder_ = null;
+
+ /**
+ * @private
+ * @type {ol.render.webgl.ReplayGroup}
+ */
+ this.replayGroup_ = null;
+
+ /**
+ * The last layer state.
+ * @private
+ * @type {?ol.LayerState}
+ */
+ this.layerState_ = null;
+
+};
+ol.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) {
+ this.layerState_ = layerState;
+ var viewState = frameState.viewState;
+ var replayGroup = this.replayGroup_;
+ var size = frameState.size;
+ var pixelRatio = frameState.pixelRatio;
+ var gl = this.mapRenderer.getGL();
+ if (replayGroup && !replayGroup.isEmpty()) {
+ gl.enable(gl.SCISSOR_TEST);
+ gl.scissor(0, 0, size[0] * pixelRatio, size[1] * pixelRatio);
+ replayGroup.replay(context,
+ viewState.center, viewState.resolution, viewState.rotation,
+ size, pixelRatio, layerState.opacity,
+ layerState.managed ? frameState.skippedFeatureUids : {});
+ gl.disable(gl.SCISSOR_TEST);
+ }
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer.prototype.disposeInternal = function() {
+ var replayGroup = this.replayGroup_;
+ if (replayGroup) {
+ var context = this.mapRenderer.getContext();
+ replayGroup.getDeleteResourcesFunction(context)();
+ this.replayGroup_ = null;
+ }
+ ol.renderer.webgl.Layer.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) {
+ if (!this.replayGroup_ || !this.layerState_) {
+ return undefined;
+ } else {
+ var context = this.mapRenderer.getContext();
+ var viewState = frameState.viewState;
+ var layer = this.getLayer();
+ var layerState = this.layerState_;
+ /** @type {Object.<string, boolean>} */
+ var features = {};
+ return this.replayGroup_.forEachFeatureAtCoordinate(coordinate,
+ context, viewState.center, viewState.resolution, viewState.rotation,
+ frameState.size, frameState.pixelRatio, layerState.opacity,
+ {},
+ /**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @return {?} Callback result.
+ */
+ function(feature) {
+ var key = ol.getUid(feature).toString();
+ if (!(key in features)) {
+ features[key] = true;
+ return callback.call(thisArg, feature, layer);
+ }
+ });
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) {
+ if (!this.replayGroup_ || !this.layerState_) {
+ return false;
+ } else {
+ var context = this.mapRenderer.getContext();
+ var viewState = frameState.viewState;
+ var layerState = this.layerState_;
+ return this.replayGroup_.hasFeatureAtCoordinate(coordinate,
+ context, viewState.center, viewState.resolution, viewState.rotation,
+ frameState.size, frameState.pixelRatio, layerState.opacity,
+ frameState.skippedFeatureUids);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {
+ var coordinate = ol.transform.apply(
+ frameState.pixelToCoordinateTransform, pixel.slice());
+ var hasFeature = this.hasFeatureAtCoordinate(coordinate, frameState);
+
+ if (hasFeature) {
+ return callback.call(thisArg, this.getLayer(), null);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * Handle changes in image style state.
+ * @param {ol.events.Event} event Image style change event.
+ * @private
+ */
+ol.renderer.webgl.VectorLayer.prototype.handleStyleImageChange_ = function(event) {
+ this.renderIfReadyAndVisible();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer.prototype.prepareFrame = function(frameState, layerState, context) {
+
+ var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
+ var vectorSource = vectorLayer.getSource();
+
+ this.updateAttributions(
+ frameState.attributions, vectorSource.getAttributions());
+ this.updateLogos(frameState, vectorSource);
+
+ var animating = frameState.viewHints[ol.View.Hint.ANIMATING];
+ var interacting = frameState.viewHints[ol.View.Hint.INTERACTING];
+ var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating();
+ var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting();
+
+ if (!this.dirty_ && (!updateWhileAnimating && animating) ||
+ (!updateWhileInteracting && interacting)) {
+ return true;
+ }
+
+ var frameStateExtent = frameState.extent;
+ var viewState = frameState.viewState;
+ var projection = viewState.projection;
+ var resolution = viewState.resolution;
+ var pixelRatio = frameState.pixelRatio;
+ var vectorLayerRevision = vectorLayer.getRevision();
+ var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
+ var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
+
+ if (vectorLayerRenderOrder === undefined) {
+ vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
+ }
+
+ var extent = ol.extent.buffer(frameStateExtent,
+ vectorLayerRenderBuffer * resolution);
+
+ if (!this.dirty_ &&
+ this.renderedResolution_ == resolution &&
+ this.renderedRevision_ == vectorLayerRevision &&
+ this.renderedRenderOrder_ == vectorLayerRenderOrder &&
+ ol.extent.containsExtent(this.renderedExtent_, extent)) {
+ return true;
+ }
+
+ if (this.replayGroup_) {
+ frameState.postRenderFunctions.push(
+ this.replayGroup_.getDeleteResourcesFunction(context));
+ }
+
+ this.dirty_ = false;
+
+ var replayGroup = new ol.render.webgl.ReplayGroup(
+ ol.renderer.vector.getTolerance(resolution, pixelRatio),
+ extent, vectorLayer.getRenderBuffer());
+ vectorSource.loadFeatures(extent, resolution, projection);
+ /**
+ * @param {ol.Feature} feature Feature.
+ * @this {ol.renderer.webgl.VectorLayer}
+ */
+ var renderFeature = function(feature) {
+ var styles;
+ var styleFunction = feature.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction.call(feature, resolution);
+ } else {
+ styleFunction = vectorLayer.getStyleFunction();
+ if (styleFunction) {
+ styles = styleFunction(feature, resolution);
+ }
+ }
+ if (styles) {
+ var dirty = this.renderFeature(
+ feature, resolution, pixelRatio, styles, replayGroup);
+ this.dirty_ = this.dirty_ || dirty;
+ }
+ };
+ if (vectorLayerRenderOrder) {
+ /** @type {Array.<ol.Feature>} */
+ var features = [];
+ vectorSource.forEachFeatureInExtent(extent,
+ /**
+ * @param {ol.Feature} feature Feature.
+ */
+ function(feature) {
+ features.push(feature);
+ }, this);
+ features.sort(vectorLayerRenderOrder);
+ features.forEach(renderFeature, this);
+ } else {
+ vectorSource.forEachFeatureInExtent(extent, renderFeature, this);
+ }
+ replayGroup.finish(context);
+
+ this.renderedResolution_ = resolution;
+ this.renderedRevision_ = vectorLayerRevision;
+ this.renderedRenderOrder_ = vectorLayerRenderOrder;
+ this.renderedExtent_ = extent;
+ this.replayGroup_ = replayGroup;
+
+ return true;
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
+ * styles.
+ * @param {ol.render.webgl.ReplayGroup} replayGroup Replay group.
+ * @return {boolean} `true` if an image is loading.
+ */
+ol.renderer.webgl.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) {
+ if (!styles) {
+ return false;
+ }
+ var loading = false;
+ if (Array.isArray(styles)) {
+ for (var i = styles.length - 1, ii = 0; i >= ii; --i) {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles[i],
+ ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
+ this.handleStyleImageChange_, this) || loading;
+ }
+ } else {
+ loading = ol.renderer.vector.renderFeature(
+ replayGroup, feature, styles,
+ ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
+ this.handleStyleImageChange_, this) || loading;
+ }
+ return loading;
+};
+
+goog.provide('ol.structs.LRUCache');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.obj');
+
+
+/**
+ * Implements a Least-Recently-Used cache where the keys do not conflict with
+ * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring
+ * items from the cache is the responsibility of the user.
+ * @constructor
+ * @struct
+ * @template T
+ */
+ol.structs.LRUCache = function() {
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.count_ = 0;
+
+ /**
+ * @private
+ * @type {!Object.<string, ol.LRUCacheEntry>}
+ */
+ this.entries_ = {};
+
+ /**
+ * @private
+ * @type {?ol.LRUCacheEntry}
+ */
+ this.oldest_ = null;
+
+ /**
+ * @private
+ * @type {?ol.LRUCacheEntry}
+ */
+ this.newest_ = null;
+
+};
+
+
+if (ol.DEBUG) {
+ /**
+ * FIXME empty description for jsdoc
+ */
+ ol.structs.LRUCache.prototype.assertValid = function() {
+ if (this.count_ === 0) {
+ console.assert(ol.obj.isEmpty(this.entries_),
+ 'entries must be an empty object (count = 0)');
+ console.assert(!this.oldest_,
+ 'oldest must be null (count = 0)');
+ console.assert(!this.newest_,
+ 'newest must be null (count = 0)');
+ } else {
+ console.assert(Object.keys(this.entries_).length == this.count_,
+ 'number of entries matches count');
+ console.assert(this.oldest_,
+ 'we have an oldest entry');
+ console.assert(!this.oldest_.older,
+ 'no entry is older than oldest');
+ console.assert(this.newest_,
+ 'we have a newest entry');
+ console.assert(!this.newest_.newer,
+ 'no entry is newer than newest');
+ var i, entry;
+ var older = null;
+ i = 0;
+ for (entry = this.oldest_; entry; entry = entry.newer) {
+ console.assert(entry.older === older,
+ 'entry.older links to correct older');
+ older = entry;
+ ++i;
+ }
+ console.assert(i == this.count_, 'iterated correct amount of times');
+ var newer = null;
+ i = 0;
+ for (entry = this.newest_; entry; entry = entry.older) {
+ console.assert(entry.newer === newer,
+ 'entry.newer links to correct newer');
+ newer = entry;
+ ++i;
+ }
+ console.assert(i == this.count_, 'iterated correct amount of times');
+ }
+ };
+}
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.structs.LRUCache.prototype.clear = function() {
+ this.count_ = 0;
+ this.entries_ = {};
+ this.oldest_ = null;
+ this.newest_ = null;
+};
+
+
+/**
+ * @param {string} key Key.
+ * @return {boolean} Contains key.
+ */
+ol.structs.LRUCache.prototype.containsKey = function(key) {
+ return this.entries_.hasOwnProperty(key);
+};
+
+
+/**
+ * @param {function(this: S, T, string, ol.structs.LRUCache): ?} f The function
+ * to call for every entry from the oldest to the newer. This function takes
+ * 3 arguments (the entry value, the entry key and the LRUCache object).
+ * The return value is ignored.
+ * @param {S=} opt_this The object to use as `this` in `f`.
+ * @template S
+ */
+ol.structs.LRUCache.prototype.forEach = function(f, opt_this) {
+ var entry = this.oldest_;
+ while (entry) {
+ f.call(opt_this, entry.value_, entry.key_, this);
+ entry = entry.newer;
+ }
+};
+
+
+/**
+ * @param {string} key Key.
+ * @return {T} Value.
+ */
+ol.structs.LRUCache.prototype.get = function(key) {
+ var entry = this.entries_[key];
+ ol.asserts.assert(entry !== undefined,
+ 15); // Tried to get a value for a key that does not exist in the cache
+ if (entry === this.newest_) {
+ return entry.value_;
+ } else if (entry === this.oldest_) {
+ this.oldest_ = /** @type {ol.LRUCacheEntry} */ (this.oldest_.newer);
+ this.oldest_.older = null;
+ } else {
+ entry.newer.older = entry.older;
+ entry.older.newer = entry.newer;
+ }
+ entry.newer = null;
+ entry.older = this.newest_;
+ this.newest_.newer = entry;
+ this.newest_ = entry;
+ return entry.value_;
+};
+
+
+/**
+ * @return {number} Count.
+ */
+ol.structs.LRUCache.prototype.getCount = function() {
+ return this.count_;
+};
+
+
+/**
+ * @return {Array.<string>} Keys.
+ */
+ol.structs.LRUCache.prototype.getKeys = function() {
+ var keys = new Array(this.count_);
+ var i = 0;
+ var entry;
+ for (entry = this.newest_; entry; entry = entry.older) {
+ keys[i++] = entry.key_;
+ }
+ ol.DEBUG && console.assert(i == this.count_, 'iterated correct number of times');
+ return keys;
+};
+
+
+/**
+ * @return {Array.<T>} Values.
+ */
+ol.structs.LRUCache.prototype.getValues = function() {
+ var values = new Array(this.count_);
+ var i = 0;
+ var entry;
+ for (entry = this.newest_; entry; entry = entry.older) {
+ values[i++] = entry.value_;
+ }
+ ol.DEBUG && console.assert(i == this.count_, 'iterated correct number of times');
+ return values;
+};
+
+
+/**
+ * @return {T} Last value.
+ */
+ol.structs.LRUCache.prototype.peekLast = function() {
+ ol.DEBUG && console.assert(this.oldest_, 'oldest must not be null');
+ return this.oldest_.value_;
+};
+
+
+/**
+ * @return {string} Last key.
+ */
+ol.structs.LRUCache.prototype.peekLastKey = function() {
+ ol.DEBUG && console.assert(this.oldest_, 'oldest must not be null');
+ return this.oldest_.key_;
+};
+
+
+/**
+ * @return {T} value Value.
+ */
+ol.structs.LRUCache.prototype.pop = function() {
+ ol.DEBUG && console.assert(this.oldest_, 'oldest must not be null');
+ ol.DEBUG && console.assert(this.newest_, 'newest must not be null');
+ var entry = this.oldest_;
+ ol.DEBUG && console.assert(entry.key_ in this.entries_,
+ 'oldest is indexed in entries');
+ delete this.entries_[entry.key_];
+ if (entry.newer) {
+ entry.newer.older = null;
+ }
+ this.oldest_ = /** @type {ol.LRUCacheEntry} */ (entry.newer);
+ if (!this.oldest_) {
+ this.newest_ = null;
+ }
+ --this.count_;
+ return entry.value_;
+};
+
+
+/**
+ * @param {string} key Key.
+ * @param {T} value Value.
+ */
+ol.structs.LRUCache.prototype.replace = function(key, value) {
+ this.get(key); // update `newest_`
+ this.entries_[key].value_ = value;
+};
+
+
+/**
+ * @param {string} key Key.
+ * @param {T} value Value.
+ */
+ol.structs.LRUCache.prototype.set = function(key, value) {
+ ol.DEBUG && console.assert(!(key in {}),
+ 'key is not a standard property of objects (e.g. "__proto__")');
+ ol.asserts.assert(!(key in this.entries_),
+ 16); // Tried to set a value for a key that is used already
+ var entry = /** @type {ol.LRUCacheEntry} */ ({
+ key_: key,
+ newer: null,
+ older: this.newest_,
+ value_: value
+ });
+ if (!this.newest_) {
+ this.oldest_ = entry;
+ } else {
+ this.newest_.newer = entry;
+ }
+ this.newest_ = entry;
+ this.entries_[key] = entry;
+ ++this.count_;
+};
+
+// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE)
+
+goog.provide('ol.renderer.webgl.Map');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.css');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.layer.Image');
+goog.require('ol.layer.Layer');
+goog.require('ol.layer.Tile');
+goog.require('ol.layer.Vector');
+goog.require('ol.render.Event');
+goog.require('ol.render.webgl.Immediate');
+goog.require('ol.renderer.Map');
+goog.require('ol.renderer.Type');
+goog.require('ol.renderer.webgl.ImageLayer');
+goog.require('ol.renderer.webgl.TileLayer');
+goog.require('ol.renderer.webgl.VectorLayer');
+goog.require('ol.source.State');
+goog.require('ol.structs.LRUCache');
+goog.require('ol.structs.PriorityQueue');
+goog.require('ol.webgl');
+goog.require('ol.webgl.Context');
+goog.require('ol.webgl.ContextEventType');
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.Map}
+ * @param {Element} container Container.
+ * @param {ol.Map} map Map.
+ */
+ol.renderer.webgl.Map = function(container, map) {
+
+ ol.renderer.Map.call(this, container, map);
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = /** @type {HTMLCanvasElement} */
+ (document.createElement('CANVAS'));
+ this.canvas_.style.width = '100%';
+ this.canvas_.style.height = '100%';
+ this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
+ container.insertBefore(this.canvas_, container.childNodes[0] || null);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.clipTileCanvasWidth_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.clipTileCanvasHeight_ = 0;
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.clipTileContext_ = ol.dom.createCanvasContext2D();
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.renderedVisible_ = true;
+
+ /**
+ * @private
+ * @type {WebGLRenderingContext}
+ */
+ this.gl_ = ol.webgl.getContext(this.canvas_, {
+ antialias: true,
+ depth: true,
+ failIfMajorPerformanceCaveat: true,
+ preserveDrawingBuffer: false,
+ stencil: true
+ });
+ ol.DEBUG && console.assert(this.gl_, 'got a WebGLRenderingContext');
+
+ /**
+ * @private
+ * @type {ol.webgl.Context}
+ */
+ this.context_ = new ol.webgl.Context(this.canvas_, this.gl_);
+
+ ol.events.listen(this.canvas_, ol.webgl.ContextEventType.LOST,
+ this.handleWebGLContextLost, this);
+ ol.events.listen(this.canvas_, ol.webgl.ContextEventType.RESTORED,
+ this.handleWebGLContextRestored, this);
+
+ /**
+ * @private
+ * @type {ol.structs.LRUCache.<ol.WebglTextureCacheEntry|null>}
+ */
+ this.textureCache_ = new ol.structs.LRUCache();
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.focus_ = null;
+
+ /**
+ * @private
+ * @type {ol.structs.PriorityQueue.<Array>}
+ */
+ this.tileTextureQueue_ = new ol.structs.PriorityQueue(
+ /**
+ * @param {Array.<*>} element Element.
+ * @return {number} Priority.
+ * @this {ol.renderer.webgl.Map}
+ */
+ (function(element) {
+ var tileCenter = /** @type {ol.Coordinate} */ (element[1]);
+ var tileResolution = /** @type {number} */ (element[2]);
+ var deltaX = tileCenter[0] - this.focus_[0];
+ var deltaY = tileCenter[1] - this.focus_[1];
+ return 65536 * Math.log(tileResolution) +
+ Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
+ }).bind(this),
+ /**
+ * @param {Array.<*>} element Element.
+ * @return {string} Key.
+ */
+ function(element) {
+ return /** @type {ol.Tile} */ (element[0]).getKey();
+ });
+
+
+ /**
+ * @param {ol.Map} map Map.
+ * @param {?olx.FrameState} frameState Frame state.
+ * @return {boolean} false.
+ * @this {ol.renderer.webgl.Map}
+ */
+ this.loadNextTileTexture_ =
+ function(map, frameState) {
+ if (!this.tileTextureQueue_.isEmpty()) {
+ this.tileTextureQueue_.reprioritize();
+ var element = this.tileTextureQueue_.dequeue();
+ var tile = /** @type {ol.Tile} */ (element[0]);
+ var tileSize = /** @type {ol.Size} */ (element[3]);
+ var tileGutter = /** @type {number} */ (element[4]);
+ this.bindTileTexture(
+ tile, tileSize, tileGutter, ol.webgl.LINEAR, ol.webgl.LINEAR);
+ }
+ return false;
+ }.bind(this);
+
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.textureCacheFrameMarkerCount_ = 0;
+
+ this.initializeGL_();
+
+};
+ol.inherits(ol.renderer.webgl.Map, ol.renderer.Map);
+
+
+/**
+ * @param {ol.Tile} tile Tile.
+ * @param {ol.Size} tileSize Tile size.
+ * @param {number} tileGutter Tile gutter.
+ * @param {number} magFilter Mag filter.
+ * @param {number} minFilter Min filter.
+ */
+ol.renderer.webgl.Map.prototype.bindTileTexture = function(tile, tileSize, tileGutter, magFilter, minFilter) {
+ var gl = this.getGL();
+ var tileKey = tile.getKey();
+ if (this.textureCache_.containsKey(tileKey)) {
+ var textureCacheEntry = this.textureCache_.get(tileKey);
+ gl.bindTexture(ol.webgl.TEXTURE_2D, textureCacheEntry.texture);
+ if (textureCacheEntry.magFilter != magFilter) {
+ gl.texParameteri(
+ ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MAG_FILTER, magFilter);
+ textureCacheEntry.magFilter = magFilter;
+ }
+ if (textureCacheEntry.minFilter != minFilter) {
+ gl.texParameteri(
+ ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MIN_FILTER, minFilter);
+ textureCacheEntry.minFilter = minFilter;
+ }
+ } else {
+ var texture = gl.createTexture();
+ gl.bindTexture(ol.webgl.TEXTURE_2D, texture);
+ if (tileGutter > 0) {
+ var clipTileCanvas = this.clipTileContext_.canvas;
+ var clipTileContext = this.clipTileContext_;
+ if (this.clipTileCanvasWidth_ !== tileSize[0] ||
+ this.clipTileCanvasHeight_ !== tileSize[1]) {
+ clipTileCanvas.width = tileSize[0];
+ clipTileCanvas.height = tileSize[1];
+ this.clipTileCanvasWidth_ = tileSize[0];
+ this.clipTileCanvasHeight_ = tileSize[1];
+ } else {
+ clipTileContext.clearRect(0, 0, tileSize[0], tileSize[1]);
+ }
+ clipTileContext.drawImage(tile.getImage(), tileGutter, tileGutter,
+ tileSize[0], tileSize[1], 0, 0, tileSize[0], tileSize[1]);
+ gl.texImage2D(ol.webgl.TEXTURE_2D, 0,
+ ol.webgl.RGBA, ol.webgl.RGBA,
+ ol.webgl.UNSIGNED_BYTE, clipTileCanvas);
+ } else {
+ gl.texImage2D(ol.webgl.TEXTURE_2D, 0,
+ ol.webgl.RGBA, ol.webgl.RGBA,
+ ol.webgl.UNSIGNED_BYTE, tile.getImage());
+ }
+ gl.texParameteri(
+ ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MAG_FILTER, magFilter);
+ gl.texParameteri(
+ ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MIN_FILTER, minFilter);
+ gl.texParameteri(ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_S,
+ ol.webgl.CLAMP_TO_EDGE);
+ gl.texParameteri(ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_T,
+ ol.webgl.CLAMP_TO_EDGE);
+ this.textureCache_.set(tileKey, {
+ texture: texture,
+ magFilter: magFilter,
+ minFilter: minFilter
+ });
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
+ if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
+ return new ol.renderer.webgl.ImageLayer(this, layer);
+ } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
+ return new ol.renderer.webgl.TileLayer(this, layer);
+ } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
+ return new ol.renderer.webgl.VectorLayer(this, layer);
+ } else {
+ ol.DEBUG && console.assert(false, 'unexpected layer configuration');
+ return null;
+ }
+};
+
+
+/**
+ * @param {ol.render.Event.Type} type Event type.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ = function(type, frameState) {
+ var map = this.getMap();
+ if (map.hasListener(type)) {
+ var context = this.context_;
+
+ var extent = frameState.extent;
+ var size = frameState.size;
+ var viewState = frameState.viewState;
+ var pixelRatio = frameState.pixelRatio;
+
+ var resolution = viewState.resolution;
+ var center = viewState.center;
+ var rotation = viewState.rotation;
+
+ var vectorContext = new ol.render.webgl.Immediate(context,
+ center, resolution, rotation, size, extent, pixelRatio);
+ var composeEvent = new ol.render.Event(type, vectorContext,
+ frameState, null, context);
+ map.dispatchEvent(composeEvent);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.disposeInternal = function() {
+ var gl = this.getGL();
+ if (!gl.isContextLost()) {
+ this.textureCache_.forEach(
+ /**
+ * @param {?ol.WebglTextureCacheEntry} textureCacheEntry
+ * Texture cache entry.
+ */
+ function(textureCacheEntry) {
+ if (textureCacheEntry) {
+ gl.deleteTexture(textureCacheEntry.texture);
+ }
+ });
+ }
+ this.context_.dispose();
+ ol.renderer.Map.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * @param {ol.Map} map Map.
+ * @param {olx.FrameState} frameState Frame state.
+ * @private
+ */
+ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) {
+ var gl = this.getGL();
+ var textureCacheEntry;
+ while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ >
+ ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) {
+ textureCacheEntry = this.textureCache_.peekLast();
+ if (!textureCacheEntry) {
+ if (+this.textureCache_.peekLastKey() == frameState.index) {
+ break;
+ } else {
+ --this.textureCacheFrameMarkerCount_;
+ }
+ } else {
+ gl.deleteTexture(textureCacheEntry.texture);
+ }
+ this.textureCache_.pop();
+ }
+};
+
+
+/**
+ * @return {ol.webgl.Context} The context.
+ */
+ol.renderer.webgl.Map.prototype.getContext = function() {
+ return this.context_;
+};
+
+
+/**
+ * @return {WebGLRenderingContext} GL.
+ */
+ol.renderer.webgl.Map.prototype.getGL = function() {
+ return this.gl_;
+};
+
+
+/**
+ * @return {ol.structs.PriorityQueue.<Array>} Tile texture queue.
+ */
+ol.renderer.webgl.Map.prototype.getTileTextureQueue = function() {
+ return this.tileTextureQueue_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.getType = function() {
+ return ol.renderer.Type.WEBGL;
+};
+
+
+/**
+ * @param {ol.events.Event} event Event.
+ * @protected
+ */
+ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) {
+ event.preventDefault();
+ this.textureCache_.clear();
+ this.textureCacheFrameMarkerCount_ = 0;
+
+ var renderers = this.getLayerRenderers();
+ for (var id in renderers) {
+ var renderer = /** @type {ol.renderer.webgl.Layer} */ (renderers[id]);
+ renderer.handleWebGLContextLost();
+ }
+};
+
+
+/**
+ * @protected
+ */
+ol.renderer.webgl.Map.prototype.handleWebGLContextRestored = function() {
+ this.initializeGL_();
+ this.getMap().render();
+};
+
+
+/**
+ * @private
+ */
+ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
+ var gl = this.gl_;
+ gl.activeTexture(ol.webgl.TEXTURE0);
+ gl.blendFuncSeparate(
+ ol.webgl.SRC_ALPHA, ol.webgl.ONE_MINUS_SRC_ALPHA,
+ ol.webgl.ONE, ol.webgl.ONE_MINUS_SRC_ALPHA);
+ gl.disable(ol.webgl.CULL_FACE);
+ gl.disable(ol.webgl.DEPTH_TEST);
+ gl.disable(ol.webgl.SCISSOR_TEST);
+ gl.disable(ol.webgl.STENCIL_TEST);
+};
+
+
+/**
+ * @param {ol.Tile} tile Tile.
+ * @return {boolean} Is tile texture loaded.
+ */
+ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
+ return this.textureCache_.containsKey(tile.getKey());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
+
+ var context = this.getContext();
+ var gl = this.getGL();
+
+ if (gl.isContextLost()) {
+ return false;
+ }
+
+ if (!frameState) {
+ if (this.renderedVisible_) {
+ this.canvas_.style.display = 'none';
+ this.renderedVisible_ = false;
+ }
+ return false;
+ }
+
+ this.focus_ = frameState.focus;
+
+ this.textureCache_.set((-frameState.index).toString(), null);
+ ++this.textureCacheFrameMarkerCount_;
+
+ this.dispatchComposeEvent_(ol.render.Event.Type.PRECOMPOSE, frameState);
+
+ /** @type {Array.<ol.LayerState>} */
+ var layerStatesToDraw = [];
+ var layerStatesArray = frameState.layerStatesArray;
+ ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex);
+
+ var viewResolution = frameState.viewState.resolution;
+ var i, ii, layerRenderer, layerState;
+ for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
+ layerState = layerStatesArray[i];
+ if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
+ layerState.sourceState == ol.source.State.READY) {
+ layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layerState.layer));
+ if (layerRenderer.prepareFrame(frameState, layerState, context)) {
+ layerStatesToDraw.push(layerState);
+ }
+ }
+ }
+
+ var width = frameState.size[0] * frameState.pixelRatio;
+ var height = frameState.size[1] * frameState.pixelRatio;
+ if (this.canvas_.width != width || this.canvas_.height != height) {
+ this.canvas_.width = width;
+ this.canvas_.height = height;
+ }
+
+ gl.bindFramebuffer(ol.webgl.FRAMEBUFFER, null);
+
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(ol.webgl.COLOR_BUFFER_BIT);
+ gl.enable(ol.webgl.BLEND);
+ gl.viewport(0, 0, this.canvas_.width, this.canvas_.height);
+
+ for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) {
+ layerState = layerStatesToDraw[i];
+ layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layerState.layer));
+ layerRenderer.composeFrame(frameState, layerState, context);
+ }
+
+ if (!this.renderedVisible_) {
+ this.canvas_.style.display = '';
+ this.renderedVisible_ = true;
+ }
+
+ this.calculateMatrices2D(frameState);
+
+ if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ >
+ ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) {
+ frameState.postRenderFunctions.push(
+ /** @type {ol.PostRenderFunction} */ (this.expireCache_.bind(this))
+ );
+ }
+
+ if (!this.tileTextureQueue_.isEmpty()) {
+ frameState.postRenderFunctions.push(this.loadNextTileTexture_);
+ frameState.animate = true;
+ }
+
+ this.dispatchComposeEvent_(ol.render.Event.Type.POSTCOMPOSE, frameState);
+
+ this.scheduleRemoveUnusedLayerRenderers(frameState);
+ this.scheduleExpireIconCache(frameState);
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg,
+ layerFilter, thisArg2) {
+ var result;
+
+ if (this.getGL().isContextLost()) {
+ return false;
+ }
+
+ var viewState = frameState.viewState;
+
+ var layerStates = frameState.layerStatesArray;
+ var numLayers = layerStates.length;
+ var i;
+ for (i = numLayers - 1; i >= 0; --i) {
+ var layerState = layerStates[i];
+ var layer = layerState.layer;
+ if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
+ layerFilter.call(thisArg2, layer)) {
+ var layerRenderer = this.getLayerRenderer(layer);
+ result = layerRenderer.forEachFeatureAtCoordinate(
+ coordinate, frameState, hitTolerance, callback, thisArg);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, layerFilter, thisArg) {
+ var hasFeature = false;
+
+ if (this.getGL().isContextLost()) {
+ return false;
+ }
+
+ var viewState = frameState.viewState;
+
+ var layerStates = frameState.layerStatesArray;
+ var numLayers = layerStates.length;
+ var i;
+ for (i = numLayers - 1; i >= 0; --i) {
+ var layerState = layerStates[i];
+ var layer = layerState.layer;
+ if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
+ layerFilter.call(thisArg, layer)) {
+ var layerRenderer = this.getLayerRenderer(layer);
+ hasFeature =
+ layerRenderer.hasFeatureAtCoordinate(coordinate, frameState);
+ if (hasFeature) {
+ return true;
+ }
+ }
+ }
+ return hasFeature;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg,
+ layerFilter, thisArg2) {
+ if (this.getGL().isContextLost()) {
+ return false;
+ }
+
+ var viewState = frameState.viewState;
+ var result;
+
+ var layerStates = frameState.layerStatesArray;
+ var numLayers = layerStates.length;
+ var i;
+ for (i = numLayers - 1; i >= 0; --i) {
+ var layerState = layerStates[i];
+ var layer = layerState.layer;
+ if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
+ layerFilter.call(thisArg, layer)) {
+ var layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layer));
+ result = layerRenderer.forEachLayerAtPixel(
+ pixel, frameState, callback, thisArg);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return undefined;
+};
+
+// FIXME recheck layer/map projection compatibility when projection changes
+// FIXME layer renderers should skip when they can't reproject
+// FIXME add tilt and height?
+
+goog.provide('ol.Map');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.MapBrowserEventHandler');
+goog.require('ol.MapEvent');
+goog.require('ol.Object');
+goog.require('ol.TileQueue');
+goog.require('ol.View');
+goog.require('ol.asserts');
+goog.require('ol.control');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.functions');
+goog.require('ol.has');
+goog.require('ol.interaction');
+goog.require('ol.layer.Group');
+goog.require('ol.obj');
+goog.require('ol.proj.common');
+goog.require('ol.renderer.Type');
+goog.require('ol.renderer.Map');
+goog.require('ol.renderer.canvas.Map');
+goog.require('ol.renderer.webgl.Map');
+goog.require('ol.size');
+goog.require('ol.structs.PriorityQueue');
+goog.require('ol.transform');
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.OL3_URL = 'https://openlayers.org/';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.OL3_LOGO_URL = 'data:image/png;base64,' +
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBI' +
+ 'WXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAA' +
+ 'AhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszW' +
+ 'WMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvY' +
+ 'asvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvX' +
+ 'H1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1Vk' +
+ 'bMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW' +
+ '2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLP' +
+ 'VcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqT' +
+ 'acrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaar' +
+ 'ldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+Hi' +
+ 'zeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDn' +
+ 'BAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSF' +
+ 'hYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJ' +
+ 'REFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxC' +
+ 'Brb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe' +
+ '0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8' +
+ 'a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7a' +
+ 'hgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCn' +
+ 'B3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDg' +
+ 'q82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC';
+
+
+/**
+ * @type {Array.<ol.renderer.Type>}
+ * @const
+ */
+ol.DEFAULT_RENDERER_TYPES = [
+ ol.renderer.Type.CANVAS,
+ ol.renderer.Type.WEBGL
+];
+
+
+/**
+ * @classdesc
+ * The map is the core component of OpenLayers. For a map to render, a view,
+ * one or more layers, and a target container are needed:
+ *
+ * var map = new ol.Map({
+ * view: new ol.View({
+ * center: [0, 0],
+ * zoom: 1
+ * }),
+ * layers: [
+ * new ol.layer.Tile({
+ * source: new ol.source.OSM()
+ * })
+ * ],
+ * target: 'map'
+ * });
+ *
+ * The above snippet creates a map using a {@link ol.layer.Tile} to display
+ * {@link ol.source.OSM} OSM data and render it to a DOM element with the
+ * id `map`.
+ *
+ * The constructor places a viewport container (with CSS class name
+ * `ol-viewport`) in the target element (see `getViewport()`), and then two
+ * further elements within the viewport: one with CSS class name
+ * `ol-overlaycontainer-stopevent` for controls and some overlays, and one with
+ * CSS class name `ol-overlaycontainer` for other overlays (see the `stopEvent`
+ * option of {@link ol.Overlay} for the difference). The map itself is placed in
+ * a further element within the viewport.
+ *
+ * Layers are stored as a `ol.Collection` in layerGroups. A top-level group is
+ * provided by the library. This is what is accessed by `getLayerGroup` and
+ * `setLayerGroup`. Layers entered in the options are added to this group, and
+ * `addLayer` and `removeLayer` change the layer collection in the group.
+ * `getLayers` is a convenience function for `getLayerGroup().getLayers()`.
+ * Note that `ol.layer.Group` is a subclass of `ol.layer.Base`, so layers
+ * entered in the options or added with `addLayer` can be groups, which can
+ * contain further groups, and so on.
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {olx.MapOptions} options Map options.
+ * @fires ol.MapBrowserEvent
+ * @fires ol.MapEvent
+ * @fires ol.render.Event#postcompose
+ * @fires ol.render.Event#precompose
+ * @api stable
+ */
+ol.Map = function(options) {
+
+ ol.Object.call(this);
+
+ var optionsInternal = ol.Map.createOptionsInternal(options);
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.loadTilesWhileAnimating_ =
+ options.loadTilesWhileAnimating !== undefined ?
+ options.loadTilesWhileAnimating : false;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.loadTilesWhileInteracting_ =
+ options.loadTilesWhileInteracting !== undefined ?
+ options.loadTilesWhileInteracting : false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelRatio_ = options.pixelRatio !== undefined ?
+ options.pixelRatio : ol.has.DEVICE_PIXEL_RATIO;
+
+ /**
+ * @private
+ * @type {Object.<string, string>}
+ */
+ this.logos_ = optionsInternal.logos;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.animationDelayKey_;
+
+ /**
+ * @private
+ */
+ this.animationDelay_ = function() {
+ this.animationDelayKey_ = undefined;
+ this.renderFrame_.call(this, Date.now());
+ }.bind(this);
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.coordinateToPixelTransform_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {ol.Transform}
+ */
+ this.pixelToCoordinateTransform_ = ol.transform.create();
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.frameIndex_ = 0;
+
+ /**
+ * @private
+ * @type {?olx.FrameState}
+ */
+ this.frameState_ = null;
+
+ /**
+ * The extent at the previous 'moveend' event.
+ * @private
+ * @type {ol.Extent}
+ */
+ this.previousExtent_ = ol.extent.createEmpty();
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.viewPropertyListenerKey_ = null;
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.viewChangeListenerKey_ = null;
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.layerGroupPropertyListenerKeys_ = null;
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.viewport_ = document.createElement('DIV');
+ this.viewport_.className = 'ol-viewport' + (ol.has.TOUCH ? ' ol-touch' : '');
+ this.viewport_.style.position = 'relative';
+ this.viewport_.style.overflow = 'hidden';
+ this.viewport_.style.width = '100%';
+ this.viewport_.style.height = '100%';
+ // prevent page zoom on IE >= 10 browsers
+ this.viewport_.style.msTouchAction = 'none';
+ this.viewport_.style.touchAction = 'none';
+
+ /**
+ * @private
+ * @type {!Element}
+ */
+ this.overlayContainer_ = document.createElement('DIV');
+ this.overlayContainer_.className = 'ol-overlaycontainer';
+ this.viewport_.appendChild(this.overlayContainer_);
+
+ /**
+ * @private
+ * @type {!Element}
+ */
+ this.overlayContainerStopEvent_ = document.createElement('DIV');
+ this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent';
+ var overlayEvents = [
+ ol.events.EventType.CLICK,
+ ol.events.EventType.DBLCLICK,
+ ol.events.EventType.MOUSEDOWN,
+ ol.events.EventType.TOUCHSTART,
+ ol.events.EventType.MSPOINTERDOWN,
+ ol.MapBrowserEvent.EventType.POINTERDOWN,
+ ol.events.EventType.MOUSEWHEEL,
+ ol.events.EventType.WHEEL
+ ];
+ for (var i = 0, ii = overlayEvents.length; i < ii; ++i) {
+ ol.events.listen(this.overlayContainerStopEvent_, overlayEvents[i],
+ ol.events.Event.stopPropagation);
+ }
+ this.viewport_.appendChild(this.overlayContainerStopEvent_);
+
+ /**
+ * @private
+ * @type {ol.MapBrowserEventHandler}
+ */
+ this.mapBrowserEventHandler_ = new ol.MapBrowserEventHandler(this);
+ for (var key in ol.MapBrowserEvent.EventType) {
+ ol.events.listen(this.mapBrowserEventHandler_, ol.MapBrowserEvent.EventType[key],
+ this.handleMapBrowserEvent, this);
+ }
+
+ /**
+ * @private
+ * @type {Element|Document}
+ */
+ this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget;
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.keyHandlerKeys_ = null;
+
+ ol.events.listen(this.viewport_, ol.events.EventType.WHEEL,
+ this.handleBrowserEvent, this);
+ ol.events.listen(this.viewport_, ol.events.EventType.MOUSEWHEEL,
+ this.handleBrowserEvent, this);
+
+ /**
+ * @type {ol.Collection.<ol.control.Control>}
+ * @private
+ */
+ this.controls_ = optionsInternal.controls;
+
+ /**
+ * @type {ol.Collection.<ol.interaction.Interaction>}
+ * @private
+ */
+ this.interactions_ = optionsInternal.interactions;
+
+ /**
+ * @type {ol.Collection.<ol.Overlay>}
+ * @private
+ */
+ this.overlays_ = optionsInternal.overlays;
+
+ /**
+ * A lookup of overlays by id.
+ * @private
+ * @type {Object.<string, ol.Overlay>}
+ */
+ this.overlayIdIndex_ = {};
+
+ /**
+ * @type {ol.renderer.Map}
+ * @private
+ */
+ this.renderer_ = new optionsInternal.rendererConstructor(this.viewport_, this);
+
+ /**
+ * @type {function(Event)|undefined}
+ * @private
+ */
+ this.handleResize_;
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.focus_ = null;
+
+ /**
+ * @private
+ * @type {Array.<ol.PreRenderFunction>}
+ */
+ this.preRenderFunctions_ = [];
+
+ /**
+ * @private
+ * @type {Array.<ol.PostRenderFunction>}
+ */
+ this.postRenderFunctions_ = [];
+
+ /**
+ * @private
+ * @type {ol.TileQueue}
+ */
+ this.tileQueue_ = new ol.TileQueue(
+ this.getTilePriority.bind(this),
+ this.handleTileChange_.bind(this));
+
+ /**
+ * Uids of features to skip at rendering time.
+ * @type {Object.<string, boolean>}
+ * @private
+ */
+ this.skippedFeatureUids_ = {};
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Map.Property.LAYERGROUP),
+ this.handleLayerGroupChanged_, this);
+ ol.events.listen(this, ol.Object.getChangeEventType(ol.Map.Property.VIEW),
+ this.handleViewChanged_, this);
+ ol.events.listen(this, ol.Object.getChangeEventType(ol.Map.Property.SIZE),
+ this.handleSizeChanged_, this);
+ ol.events.listen(this, ol.Object.getChangeEventType(ol.Map.Property.TARGET),
+ this.handleTargetChanged_, this);
+
+ // setProperties will trigger the rendering of the map if the map
+ // is "defined" already.
+ this.setProperties(optionsInternal.values);
+
+ this.controls_.forEach(
+ /**
+ * @param {ol.control.Control} control Control.
+ * @this {ol.Map}
+ */
+ function(control) {
+ control.setMap(this);
+ }, this);
+
+ ol.events.listen(this.controls_, ol.Collection.EventType.ADD,
+ /**
+ * @param {ol.Collection.Event} event Collection event.
+ */
+ function(event) {
+ event.element.setMap(this);
+ }, this);
+
+ ol.events.listen(this.controls_, ol.Collection.EventType.REMOVE,
+ /**
+ * @param {ol.Collection.Event} event Collection event.
+ */
+ function(event) {
+ event.element.setMap(null);
+ }, this);
+
+ this.interactions_.forEach(
+ /**
+ * @param {ol.interaction.Interaction} interaction Interaction.
+ * @this {ol.Map}
+ */
+ function(interaction) {
+ interaction.setMap(this);
+ }, this);
+
+ ol.events.listen(this.interactions_, ol.Collection.EventType.ADD,
+ /**
+ * @param {ol.Collection.Event} event Collection event.
+ */
+ function(event) {
+ event.element.setMap(this);
+ }, this);
+
+ ol.events.listen(this.interactions_, ol.Collection.EventType.REMOVE,
+ /**
+ * @param {ol.Collection.Event} event Collection event.
+ */
+ function(event) {
+ event.element.setMap(null);
+ }, this);
+
+ this.overlays_.forEach(this.addOverlayInternal_, this);
+
+ ol.events.listen(this.overlays_, ol.Collection.EventType.ADD,
+ /**
+ * @param {ol.Collection.Event} event Collection event.
+ */
+ function(event) {
+ this.addOverlayInternal_(/** @type {ol.Overlay} */ (event.element));
+ }, this);
+
+ ol.events.listen(this.overlays_, ol.Collection.EventType.REMOVE,
+ /**
+ * @param {ol.Collection.Event} event Collection event.
+ */
+ function(event) {
+ var overlay = /** @type {ol.Overlay} */ (event.element);
+ var id = overlay.getId();
+ if (id !== undefined) {
+ delete this.overlayIdIndex_[id.toString()];
+ }
+ event.element.setMap(null);
+ }, this);
+
+};
+ol.inherits(ol.Map, ol.Object);
+
+
+/**
+ * Add the given control to the map.
+ * @param {ol.control.Control} control Control.
+ * @api stable
+ */
+ol.Map.prototype.addControl = function(control) {
+ this.getControls().push(control);
+};
+
+
+/**
+ * Add the given interaction to the map.
+ * @param {ol.interaction.Interaction} interaction Interaction to add.
+ * @api stable
+ */
+ol.Map.prototype.addInteraction = function(interaction) {
+ this.getInteractions().push(interaction);
+};
+
+
+/**
+ * Adds the given layer to the top of this map. If you want to add a layer
+ * elsewhere in the stack, use `getLayers()` and the methods available on
+ * {@link ol.Collection}.
+ * @param {ol.layer.Base} layer Layer.
+ * @api stable
+ */
+ol.Map.prototype.addLayer = function(layer) {
+ var layers = this.getLayerGroup().getLayers();
+ layers.push(layer);
+};
+
+
+/**
+ * Add the given overlay to the map.
+ * @param {ol.Overlay} overlay Overlay.
+ * @api stable
+ */
+ol.Map.prototype.addOverlay = function(overlay) {
+ this.getOverlays().push(overlay);
+};
+
+
+/**
+ * This deals with map's overlay collection changes.
+ * @param {ol.Overlay} overlay Overlay.
+ * @private
+ */
+ol.Map.prototype.addOverlayInternal_ = function(overlay) {
+ var id = overlay.getId();
+ if (id !== undefined) {
+ this.overlayIdIndex_[id.toString()] = overlay;
+ }
+ overlay.setMap(this);
+};
+
+
+/**
+ * Deprecated (use {@link ol.View#animate} instead).
+ * Add functions to be called before rendering. This can be used for attaching
+ * animations before updating the map's view. The {@link ol.animation}
+ * namespace provides several static methods for creating prerender functions.
+ * @param {...ol.PreRenderFunction} var_args Any number of pre-render functions.
+ * @api
+ */
+ol.Map.prototype.beforeRender = function(var_args) {
+ ol.DEBUG && console.warn('map.beforeRender() is deprecated. Use view.animate() instead.');
+ this.render();
+ Array.prototype.push.apply(this.preRenderFunctions_, arguments);
+};
+
+
+/**
+ *
+ * @inheritDoc
+ */
+ol.Map.prototype.disposeInternal = function() {
+ this.mapBrowserEventHandler_.dispose();
+ this.renderer_.dispose();
+ ol.events.unlisten(this.viewport_, ol.events.EventType.WHEEL,
+ this.handleBrowserEvent, this);
+ ol.events.unlisten(this.viewport_, ol.events.EventType.MOUSEWHEEL,
+ this.handleBrowserEvent, this);
+ if (this.handleResize_ !== undefined) {
+ window.removeEventListener(ol.events.EventType.RESIZE,
+ this.handleResize_, false);
+ this.handleResize_ = undefined;
+ }
+ if (this.animationDelayKey_) {
+ cancelAnimationFrame(this.animationDelayKey_);
+ this.animationDelayKey_ = undefined;
+ }
+ this.setTarget(null);
+ ol.Object.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * Detect features that intersect a pixel on the viewport, and execute a
+ * callback with each intersecting feature. Layers included in the detection can
+ * be configured through `opt_layerFilter`.
+ * @param {ol.Pixel} pixel Pixel.
+ * @param {function(this: S, (ol.Feature|ol.render.Feature),
+ * ol.layer.Layer): T} callback Feature callback. The callback will be
+ * called with two arguments. The first argument is one
+ * {@link ol.Feature feature} or
+ * {@link ol.render.Feature render feature} at the pixel, the second is
+ * the {@link ol.layer.Layer layer} of the feature and will be null for
+ * unmanaged layers. To stop detection, callback functions can return a
+ * truthy value.
+ * @param {olx.AtPixelOptions=} opt_options Optional options.
+ * @return {T|undefined} Callback result, i.e. the return value of last
+ * callback execution, or the first truthy callback return value.
+ * @template S,T
+ * @api stable
+ */
+ol.Map.prototype.forEachFeatureAtPixel = function(pixel, callback, opt_options) {
+ if (!this.frameState_) {
+ return;
+ }
+ var coordinate = this.getCoordinateFromPixel(pixel);
+ opt_options = opt_options !== undefined ? opt_options : {};
+ var hitTolerance = opt_options.hitTolerance !== undefined ?
+ opt_options.hitTolerance * this.frameState_.pixelRatio : 0;
+ var layerFilter = opt_options.layerFilter !== undefined ?
+ opt_options.layerFilter : ol.functions.TRUE;
+ return this.renderer_.forEachFeatureAtCoordinate(
+ coordinate, this.frameState_, hitTolerance, callback, null,
+ layerFilter, null);
+};
+
+
+/**
+ * Detect layers that have a color value at a pixel on the viewport, and
+ * execute a callback with each matching layer. Layers included in the
+ * detection can be configured through `opt_layerFilter`.
+ * @param {ol.Pixel} pixel Pixel.
+ * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback
+ * Layer callback. This callback will recieve two arguments: first is the
+ * {@link ol.layer.Layer layer}, second argument is an array representing
+ * [R, G, B, A] pixel values (0 - 255) and will be `null` for layer types
+ * that do not currently support this argument. To stop detection, callback
+ * functions can return a truthy value.
+ * @param {S=} opt_this Value to use as `this` when executing `callback`.
+ * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer
+ * filter function. The filter function will receive one argument, the
+ * {@link ol.layer.Layer layer-candidate} and it should return a boolean
+ * value. Only layers which are visible and for which this function returns
+ * `true` will be tested for features. By default, all visible layers will
+ * be tested.
+ * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`.
+ * @return {T|undefined} Callback result, i.e. the return value of last
+ * callback execution, or the first truthy callback return value.
+ * @template S,T,U
+ * @api stable
+ */
+ol.Map.prototype.forEachLayerAtPixel = function(pixel, callback, opt_this, opt_layerFilter, opt_this2) {
+ if (!this.frameState_) {
+ return;
+ }
+ var thisArg = opt_this !== undefined ? opt_this : null;
+ var layerFilter = opt_layerFilter !== undefined ?
+ opt_layerFilter : ol.functions.TRUE;
+ var thisArg2 = opt_this2 !== undefined ? opt_this2 : null;
+ return this.renderer_.forEachLayerAtPixel(
+ pixel, this.frameState_, callback, thisArg,
+ layerFilter, thisArg2);
+};
+
+
+/**
+ * Detect if features intersect a pixel on the viewport. Layers included in the
+ * detection can be configured through `opt_layerFilter`.
+ * @param {ol.Pixel} pixel Pixel.
+ * @param {olx.AtPixelOptions=} opt_options Optional options.
+ * @return {boolean} Is there a feature at the given pixel?
+ * @template U
+ * @api
+ */
+ol.Map.prototype.hasFeatureAtPixel = function(pixel, opt_options) {
+ if (!this.frameState_) {
+ return false;
+ }
+ var coordinate = this.getCoordinateFromPixel(pixel);
+ opt_options = opt_options !== undefined ? opt_options : {};
+ var layerFilter = opt_options.layerFilter !== undefined ?
+ opt_options.layerFilter : ol.functions.TRUE;
+ var hitTolerance = opt_options.hitTolerance !== undefined ?
+ opt_options.hitTolerance * this.frameState_.pixelRatio : 0;
+ return this.renderer_.hasFeatureAtCoordinate(
+ coordinate, this.frameState_, hitTolerance, layerFilter, null);
+};
+
+
+/**
+ * Returns the coordinate in view projection for a browser event.
+ * @param {Event} event Event.
+ * @return {ol.Coordinate} Coordinate.
+ * @api stable
+ */
+ol.Map.prototype.getEventCoordinate = function(event) {
+ return this.getCoordinateFromPixel(this.getEventPixel(event));
+};
+
+
+/**
+ * Returns the map pixel position for a browser event relative to the viewport.
+ * @param {Event} event Event.
+ * @return {ol.Pixel} Pixel.
+ * @api stable
+ */
+ol.Map.prototype.getEventPixel = function(event) {
+ var viewportPosition = this.viewport_.getBoundingClientRect();
+ var eventPosition = event.changedTouches ? event.changedTouches[0] : event;
+ return [
+ eventPosition.clientX - viewportPosition.left,
+ eventPosition.clientY - viewportPosition.top
+ ];
+};
+
+
+/**
+ * Get the target in which this map is rendered.
+ * Note that this returns what is entered as an option or in setTarget:
+ * if that was an element, it returns an element; if a string, it returns that.
+ * @return {Element|string|undefined} The Element or id of the Element that the
+ * map is rendered in.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.getTarget = function() {
+ return /** @type {Element|string|undefined} */ (
+ this.get(ol.Map.Property.TARGET));
+};
+
+
+/**
+ * Get the DOM element into which this map is rendered. In contrast to
+ * `getTarget` this method always return an `Element`, or `null` if the
+ * map has no target.
+ * @return {Element} The element that the map is rendered in.
+ * @api
+ */
+ol.Map.prototype.getTargetElement = function() {
+ var target = this.getTarget();
+ if (target !== undefined) {
+ return typeof target === 'string' ?
+ document.getElementById(target) :
+ target;
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * Get the coordinate for a given pixel. This returns a coordinate in the
+ * map view projection.
+ * @param {ol.Pixel} pixel Pixel position in the map viewport.
+ * @return {ol.Coordinate} The coordinate for the pixel position.
+ * @api stable
+ */
+ol.Map.prototype.getCoordinateFromPixel = function(pixel) {
+ var frameState = this.frameState_;
+ if (!frameState) {
+ return null;
+ } else {
+ return ol.transform.apply(frameState.pixelToCoordinateTransform, pixel.slice());
+ }
+};
+
+
+/**
+ * Get the map controls. Modifying this collection changes the controls
+ * associated with the map.
+ * @return {ol.Collection.<ol.control.Control>} Controls.
+ * @api stable
+ */
+ol.Map.prototype.getControls = function() {
+ return this.controls_;
+};
+
+
+/**
+ * Get the map overlays. Modifying this collection changes the overlays
+ * associated with the map.
+ * @return {ol.Collection.<ol.Overlay>} Overlays.
+ * @api stable
+ */
+ol.Map.prototype.getOverlays = function() {
+ return this.overlays_;
+};
+
+
+/**
+ * Get an overlay by its identifier (the value returned by overlay.getId()).
+ * Note that the index treats string and numeric identifiers as the same. So
+ * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`.
+ * @param {string|number} id Overlay identifier.
+ * @return {ol.Overlay} Overlay.
+ * @api
+ */
+ol.Map.prototype.getOverlayById = function(id) {
+ var overlay = this.overlayIdIndex_[id.toString()];
+ return overlay !== undefined ? overlay : null;
+};
+
+
+/**
+ * Get the map interactions. Modifying this collection changes the interactions
+ * associated with the map.
+ *
+ * Interactions are used for e.g. pan, zoom and rotate.
+ * @return {ol.Collection.<ol.interaction.Interaction>} Interactions.
+ * @api stable
+ */
+ol.Map.prototype.getInteractions = function() {
+ return this.interactions_;
+};
+
+
+/**
+ * Get the layergroup associated with this map.
+ * @return {ol.layer.Group} A layer group containing the layers in this map.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.getLayerGroup = function() {
+ return /** @type {ol.layer.Group} */ (this.get(ol.Map.Property.LAYERGROUP));
+};
+
+
+/**
+ * Get the collection of layers associated with this map.
+ * @return {!ol.Collection.<ol.layer.Base>} Layers.
+ * @api stable
+ */
+ol.Map.prototype.getLayers = function() {
+ var layers = this.getLayerGroup().getLayers();
+ return layers;
+};
+
+
+/**
+ * Get the pixel for a coordinate. This takes a coordinate in the map view
+ * projection and returns the corresponding pixel.
+ * @param {ol.Coordinate} coordinate A map coordinate.
+ * @return {ol.Pixel} A pixel position in the map viewport.
+ * @api stable
+ */
+ol.Map.prototype.getPixelFromCoordinate = function(coordinate) {
+ var frameState = this.frameState_;
+ if (!frameState) {
+ return null;
+ } else {
+ return ol.transform.apply(frameState.coordinateToPixelTransform,
+ coordinate.slice(0, 2));
+ }
+};
+
+
+/**
+ * Get the map renderer.
+ * @return {ol.renderer.Map} Renderer
+ */
+ol.Map.prototype.getRenderer = function() {
+ return this.renderer_;
+};
+
+
+/**
+ * Get the size of this map.
+ * @return {ol.Size|undefined} The size in pixels of the map in the DOM.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.getSize = function() {
+ return /** @type {ol.Size|undefined} */ (this.get(ol.Map.Property.SIZE));
+};
+
+
+/**
+ * Get the view associated with this map. A view manages properties such as
+ * center and resolution.
+ * @return {ol.View} The view that controls this map.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.getView = function() {
+ return /** @type {ol.View} */ (this.get(ol.Map.Property.VIEW));
+};
+
+
+/**
+ * Get the element that serves as the map viewport.
+ * @return {Element} Viewport.
+ * @api stable
+ */
+ol.Map.prototype.getViewport = function() {
+ return this.viewport_;
+};
+
+
+/**
+ * Get the element that serves as the container for overlays. Elements added to
+ * this container will let mousedown and touchstart events through to the map,
+ * so clicks and gestures on an overlay will trigger {@link ol.MapBrowserEvent}
+ * events.
+ * @return {!Element} The map's overlay container.
+ */
+ol.Map.prototype.getOverlayContainer = function() {
+ return this.overlayContainer_;
+};
+
+
+/**
+ * Get the element that serves as a container for overlays that don't allow
+ * event propagation. Elements added to this container won't let mousedown and
+ * touchstart events through to the map, so clicks and gestures on an overlay
+ * don't trigger any {@link ol.MapBrowserEvent}.
+ * @return {!Element} The map's overlay container that stops events.
+ */
+ol.Map.prototype.getOverlayContainerStopEvent = function() {
+ return this.overlayContainerStopEvent_;
+};
+
+
+/**
+ * @param {ol.Tile} tile Tile.
+ * @param {string} tileSourceKey Tile source key.
+ * @param {ol.Coordinate} tileCenter Tile center.
+ * @param {number} tileResolution Tile resolution.
+ * @return {number} Tile priority.
+ */
+ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter, tileResolution) {
+ // Filter out tiles at higher zoom levels than the current zoom level, or that
+ // are outside the visible extent.
+ var frameState = this.frameState_;
+ if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
+ return ol.structs.PriorityQueue.DROP;
+ }
+ if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
+ return ol.structs.PriorityQueue.DROP;
+ }
+ // Prioritize the highest zoom level tiles closest to the focus.
+ // Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
+ // Within a zoom level, tiles are prioritized by the distance in pixels
+ // between the center of the tile and the focus. The factor of 65536 means
+ // that the prioritization should behave as desired for tiles up to
+ // 65536 * Math.log(2) = 45426 pixels from the focus.
+ var deltaX = tileCenter[0] - frameState.focus[0];
+ var deltaY = tileCenter[1] - frameState.focus[1];
+ return 65536 * Math.log(tileResolution) +
+ Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
+};
+
+
+/**
+ * @param {Event} browserEvent Browser event.
+ * @param {string=} opt_type Type.
+ */
+ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) {
+ var type = opt_type || browserEvent.type;
+ var mapBrowserEvent = new ol.MapBrowserEvent(type, this, browserEvent);
+ this.handleMapBrowserEvent(mapBrowserEvent);
+};
+
+
+/**
+ * @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle.
+ */
+ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
+ if (!this.frameState_) {
+ // With no view defined, we cannot translate pixels into geographical
+ // coordinates so interactions cannot be used.
+ return;
+ }
+ this.focus_ = mapBrowserEvent.coordinate;
+ mapBrowserEvent.frameState = this.frameState_;
+ var interactionsArray = this.getInteractions().getArray();
+ var i;
+ if (this.dispatchEvent(mapBrowserEvent) !== false) {
+ for (i = interactionsArray.length - 1; i >= 0; i--) {
+ var interaction = interactionsArray[i];
+ if (!interaction.getActive()) {
+ continue;
+ }
+ var cont = interaction.handleEvent(mapBrowserEvent);
+ if (!cont) {
+ break;
+ }
+ }
+ }
+};
+
+
+/**
+ * @protected
+ */
+ol.Map.prototype.handlePostRender = function() {
+
+ var frameState = this.frameState_;
+
+ // Manage the tile queue
+ // Image loads are expensive and a limited resource, so try to use them
+ // efficiently:
+ // * When the view is static we allow a large number of parallel tile loads
+ // to complete the frame as quickly as possible.
+ // * When animating or interacting, image loads can cause janks, so we reduce
+ // the maximum number of loads per frame and limit the number of parallel
+ // tile loads to remain reactive to view changes and to reduce the chance of
+ // loading tiles that will quickly disappear from view.
+ var tileQueue = this.tileQueue_;
+ if (!tileQueue.isEmpty()) {
+ var maxTotalLoading = 16;
+ var maxNewLoads = maxTotalLoading;
+ if (frameState) {
+ var hints = frameState.viewHints;
+ if (hints[ol.View.Hint.ANIMATING]) {
+ maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0;
+ maxNewLoads = 2;
+ }
+ if (hints[ol.View.Hint.INTERACTING]) {
+ maxTotalLoading = this.loadTilesWhileInteracting_ ? 8 : 0;
+ maxNewLoads = 2;
+ }
+ }
+ if (tileQueue.getTilesLoading() < maxTotalLoading) {
+ tileQueue.reprioritize(); // FIXME only call if view has changed
+ tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
+ }
+ }
+
+ var postRenderFunctions = this.postRenderFunctions_;
+ var i, ii;
+ for (i = 0, ii = postRenderFunctions.length; i < ii; ++i) {
+ postRenderFunctions[i](this, frameState);
+ }
+ postRenderFunctions.length = 0;
+};
+
+
+/**
+ * @private
+ */
+ol.Map.prototype.handleSizeChanged_ = function() {
+ this.render();
+};
+
+
+/**
+ * @private
+ */
+ol.Map.prototype.handleTargetChanged_ = function() {
+ // target may be undefined, null, a string or an Element.
+ // If it's a string we convert it to an Element before proceeding.
+ // If it's not now an Element we remove the viewport from the DOM.
+ // If it's an Element we append the viewport element to it.
+
+ var targetElement;
+ if (this.getTarget()) {
+ targetElement = this.getTargetElement();
+ ol.DEBUG && console.assert(targetElement !== null,
+ 'expects a non-null value for targetElement');
+ }
+
+ if (this.keyHandlerKeys_) {
+ for (var i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) {
+ ol.events.unlistenByKey(this.keyHandlerKeys_[i]);
+ }
+ this.keyHandlerKeys_ = null;
+ }
+
+ if (!targetElement) {
+ ol.dom.removeNode(this.viewport_);
+ if (this.handleResize_ !== undefined) {
+ window.removeEventListener(ol.events.EventType.RESIZE,
+ this.handleResize_, false);
+ this.handleResize_ = undefined;
+ }
+ } else {
+ targetElement.appendChild(this.viewport_);
+
+ var keyboardEventTarget = !this.keyboardEventTarget_ ?
+ targetElement : this.keyboardEventTarget_;
+ this.keyHandlerKeys_ = [
+ ol.events.listen(keyboardEventTarget, ol.events.EventType.KEYDOWN,
+ this.handleBrowserEvent, this),
+ ol.events.listen(keyboardEventTarget, ol.events.EventType.KEYPRESS,
+ this.handleBrowserEvent, this)
+ ];
+
+ if (!this.handleResize_) {
+ this.handleResize_ = this.updateSize.bind(this);
+ window.addEventListener(ol.events.EventType.RESIZE,
+ this.handleResize_, false);
+ }
+ }
+
+ this.updateSize();
+ // updateSize calls setSize, so no need to call this.render
+ // ourselves here.
+};
+
+
+/**
+ * @private
+ */
+ol.Map.prototype.handleTileChange_ = function() {
+ this.render();
+};
+
+
+/**
+ * @private
+ */
+ol.Map.prototype.handleViewPropertyChanged_ = function() {
+ this.render();
+};
+
+
+/**
+ * @private
+ */
+ol.Map.prototype.handleViewChanged_ = function() {
+ if (this.viewPropertyListenerKey_) {
+ ol.events.unlistenByKey(this.viewPropertyListenerKey_);
+ this.viewPropertyListenerKey_ = null;
+ }
+ if (this.viewChangeListenerKey_) {
+ ol.events.unlistenByKey(this.viewChangeListenerKey_);
+ this.viewChangeListenerKey_ = null;
+ }
+ var view = this.getView();
+ if (view) {
+ this.viewPropertyListenerKey_ = ol.events.listen(
+ view, ol.Object.EventType.PROPERTYCHANGE,
+ this.handleViewPropertyChanged_, this);
+ this.viewChangeListenerKey_ = ol.events.listen(
+ view, ol.events.EventType.CHANGE,
+ this.handleViewPropertyChanged_, this);
+ }
+ this.render();
+};
+
+
+/**
+ * @private
+ */
+ol.Map.prototype.handleLayerGroupChanged_ = function() {
+ if (this.layerGroupPropertyListenerKeys_) {
+ this.layerGroupPropertyListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.layerGroupPropertyListenerKeys_ = null;
+ }
+ var layerGroup = this.getLayerGroup();
+ if (layerGroup) {
+ this.layerGroupPropertyListenerKeys_ = [
+ ol.events.listen(
+ layerGroup, ol.Object.EventType.PROPERTYCHANGE,
+ this.render, this),
+ ol.events.listen(
+ layerGroup, ol.events.EventType.CHANGE,
+ this.render, this)
+ ];
+ }
+ this.render();
+};
+
+
+/**
+ * @return {boolean} Is rendered.
+ */
+ol.Map.prototype.isRendered = function() {
+ return !!this.frameState_;
+};
+
+
+/**
+ * Requests an immediate render in a synchronous manner.
+ * @api stable
+ */
+ol.Map.prototype.renderSync = function() {
+ if (this.animationDelayKey_) {
+ cancelAnimationFrame(this.animationDelayKey_);
+ }
+ this.animationDelay_();
+};
+
+
+/**
+ * Request a map rendering (at the next animation frame).
+ * @api stable
+ */
+ol.Map.prototype.render = function() {
+ if (this.animationDelayKey_ === undefined) {
+ this.animationDelayKey_ = requestAnimationFrame(
+ this.animationDelay_);
+ }
+};
+
+
+/**
+ * Remove the given control from the map.
+ * @param {ol.control.Control} control Control.
+ * @return {ol.control.Control|undefined} The removed control (or undefined
+ * if the control was not found).
+ * @api stable
+ */
+ol.Map.prototype.removeControl = function(control) {
+ return this.getControls().remove(control);
+};
+
+
+/**
+ * Remove the given interaction from the map.
+ * @param {ol.interaction.Interaction} interaction Interaction to remove.
+ * @return {ol.interaction.Interaction|undefined} The removed interaction (or
+ * undefined if the interaction was not found).
+ * @api stable
+ */
+ol.Map.prototype.removeInteraction = function(interaction) {
+ return this.getInteractions().remove(interaction);
+};
+
+
+/**
+ * Removes the given layer from the map.
+ * @param {ol.layer.Base} layer Layer.
+ * @return {ol.layer.Base|undefined} The removed layer (or undefined if the
+ * layer was not found).
+ * @api stable
+ */
+ol.Map.prototype.removeLayer = function(layer) {
+ var layers = this.getLayerGroup().getLayers();
+ return layers.remove(layer);
+};
+
+
+/**
+ * Remove the given overlay from the map.
+ * @param {ol.Overlay} overlay Overlay.
+ * @return {ol.Overlay|undefined} The removed overlay (or undefined
+ * if the overlay was not found).
+ * @api stable
+ */
+ol.Map.prototype.removeOverlay = function(overlay) {
+ return this.getOverlays().remove(overlay);
+};
+
+
+/**
+ * @param {number} time Time.
+ * @private
+ */
+ol.Map.prototype.renderFrame_ = function(time) {
+ var i, ii, viewState;
+
+ var size = this.getSize();
+ var view = this.getView();
+ var extent = ol.extent.createEmpty();
+ /** @type {?olx.FrameState} */
+ var frameState = null;
+ if (size !== undefined && ol.size.hasArea(size) && view && view.isDef()) {
+ var viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
+ var layerStatesArray = this.getLayerGroup().getLayerStatesArray();
+ var layerStates = {};
+ for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
+ layerStates[ol.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
+ }
+ viewState = view.getState();
+ frameState = /** @type {olx.FrameState} */ ({
+ animate: false,
+ attributions: {},
+ coordinateToPixelTransform: this.coordinateToPixelTransform_,
+ extent: extent,
+ focus: !this.focus_ ? viewState.center : this.focus_,
+ index: this.frameIndex_++,
+ layerStates: layerStates,
+ layerStatesArray: layerStatesArray,
+ logos: ol.obj.assign({}, this.logos_),
+ pixelRatio: this.pixelRatio_,
+ pixelToCoordinateTransform: this.pixelToCoordinateTransform_,
+ postRenderFunctions: [],
+ size: size,
+ skippedFeatureUids: this.skippedFeatureUids_,
+ tileQueue: this.tileQueue_,
+ time: time,
+ usedTiles: {},
+ viewState: viewState,
+ viewHints: viewHints,
+ wantedTiles: {}
+ });
+ }
+
+ if (frameState) {
+ var preRenderFunctions = this.preRenderFunctions_;
+ var n = 0, preRenderFunction;
+ for (i = 0, ii = preRenderFunctions.length; i < ii; ++i) {
+ preRenderFunction = preRenderFunctions[i];
+ if (preRenderFunction(this, frameState)) {
+ preRenderFunctions[n++] = preRenderFunction;
+ }
+ }
+ preRenderFunctions.length = n;
+
+ frameState.extent = ol.extent.getForViewAndSize(viewState.center,
+ viewState.resolution, viewState.rotation, frameState.size, extent);
+ }
+
+ this.frameState_ = frameState;
+ this.renderer_.renderFrame(frameState);
+
+ if (frameState) {
+ if (frameState.animate) {
+ this.render();
+ }
+ Array.prototype.push.apply(
+ this.postRenderFunctions_, frameState.postRenderFunctions);
+
+ var idle = this.preRenderFunctions_.length === 0 &&
+ !frameState.viewHints[ol.View.Hint.ANIMATING] &&
+ !frameState.viewHints[ol.View.Hint.INTERACTING] &&
+ !ol.extent.equals(frameState.extent, this.previousExtent_);
+
+ if (idle) {
+ this.dispatchEvent(
+ new ol.MapEvent(ol.MapEvent.Type.MOVEEND, this, frameState));
+ ol.extent.clone(frameState.extent, this.previousExtent_);
+ }
+ }
+
+ this.dispatchEvent(
+ new ol.MapEvent(ol.MapEvent.Type.POSTRENDER, this, frameState));
+
+ setTimeout(this.handlePostRender.bind(this), 0);
+
+};
+
+
+/**
+ * Sets the layergroup of this map.
+ * @param {ol.layer.Group} layerGroup A layer group containing the layers in
+ * this map.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.setLayerGroup = function(layerGroup) {
+ this.set(ol.Map.Property.LAYERGROUP, layerGroup);
+};
+
+
+/**
+ * Set the size of this map.
+ * @param {ol.Size|undefined} size The size in pixels of the map in the DOM.
+ * @observable
+ * @api
+ */
+ol.Map.prototype.setSize = function(size) {
+ this.set(ol.Map.Property.SIZE, size);
+};
+
+
+/**
+ * Set the target element to render this map into.
+ * @param {Element|string|undefined} target The Element or id of the Element
+ * that the map is rendered in.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.setTarget = function(target) {
+ this.set(ol.Map.Property.TARGET, target);
+};
+
+
+/**
+ * Set the view for this map.
+ * @param {ol.View} view The view that controls this map.
+ * @observable
+ * @api stable
+ */
+ol.Map.prototype.setView = function(view) {
+ this.set(ol.Map.Property.VIEW, view);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ */
+ol.Map.prototype.skipFeature = function(feature) {
+ var featureUid = ol.getUid(feature).toString();
+ this.skippedFeatureUids_[featureUid] = true;
+ this.render();
+};
+
+
+/**
+ * Force a recalculation of the map viewport size. This should be called when
+ * third-party code changes the size of the map viewport.
+ * @api stable
+ */
+ol.Map.prototype.updateSize = function() {
+ var targetElement = this.getTargetElement();
+
+ if (!targetElement) {
+ this.setSize(undefined);
+ } else {
+ var computedStyle = getComputedStyle(targetElement);
+ this.setSize([
+ targetElement.offsetWidth -
+ parseFloat(computedStyle['borderLeftWidth']) -
+ parseFloat(computedStyle['paddingLeft']) -
+ parseFloat(computedStyle['paddingRight']) -
+ parseFloat(computedStyle['borderRightWidth']),
+ targetElement.offsetHeight -
+ parseFloat(computedStyle['borderTopWidth']) -
+ parseFloat(computedStyle['paddingTop']) -
+ parseFloat(computedStyle['paddingBottom']) -
+ parseFloat(computedStyle['borderBottomWidth'])
+ ]);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ */
+ol.Map.prototype.unskipFeature = function(feature) {
+ var featureUid = ol.getUid(feature).toString();
+ delete this.skippedFeatureUids_[featureUid];
+ this.render();
+};
+
+
+/**
+ * @param {olx.MapOptions} options Map options.
+ * @return {ol.MapOptionsInternal} Internal map options.
+ */
+ol.Map.createOptionsInternal = function(options) {
+
+ /**
+ * @type {Element|Document}
+ */
+ var keyboardEventTarget = null;
+ if (options.keyboardEventTarget !== undefined) {
+ keyboardEventTarget = typeof options.keyboardEventTarget === 'string' ?
+ document.getElementById(options.keyboardEventTarget) :
+ options.keyboardEventTarget;
+ }
+
+ /**
+ * @type {Object.<string, *>}
+ */
+ var values = {};
+
+ var logos = {};
+ if (options.logo === undefined ||
+ (typeof options.logo === 'boolean' && options.logo)) {
+ logos[ol.OL3_LOGO_URL] = ol.OL3_URL;
+ } else {
+ var logo = options.logo;
+ if (typeof logo === 'string') {
+ logos[logo] = '';
+ } else if (logo instanceof HTMLElement) {
+ logos[ol.getUid(logo).toString()] = logo;
+ } else if (logo) {
+ ol.asserts.assert(typeof logo.href == 'string', 44); // `logo.href` should be a string.
+ ol.asserts.assert(typeof logo.src == 'string', 45); // `logo.src` should be a string.
+ logos[logo.src] = logo.href;
+ }
+ }
+
+ var layerGroup = (options.layers instanceof ol.layer.Group) ?
+ options.layers : new ol.layer.Group({layers: options.layers});
+ values[ol.Map.Property.LAYERGROUP] = layerGroup;
+
+ values[ol.Map.Property.TARGET] = options.target;
+
+ values[ol.Map.Property.VIEW] = options.view !== undefined ?
+ options.view : new ol.View();
+
+ /**
+ * @type {function(new: ol.renderer.Map, Element, ol.Map)}
+ */
+ var rendererConstructor = ol.renderer.Map;
+
+ /**
+ * @type {Array.<ol.renderer.Type>}
+ */
+ var rendererTypes;
+ if (options.renderer !== undefined) {
+ if (Array.isArray(options.renderer)) {
+ rendererTypes = options.renderer;
+ } else if (typeof options.renderer === 'string') {
+ rendererTypes = [options.renderer];
+ } else {
+ ol.asserts.assert(false, 46); // Incorrect format for `renderer` option
+ }
+ if (rendererTypes.indexOf(/** @type {ol.renderer.Type} */ ('dom')) >= 0) {
+ ol.DEBUG && console.assert(false, 'The DOM render has been removed');
+ rendererTypes = rendererTypes.concat(ol.DEFAULT_RENDERER_TYPES);
+ }
+ } else {
+ rendererTypes = ol.DEFAULT_RENDERER_TYPES;
+ }
+
+ var i, ii;
+ for (i = 0, ii = rendererTypes.length; i < ii; ++i) {
+ /** @type {ol.renderer.Type} */
+ var rendererType = rendererTypes[i];
+ if (ol.ENABLE_CANVAS && rendererType == ol.renderer.Type.CANVAS) {
+ if (ol.has.CANVAS) {
+ rendererConstructor = ol.renderer.canvas.Map;
+ break;
+ }
+ } else if (ol.ENABLE_WEBGL && rendererType == ol.renderer.Type.WEBGL) {
+ if (ol.has.WEBGL) {
+ rendererConstructor = ol.renderer.webgl.Map;
+ break;
+ }
+ }
+ }
+
+ var controls;
+ if (options.controls !== undefined) {
+ if (Array.isArray(options.controls)) {
+ controls = new ol.Collection(options.controls.slice());
+ } else {
+ ol.asserts.assert(options.controls instanceof ol.Collection,
+ 47); // Expected `controls` to be an array or an `ol.Collection`
+ controls = options.controls;
+ }
+ } else {
+ controls = ol.control.defaults();
+ }
+
+ var interactions;
+ if (options.interactions !== undefined) {
+ if (Array.isArray(options.interactions)) {
+ interactions = new ol.Collection(options.interactions.slice());
+ } else {
+ ol.asserts.assert(options.interactions instanceof ol.Collection,
+ 48); // Expected `interactions` to be an array or an `ol.Collection`
+ interactions = options.interactions;
+ }
+ } else {
+ interactions = ol.interaction.defaults();
+ }
+
+ var overlays;
+ if (options.overlays !== undefined) {
+ if (Array.isArray(options.overlays)) {
+ overlays = new ol.Collection(options.overlays.slice());
+ } else {
+ ol.asserts.assert(options.overlays instanceof ol.Collection,
+ 49); // Expected `overlays` to be an array or an `ol.Collection`
+ overlays = options.overlays;
+ }
+ } else {
+ overlays = new ol.Collection();
+ }
+
+ return {
+ controls: controls,
+ interactions: interactions,
+ keyboardEventTarget: keyboardEventTarget,
+ logos: logos,
+ overlays: overlays,
+ rendererConstructor: rendererConstructor,
+ values: values
+ };
+
+};
+
+/**
+ * @enum {string}
+ */
+ol.Map.Property = {
+ LAYERGROUP: 'layergroup',
+ SIZE: 'size',
+ TARGET: 'target',
+ VIEW: 'view'
+};
+
+
+ol.proj.common.add();
+
+goog.provide('ol.Overlay');
+
+goog.require('ol');
+goog.require('ol.MapEvent');
+goog.require('ol.Object');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.extent');
+
+
+/**
+ * @classdesc
+ * An element to be displayed over the map and attached to a single map
+ * location. Like {@link ol.control.Control}, Overlays are visible widgets.
+ * Unlike Controls, they are not in a fixed position on the screen, but are tied
+ * to a geographical coordinate, so panning the map will move an Overlay but not
+ * a Control.
+ *
+ * Example:
+ *
+ * var popup = new ol.Overlay({
+ * element: document.getElementById('popup')
+ * });
+ * popup.setPosition(coordinate);
+ * map.addOverlay(popup);
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {olx.OverlayOptions} options Overlay options.
+ * @api stable
+ */
+ol.Overlay = function(options) {
+
+ ol.Object.call(this);
+
+ /**
+ * @private
+ * @type {number|string|undefined}
+ */
+ this.id_ = options.id;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.insertFirst_ = options.insertFirst !== undefined ?
+ options.insertFirst : true;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.stopEvent_ = options.stopEvent !== undefined ? options.stopEvent : true;
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.element_ = document.createElement('DIV');
+ this.element_.className = 'ol-overlay-container';
+ this.element_.style.position = 'absolute';
+
+ /**
+ * @protected
+ * @type {boolean}
+ */
+ this.autoPan = options.autoPan !== undefined ? options.autoPan : false;
+
+ /**
+ * @private
+ * @type {olx.OverlayPanOptions}
+ */
+ this.autoPanAnimation_ = options.autoPanAnimation ||
+ /** @type {olx.OverlayPanOptions} */ ({});
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.autoPanMargin_ = options.autoPanMargin !== undefined ?
+ options.autoPanMargin : 20;
+
+ /**
+ * @private
+ * @type {{bottom_: string,
+ * left_: string,
+ * right_: string,
+ * top_: string,
+ * visible: boolean}}
+ */
+ this.rendered_ = {
+ bottom_: '',
+ left_: '',
+ right_: '',
+ top_: '',
+ visible: true
+ };
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.mapPostrenderListenerKey_ = null;
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Overlay.Property.ELEMENT),
+ this.handleElementChanged, this);
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Overlay.Property.MAP),
+ this.handleMapChanged, this);
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Overlay.Property.OFFSET),
+ this.handleOffsetChanged, this);
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Overlay.Property.POSITION),
+ this.handlePositionChanged, this);
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Overlay.Property.POSITIONING),
+ this.handlePositioningChanged, this);
+
+ if (options.element !== undefined) {
+ this.setElement(options.element);
+ }
+
+ this.setOffset(options.offset !== undefined ? options.offset : [0, 0]);
+
+ this.setPositioning(options.positioning !== undefined ?
+ /** @type {ol.Overlay.Positioning} */ (options.positioning) :
+ ol.Overlay.Positioning.TOP_LEFT);
+
+ if (options.position !== undefined) {
+ this.setPosition(options.position);
+ }
+
+};
+ol.inherits(ol.Overlay, ol.Object);
+
+
+/**
+ * Get the DOM element of this overlay.
+ * @return {Element|undefined} The Element containing the overlay.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.getElement = function() {
+ return /** @type {Element|undefined} */ (
+ this.get(ol.Overlay.Property.ELEMENT));
+};
+
+
+/**
+ * Get the overlay identifier which is set on constructor.
+ * @return {number|string|undefined} Id.
+ * @api
+ */
+ol.Overlay.prototype.getId = function() {
+ return this.id_;
+};
+
+
+/**
+ * Get the map associated with this overlay.
+ * @return {ol.Map|undefined} The map that the overlay is part of.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.getMap = function() {
+ return /** @type {ol.Map|undefined} */ (
+ this.get(ol.Overlay.Property.MAP));
+};
+
+
+/**
+ * Get the offset of this overlay.
+ * @return {Array.<number>} The offset.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.getOffset = function() {
+ return /** @type {Array.<number>} */ (
+ this.get(ol.Overlay.Property.OFFSET));
+};
+
+
+/**
+ * Get the current position of this overlay.
+ * @return {ol.Coordinate|undefined} The spatial point that the overlay is
+ * anchored at.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.getPosition = function() {
+ return /** @type {ol.Coordinate|undefined} */ (
+ this.get(ol.Overlay.Property.POSITION));
+};
+
+
+/**
+ * Get the current positioning of this overlay.
+ * @return {ol.Overlay.Positioning} How the overlay is positioned
+ * relative to its point on the map.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.getPositioning = function() {
+ return /** @type {ol.Overlay.Positioning} */ (
+ this.get(ol.Overlay.Property.POSITIONING));
+};
+
+
+/**
+ * @protected
+ */
+ol.Overlay.prototype.handleElementChanged = function() {
+ ol.dom.removeChildren(this.element_);
+ var element = this.getElement();
+ if (element) {
+ this.element_.appendChild(element);
+ }
+};
+
+
+/**
+ * @protected
+ */
+ol.Overlay.prototype.handleMapChanged = function() {
+ if (this.mapPostrenderListenerKey_) {
+ ol.dom.removeNode(this.element_);
+ ol.events.unlistenByKey(this.mapPostrenderListenerKey_);
+ this.mapPostrenderListenerKey_ = null;
+ }
+ var map = this.getMap();
+ if (map) {
+ this.mapPostrenderListenerKey_ = ol.events.listen(map,
+ ol.MapEvent.Type.POSTRENDER, this.render, this);
+ this.updatePixelPosition();
+ var container = this.stopEvent_ ?
+ map.getOverlayContainerStopEvent() : map.getOverlayContainer();
+ if (this.insertFirst_) {
+ container.insertBefore(this.element_, container.childNodes[0] || null);
+ } else {
+ container.appendChild(this.element_);
+ }
+ }
+};
+
+
+/**
+ * @protected
+ */
+ol.Overlay.prototype.render = function() {
+ this.updatePixelPosition();
+};
+
+
+/**
+ * @protected
+ */
+ol.Overlay.prototype.handleOffsetChanged = function() {
+ this.updatePixelPosition();
+};
+
+
+/**
+ * @protected
+ */
+ol.Overlay.prototype.handlePositionChanged = function() {
+ this.updatePixelPosition();
+ if (this.get(ol.Overlay.Property.POSITION) !== undefined && this.autoPan) {
+ this.panIntoView_();
+ }
+};
+
+
+/**
+ * @protected
+ */
+ol.Overlay.prototype.handlePositioningChanged = function() {
+ this.updatePixelPosition();
+};
+
+
+/**
+ * Set the DOM element to be associated with this overlay.
+ * @param {Element|undefined} element The Element containing the overlay.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.setElement = function(element) {
+ this.set(ol.Overlay.Property.ELEMENT, element);
+};
+
+
+/**
+ * Set the map to be associated with this overlay.
+ * @param {ol.Map|undefined} map The map that the overlay is part of.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.setMap = function(map) {
+ this.set(ol.Overlay.Property.MAP, map);
+};
+
+
+/**
+ * Set the offset for this overlay.
+ * @param {Array.<number>} offset Offset.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.setOffset = function(offset) {
+ this.set(ol.Overlay.Property.OFFSET, offset);
+};
+
+
+/**
+ * Set the position for this overlay. If the position is `undefined` the
+ * overlay is hidden.
+ * @param {ol.Coordinate|undefined} position The spatial point that the overlay
+ * is anchored at.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.setPosition = function(position) {
+ this.set(ol.Overlay.Property.POSITION, position);
+};
+
+
+/**
+ * Pan the map so that the overlay is entirely visible in the current viewport
+ * (if necessary).
+ * @private
+ */
+ol.Overlay.prototype.panIntoView_ = function() {
+ var map = this.getMap();
+
+ if (map === undefined || !map.getTargetElement()) {
+ return;
+ }
+
+ var mapRect = this.getRect_(map.getTargetElement(), map.getSize());
+ var element = /** @type {!Element} */ (this.getElement());
+ var overlayRect = this.getRect_(element,
+ [ol.dom.outerWidth(element), ol.dom.outerHeight(element)]);
+
+ var margin = this.autoPanMargin_;
+ if (!ol.extent.containsExtent(mapRect, overlayRect)) {
+ // the overlay is not completely inside the viewport, so pan the map
+ var offsetLeft = overlayRect[0] - mapRect[0];
+ var offsetRight = mapRect[2] - overlayRect[2];
+ var offsetTop = overlayRect[1] - mapRect[1];
+ var offsetBottom = mapRect[3] - overlayRect[3];
+
+ var delta = [0, 0];
+ if (offsetLeft < 0) {
+ // move map to the left
+ delta[0] = offsetLeft - margin;
+ } else if (offsetRight < 0) {
+ // move map to the right
+ delta[0] = Math.abs(offsetRight) + margin;
+ }
+ if (offsetTop < 0) {
+ // move map up
+ delta[1] = offsetTop - margin;
+ } else if (offsetBottom < 0) {
+ // move map down
+ delta[1] = Math.abs(offsetBottom) + margin;
+ }
+
+ if (delta[0] !== 0 || delta[1] !== 0) {
+ var center = /** @type {ol.Coordinate} */ (map.getView().getCenter());
+ var centerPx = map.getPixelFromCoordinate(center);
+ var newCenterPx = [
+ centerPx[0] + delta[0],
+ centerPx[1] + delta[1]
+ ];
+
+ map.getView().animate({
+ center: map.getCoordinateFromPixel(newCenterPx),
+ duration: this.autoPanAnimation_.duration,
+ easing: this.autoPanAnimation_.easing
+ });
+ }
+ }
+};
+
+
+/**
+ * Get the extent of an element relative to the document
+ * @param {Element|undefined} element The element.
+ * @param {ol.Size|undefined} size The size of the element.
+ * @return {ol.Extent} The extent.
+ * @private
+ */
+ol.Overlay.prototype.getRect_ = function(element, size) {
+ var box = element.getBoundingClientRect();
+ var offsetX = box.left + window.pageXOffset;
+ var offsetY = box.top + window.pageYOffset;
+ return [
+ offsetX,
+ offsetY,
+ offsetX + size[0],
+ offsetY + size[1]
+ ];
+};
+
+
+/**
+ * Set the positioning for this overlay.
+ * @param {ol.Overlay.Positioning} positioning how the overlay is
+ * positioned relative to its point on the map.
+ * @observable
+ * @api stable
+ */
+ol.Overlay.prototype.setPositioning = function(positioning) {
+ this.set(ol.Overlay.Property.POSITIONING, positioning);
+};
+
+
+/**
+ * Modify the visibility of the element.
+ * @param {boolean} visible Element visibility.
+ * @protected
+ */
+ol.Overlay.prototype.setVisible = function(visible) {
+ if (this.rendered_.visible !== visible) {
+ this.element_.style.display = visible ? '' : 'none';
+ this.rendered_.visible = visible;
+ }
+};
+
+
+/**
+ * Update pixel position.
+ * @protected
+ */
+ol.Overlay.prototype.updatePixelPosition = function() {
+ var map = this.getMap();
+ var position = this.getPosition();
+ if (map === undefined || !map.isRendered() || position === undefined) {
+ this.setVisible(false);
+ return;
+ }
+
+ var pixel = map.getPixelFromCoordinate(position);
+ var mapSize = map.getSize();
+ this.updateRenderedPosition(pixel, mapSize);
+};
+
+
+/**
+ * @param {ol.Pixel} pixel The pixel location.
+ * @param {ol.Size|undefined} mapSize The map size.
+ * @protected
+ */
+ol.Overlay.prototype.updateRenderedPosition = function(pixel, mapSize) {
+ var style = this.element_.style;
+ var offset = this.getOffset();
+
+ var positioning = this.getPositioning();
+ ol.DEBUG && console.assert(positioning !== undefined,
+ 'positioning should be defined');
+
+ var offsetX = offset[0];
+ var offsetY = offset[1];
+ if (positioning == ol.Overlay.Positioning.BOTTOM_RIGHT ||
+ positioning == ol.Overlay.Positioning.CENTER_RIGHT ||
+ positioning == ol.Overlay.Positioning.TOP_RIGHT) {
+ if (this.rendered_.left_ !== '') {
+ this.rendered_.left_ = style.left = '';
+ }
+ var right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px';
+ if (this.rendered_.right_ != right) {
+ this.rendered_.right_ = style.right = right;
+ }
+ } else {
+ if (this.rendered_.right_ !== '') {
+ this.rendered_.right_ = style.right = '';
+ }
+ if (positioning == ol.Overlay.Positioning.BOTTOM_CENTER ||
+ positioning == ol.Overlay.Positioning.CENTER_CENTER ||
+ positioning == ol.Overlay.Positioning.TOP_CENTER) {
+ offsetX -= this.element_.offsetWidth / 2;
+ }
+ var left = Math.round(pixel[0] + offsetX) + 'px';
+ if (this.rendered_.left_ != left) {
+ this.rendered_.left_ = style.left = left;
+ }
+ }
+ if (positioning == ol.Overlay.Positioning.BOTTOM_LEFT ||
+ positioning == ol.Overlay.Positioning.BOTTOM_CENTER ||
+ positioning == ol.Overlay.Positioning.BOTTOM_RIGHT) {
+ if (this.rendered_.top_ !== '') {
+ this.rendered_.top_ = style.top = '';
+ }
+ var bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px';
+ if (this.rendered_.bottom_ != bottom) {
+ this.rendered_.bottom_ = style.bottom = bottom;
+ }
+ } else {
+ if (this.rendered_.bottom_ !== '') {
+ this.rendered_.bottom_ = style.bottom = '';
+ }
+ if (positioning == ol.Overlay.Positioning.CENTER_LEFT ||
+ positioning == ol.Overlay.Positioning.CENTER_CENTER ||
+ positioning == ol.Overlay.Positioning.CENTER_RIGHT) {
+ offsetY -= this.element_.offsetHeight / 2;
+ }
+ var top = Math.round(pixel[1] + offsetY) + 'px';
+ if (this.rendered_.top_ != top) {
+ this.rendered_.top_ = style.top = top;
+ }
+ }
+
+ this.setVisible(true);
+};
+
+
+/**
+ * Overlay position: `'bottom-left'`, `'bottom-center'`, `'bottom-right'`,
+ * `'center-left'`, `'center-center'`, `'center-right'`, `'top-left'`,
+ * `'top-center'`, `'top-right'`
+ * @enum {string}
+ */
+ol.Overlay.Positioning = {
+ BOTTOM_LEFT: 'bottom-left',
+ BOTTOM_CENTER: 'bottom-center',
+ BOTTOM_RIGHT: 'bottom-right',
+ CENTER_LEFT: 'center-left',
+ CENTER_CENTER: 'center-center',
+ CENTER_RIGHT: 'center-right',
+ TOP_LEFT: 'top-left',
+ TOP_CENTER: 'top-center',
+ TOP_RIGHT: 'top-right'
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.Overlay.Property = {
+ ELEMENT: 'element',
+ MAP: 'map',
+ OFFSET: 'offset',
+ POSITION: 'position',
+ POSITIONING: 'positioning'
+};
+
+goog.provide('ol.control.OverviewMap');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.Map');
+goog.require('ol.MapEvent');
+goog.require('ol.Object');
+goog.require('ol.Overlay');
+goog.require('ol.View');
+goog.require('ol.control.Control');
+goog.require('ol.coordinate');
+goog.require('ol.css');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+
+
+/**
+ * Create a new control with a map acting as an overview map for an other
+ * defined map.
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.OverviewMapOptions=} opt_options OverviewMap options.
+ * @api
+ */
+ol.control.OverviewMap = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.collapsible_ = options.collapsible !== undefined ?
+ options.collapsible : true;
+
+ if (!this.collapsible_) {
+ this.collapsed_ = false;
+ }
+
+ var className = options.className !== undefined ? options.className : 'ol-overviewmap';
+
+ var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map';
+
+ var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB';
+
+ if (typeof collapseLabel === 'string') {
+ /**
+ * @private
+ * @type {Node}
+ */
+ this.collapseLabel_ = document.createElement('span');
+ this.collapseLabel_.textContent = collapseLabel;
+ } else {
+ this.collapseLabel_ = collapseLabel;
+ }
+
+ var label = options.label !== undefined ? options.label : '\u00BB';
+
+
+ if (typeof label === 'string') {
+ /**
+ * @private
+ * @type {Node}
+ */
+ this.label_ = document.createElement('span');
+ this.label_.textContent = label;
+ } else {
+ this.label_ = label;
+ }
+
+ var activeLabel = (this.collapsible_ && !this.collapsed_) ?
+ this.collapseLabel_ : this.label_;
+ var button = document.createElement('button');
+ button.setAttribute('type', 'button');
+ button.title = tipLabel;
+ button.appendChild(activeLabel);
+
+ ol.events.listen(button, ol.events.EventType.CLICK,
+ this.handleClick_, this);
+
+ var ovmapDiv = document.createElement('DIV');
+ ovmapDiv.className = 'ol-overviewmap-map';
+
+ /**
+ * @type {ol.Map}
+ * @private
+ */
+ this.ovmap_ = new ol.Map({
+ controls: new ol.Collection(),
+ interactions: new ol.Collection(),
+ target: ovmapDiv,
+ view: options.view
+ });
+ var ovmap = this.ovmap_;
+
+ if (options.layers) {
+ options.layers.forEach(
+ /**
+ * @param {ol.layer.Layer} layer Layer.
+ */
+ function(layer) {
+ ovmap.addLayer(layer);
+ }, this);
+ }
+
+ var box = document.createElement('DIV');
+ box.className = 'ol-overviewmap-box';
+ box.style.boxSizing = 'border-box';
+
+ /**
+ * @type {ol.Overlay}
+ * @private
+ */
+ this.boxOverlay_ = new ol.Overlay({
+ position: [0, 0],
+ positioning: ol.Overlay.Positioning.BOTTOM_LEFT,
+ element: box
+ });
+ this.ovmap_.addOverlay(this.boxOverlay_);
+
+ var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
+ ol.css.CLASS_CONTROL +
+ (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') +
+ (this.collapsible_ ? '' : ' ol-uncollapsible');
+ var element = document.createElement('div');
+ element.className = cssClasses;
+ element.appendChild(ovmapDiv);
+ element.appendChild(button);
+
+ var render = options.render ? options.render : ol.control.OverviewMap.render;
+
+ ol.control.Control.call(this, {
+ element: element,
+ render: render,
+ target: options.target
+ });
+};
+ol.inherits(ol.control.OverviewMap, ol.control.Control);
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.control.OverviewMap.prototype.setMap = function(map) {
+ var oldMap = this.getMap();
+ if (map === oldMap) {
+ return;
+ }
+ if (oldMap) {
+ var oldView = oldMap.getView();
+ if (oldView) {
+ this.unbindView_(oldView);
+ }
+ }
+ ol.control.Control.prototype.setMap.call(this, map);
+
+ if (map) {
+ this.listenerKeys.push(ol.events.listen(
+ map, ol.Object.EventType.PROPERTYCHANGE,
+ this.handleMapPropertyChange_, this));
+
+ // TODO: to really support map switching, this would need to be reworked
+ if (this.ovmap_.getLayers().getLength() === 0) {
+ this.ovmap_.setLayerGroup(map.getLayerGroup());
+ }
+
+ var view = map.getView();
+ if (view) {
+ this.bindView_(view);
+ if (view.isDef()) {
+ this.ovmap_.updateSize();
+ this.resetExtent_();
+ }
+ }
+ }
+};
+
+
+/**
+ * Handle map property changes. This only deals with changes to the map's view.
+ * @param {ol.Object.Event} event The propertychange event.
+ * @private
+ */
+ol.control.OverviewMap.prototype.handleMapPropertyChange_ = function(event) {
+ if (event.key === ol.Map.Property.VIEW) {
+ var oldView = /** @type {ol.View} */ (event.oldValue);
+ if (oldView) {
+ this.unbindView_(oldView);
+ }
+ var newView = this.getMap().getView();
+ this.bindView_(newView);
+ }
+};
+
+
+/**
+ * Register listeners for view property changes.
+ * @param {ol.View} view The view.
+ * @private
+ */
+ol.control.OverviewMap.prototype.bindView_ = function(view) {
+ ol.events.listen(view,
+ ol.Object.getChangeEventType(ol.View.Property.ROTATION),
+ this.handleRotationChanged_, this);
+};
+
+
+/**
+ * Unregister listeners for view property changes.
+ * @param {ol.View} view The view.
+ * @private
+ */
+ol.control.OverviewMap.prototype.unbindView_ = function(view) {
+ ol.events.unlisten(view,
+ ol.Object.getChangeEventType(ol.View.Property.ROTATION),
+ this.handleRotationChanged_, this);
+};
+
+
+/**
+ * Handle rotation changes to the main map.
+ * TODO: This should rotate the extent rectrangle instead of the
+ * overview map's view.
+ * @private
+ */
+ol.control.OverviewMap.prototype.handleRotationChanged_ = function() {
+ this.ovmap_.getView().setRotation(this.getMap().getView().getRotation());
+};
+
+
+/**
+ * Update the overview map element.
+ * @param {ol.MapEvent} mapEvent Map event.
+ * @this {ol.control.OverviewMap}
+ * @api
+ */
+ol.control.OverviewMap.render = function(mapEvent) {
+ this.validateExtent_();
+ this.updateBox_();
+};
+
+
+/**
+ * Reset the overview map extent if the box size (width or
+ * height) is less than the size of the overview map size times minRatio
+ * or is greater than the size of the overview size times maxRatio.
+ *
+ * If the map extent was not reset, the box size can fits in the defined
+ * ratio sizes. This method then checks if is contained inside the overview
+ * map current extent. If not, recenter the overview map to the current
+ * main map center location.
+ * @private
+ */
+ol.control.OverviewMap.prototype.validateExtent_ = function() {
+ var map = this.getMap();
+ var ovmap = this.ovmap_;
+
+ if (!map.isRendered() || !ovmap.isRendered()) {
+ return;
+ }
+
+ var mapSize = /** @type {ol.Size} */ (map.getSize());
+
+ var view = map.getView();
+ var extent = view.calculateExtent(mapSize);
+
+ var ovmapSize = /** @type {ol.Size} */ (ovmap.getSize());
+
+ var ovview = ovmap.getView();
+ var ovextent = ovview.calculateExtent(ovmapSize);
+
+ var topLeftPixel =
+ ovmap.getPixelFromCoordinate(ol.extent.getTopLeft(extent));
+ var bottomRightPixel =
+ ovmap.getPixelFromCoordinate(ol.extent.getBottomRight(extent));
+
+ var boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]);
+ var boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]);
+
+ var ovmapWidth = ovmapSize[0];
+ var ovmapHeight = ovmapSize[1];
+
+ if (boxWidth < ovmapWidth * ol.OVERVIEWMAP_MIN_RATIO ||
+ boxHeight < ovmapHeight * ol.OVERVIEWMAP_MIN_RATIO ||
+ boxWidth > ovmapWidth * ol.OVERVIEWMAP_MAX_RATIO ||
+ boxHeight > ovmapHeight * ol.OVERVIEWMAP_MAX_RATIO) {
+ this.resetExtent_();
+ } else if (!ol.extent.containsExtent(ovextent, extent)) {
+ this.recenter_();
+ }
+};
+
+
+/**
+ * Reset the overview map extent to half calculated min and max ratio times
+ * the extent of the main map.
+ * @private
+ */
+ol.control.OverviewMap.prototype.resetExtent_ = function() {
+ if (ol.OVERVIEWMAP_MAX_RATIO === 0 || ol.OVERVIEWMAP_MIN_RATIO === 0) {
+ return;
+ }
+
+ var map = this.getMap();
+ var ovmap = this.ovmap_;
+
+ var mapSize = /** @type {ol.Size} */ (map.getSize());
+
+ var view = map.getView();
+ var extent = view.calculateExtent(mapSize);
+
+ var ovmapSize = /** @type {ol.Size} */ (ovmap.getSize());
+
+ var ovview = ovmap.getView();
+
+ // get how many times the current map overview could hold different
+ // box sizes using the min and max ratio, pick the step in the middle used
+ // to calculate the extent from the main map to set it to the overview map,
+ var steps = Math.log(
+ ol.OVERVIEWMAP_MAX_RATIO / ol.OVERVIEWMAP_MIN_RATIO) / Math.LN2;
+ var ratio = 1 / (Math.pow(2, steps / 2) * ol.OVERVIEWMAP_MIN_RATIO);
+ ol.extent.scaleFromCenter(extent, ratio);
+ ovview.fit(extent, ovmapSize);
+};
+
+
+/**
+ * Set the center of the overview map to the map center without changing its
+ * resolution.
+ * @private
+ */
+ol.control.OverviewMap.prototype.recenter_ = function() {
+ var map = this.getMap();
+ var ovmap = this.ovmap_;
+
+ var view = map.getView();
+
+ var ovview = ovmap.getView();
+
+ ovview.setCenter(view.getCenter());
+};
+
+
+/**
+ * Update the box using the main map extent
+ * @private
+ */
+ol.control.OverviewMap.prototype.updateBox_ = function() {
+ var map = this.getMap();
+ var ovmap = this.ovmap_;
+
+ if (!map.isRendered() || !ovmap.isRendered()) {
+ return;
+ }
+
+ var mapSize = /** @type {ol.Size} */ (map.getSize());
+
+ var view = map.getView();
+
+ var ovview = ovmap.getView();
+
+ var rotation = view.getRotation();
+
+ var overlay = this.boxOverlay_;
+ var box = this.boxOverlay_.getElement();
+ var extent = view.calculateExtent(mapSize);
+ var ovresolution = ovview.getResolution();
+ var bottomLeft = ol.extent.getBottomLeft(extent);
+ var topRight = ol.extent.getTopRight(extent);
+
+ // set position using bottom left coordinates
+ var rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft);
+ overlay.setPosition(rotateBottomLeft);
+
+ // set box size calculated from map extent size and overview map resolution
+ if (box) {
+ box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px';
+ box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px';
+ }
+};
+
+
+/**
+ * @param {number} rotation Target rotation.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {ol.Coordinate|undefined} Coordinate for rotation and center anchor.
+ * @private
+ */
+ol.control.OverviewMap.prototype.calculateCoordinateRotate_ = function(
+ rotation, coordinate) {
+ var coordinateRotate;
+
+ var map = this.getMap();
+ var view = map.getView();
+
+ var currentCenter = view.getCenter();
+
+ if (currentCenter) {
+ coordinateRotate = [
+ coordinate[0] - currentCenter[0],
+ coordinate[1] - currentCenter[1]
+ ];
+ ol.coordinate.rotate(coordinateRotate, rotation);
+ ol.coordinate.add(coordinateRotate, currentCenter);
+ }
+ return coordinateRotate;
+};
+
+
+/**
+ * @param {Event} event The event to handle
+ * @private
+ */
+ol.control.OverviewMap.prototype.handleClick_ = function(event) {
+ event.preventDefault();
+ this.handleToggle_();
+};
+
+
+/**
+ * @private
+ */
+ol.control.OverviewMap.prototype.handleToggle_ = function() {
+ this.element.classList.toggle('ol-collapsed');
+ if (this.collapsed_) {
+ ol.dom.replaceNode(this.collapseLabel_, this.label_);
+ } else {
+ ol.dom.replaceNode(this.label_, this.collapseLabel_);
+ }
+ this.collapsed_ = !this.collapsed_;
+
+ // manage overview map if it had not been rendered before and control
+ // is expanded
+ var ovmap = this.ovmap_;
+ if (!this.collapsed_ && !ovmap.isRendered()) {
+ ovmap.updateSize();
+ this.resetExtent_();
+ ol.events.listenOnce(ovmap, ol.MapEvent.Type.POSTRENDER,
+ function(event) {
+ this.updateBox_();
+ },
+ this);
+ }
+};
+
+
+/**
+ * Return `true` if the overview map is collapsible, `false` otherwise.
+ * @return {boolean} True if the widget is collapsible.
+ * @api stable
+ */
+ol.control.OverviewMap.prototype.getCollapsible = function() {
+ return this.collapsible_;
+};
+
+
+/**
+ * Set whether the overview map should be collapsible.
+ * @param {boolean} collapsible True if the widget is collapsible.
+ * @api stable
+ */
+ol.control.OverviewMap.prototype.setCollapsible = function(collapsible) {
+ if (this.collapsible_ === collapsible) {
+ return;
+ }
+ this.collapsible_ = collapsible;
+ this.element.classList.toggle('ol-uncollapsible');
+ if (!collapsible && this.collapsed_) {
+ this.handleToggle_();
+ }
+};
+
+
+/**
+ * Collapse or expand the overview map according to the passed parameter. Will
+ * not do anything if the overview map isn't collapsible or if the current
+ * collapsed state is already the one requested.
+ * @param {boolean} collapsed True if the widget is collapsed.
+ * @api stable
+ */
+ol.control.OverviewMap.prototype.setCollapsed = function(collapsed) {
+ if (!this.collapsible_ || this.collapsed_ === collapsed) {
+ return;
+ }
+ this.handleToggle_();
+};
+
+
+/**
+ * Determine if the overview map is collapsed.
+ * @return {boolean} The overview map is collapsed.
+ * @api stable
+ */
+ol.control.OverviewMap.prototype.getCollapsed = function() {
+ return this.collapsed_;
+};
+
+
+/**
+ * Return the overview map.
+ * @return {ol.Map} Overview map.
+ * @api
+ */
+ol.control.OverviewMap.prototype.getOverviewMap = function() {
+ return this.ovmap_;
+};
+
+goog.provide('ol.control.ScaleLine');
+
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.asserts');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+goog.require('ol.events');
+goog.require('ol.proj');
+goog.require('ol.proj.Units');
+
+
+/**
+ * @classdesc
+ * A control displaying rough y-axis distances, calculated for the center of the
+ * viewport. For conformal projections (e.g. EPSG:3857, the default view
+ * projection in OpenLayers), the scale is valid for all directions.
+ * No scale line will be shown when the y-axis distance of a pixel at the
+ * viewport center cannot be calculated in the view projection.
+ * By default the scale line will show in the bottom left portion of the map,
+ * but this can be changed by using the css selector `.ol-scale-line`.
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.ScaleLineOptions=} opt_options Scale line options.
+ * @api stable
+ */
+ol.control.ScaleLine = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ var className = options.className !== undefined ? options.className : 'ol-scale-line';
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.innerElement_ = document.createElement('DIV');
+ this.innerElement_.className = className + '-inner';
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.element_ = document.createElement('DIV');
+ this.element_.className = className + ' ' + ol.css.CLASS_UNSELECTABLE;
+ this.element_.appendChild(this.innerElement_);
+
+ /**
+ * @private
+ * @type {?olx.ViewState}
+ */
+ this.viewState_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.renderedVisible_ = false;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.renderedWidth_ = undefined;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.renderedHTML_ = '';
+
+ var render = options.render ? options.render : ol.control.ScaleLine.render;
+
+ ol.control.Control.call(this, {
+ element: this.element_,
+ render: render,
+ target: options.target
+ });
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.control.ScaleLine.Property.UNITS),
+ this.handleUnitsChanged_, this);
+
+ this.setUnits(/** @type {ol.control.ScaleLine.Units} */ (options.units) ||
+ ol.control.ScaleLine.Units.METRIC);
+
+};
+ol.inherits(ol.control.ScaleLine, ol.control.Control);
+
+
+/**
+ * @const
+ * @type {Array.<number>}
+ */
+ol.control.ScaleLine.LEADING_DIGITS = [1, 2, 5];
+
+
+/**
+ * Return the units to use in the scale line.
+ * @return {ol.control.ScaleLine.Units|undefined} The units to use in the scale
+ * line.
+ * @observable
+ * @api stable
+ */
+ol.control.ScaleLine.prototype.getUnits = function() {
+ return /** @type {ol.control.ScaleLine.Units|undefined} */ (
+ this.get(ol.control.ScaleLine.Property.UNITS));
+};
+
+
+/**
+ * Update the scale line element.
+ * @param {ol.MapEvent} mapEvent Map event.
+ * @this {ol.control.ScaleLine}
+ * @api
+ */
+ol.control.ScaleLine.render = function(mapEvent) {
+ var frameState = mapEvent.frameState;
+ if (!frameState) {
+ this.viewState_ = null;
+ } else {
+ this.viewState_ = frameState.viewState;
+ }
+ this.updateElement_();
+};
+
+
+/**
+ * @private
+ */
+ol.control.ScaleLine.prototype.handleUnitsChanged_ = function() {
+ this.updateElement_();
+};
+
+
+/**
+ * Set the units to use in the scale line.
+ * @param {ol.control.ScaleLine.Units} units The units to use in the scale line.
+ * @observable
+ * @api stable
+ */
+ol.control.ScaleLine.prototype.setUnits = function(units) {
+ this.set(ol.control.ScaleLine.Property.UNITS, units);
+};
+
+
+/**
+ * @private
+ */
+ol.control.ScaleLine.prototype.updateElement_ = function() {
+ var viewState = this.viewState_;
+
+ if (!viewState) {
+ if (this.renderedVisible_) {
+ this.element_.style.display = 'none';
+ this.renderedVisible_ = false;
+ }
+ return;
+ }
+
+ var center = viewState.center;
+ var projection = viewState.projection;
+ var metersPerUnit = projection.getMetersPerUnit();
+ var pointResolution =
+ ol.proj.getPointResolution(projection, viewState.resolution, center) *
+ metersPerUnit;
+
+ var nominalCount = this.minWidth_ * pointResolution;
+ var suffix = '';
+ var units = this.getUnits();
+ if (units == ol.control.ScaleLine.Units.DEGREES) {
+ var metersPerDegree = ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES];
+ pointResolution /= metersPerDegree;
+ if (nominalCount < metersPerDegree / 60) {
+ suffix = '\u2033'; // seconds
+ pointResolution *= 3600;
+ } else if (nominalCount < metersPerDegree) {
+ suffix = '\u2032'; // minutes
+ pointResolution *= 60;
+ } else {
+ suffix = '\u00b0'; // degrees
+ }
+ } else if (units == ol.control.ScaleLine.Units.IMPERIAL) {
+ if (nominalCount < 0.9144) {
+ suffix = 'in';
+ pointResolution /= 0.0254;
+ } else if (nominalCount < 1609.344) {
+ suffix = 'ft';
+ pointResolution /= 0.3048;
+ } else {
+ suffix = 'mi';
+ pointResolution /= 1609.344;
+ }
+ } else if (units == ol.control.ScaleLine.Units.NAUTICAL) {
+ pointResolution /= 1852;
+ suffix = 'nm';
+ } else if (units == ol.control.ScaleLine.Units.METRIC) {
+ if (nominalCount < 1) {
+ suffix = 'mm';
+ pointResolution *= 1000;
+ } else if (nominalCount < 1000) {
+ suffix = 'm';
+ } else {
+ suffix = 'km';
+ pointResolution /= 1000;
+ }
+ } else if (units == ol.control.ScaleLine.Units.US) {
+ if (nominalCount < 0.9144) {
+ suffix = 'in';
+ pointResolution *= 39.37;
+ } else if (nominalCount < 1609.344) {
+ suffix = 'ft';
+ pointResolution /= 0.30480061;
+ } else {
+ suffix = 'mi';
+ pointResolution /= 1609.3472;
+ }
+ } else {
+ ol.asserts.assert(false, 33); // Invalid units
+ }
+
+ var i = 3 * Math.floor(
+ Math.log(this.minWidth_ * pointResolution) / Math.log(10));
+ var count, width;
+ while (true) {
+ count = ol.control.ScaleLine.LEADING_DIGITS[((i % 3) + 3) % 3] *
+ Math.pow(10, Math.floor(i / 3));
+ width = Math.round(count / pointResolution);
+ if (isNaN(width)) {
+ this.element_.style.display = 'none';
+ this.renderedVisible_ = false;
+ return;
+ } else if (width >= this.minWidth_) {
+ break;
+ }
+ ++i;
+ }
+
+ var html = count + ' ' + suffix;
+ if (this.renderedHTML_ != html) {
+ this.innerElement_.innerHTML = html;
+ this.renderedHTML_ = html;
+ }
+
+ if (this.renderedWidth_ != width) {
+ this.innerElement_.style.width = width + 'px';
+ this.renderedWidth_ = width;
+ }
+
+ if (!this.renderedVisible_) {
+ this.element_.style.display = '';
+ this.renderedVisible_ = true;
+ }
+
+};
+
+
+/**
+ * @enum {string}
+ * @api
+ */
+ol.control.ScaleLine.Property = {
+ UNITS: 'units'
+};
+
+
+/**
+ * Units for the scale line. Supported values are `'degrees'`, `'imperial'`,
+ * `'nautical'`, `'metric'`, `'us'`.
+ * @enum {string}
+ */
+ol.control.ScaleLine.Units = {
+ DEGREES: 'degrees',
+ IMPERIAL: 'imperial',
+ NAUTICAL: 'nautical',
+ METRIC: 'metric',
+ US: 'us'
+};
+
+// FIXME should possibly show tooltip when dragging?
+
+goog.provide('ol.control.ZoomSlider');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+goog.require('ol.easing');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.EventType');
+goog.require('ol.math');
+goog.require('ol.pointer.EventType');
+goog.require('ol.pointer.PointerEventHandler');
+
+
+/**
+ * @classdesc
+ * A slider type of control for zooming.
+ *
+ * Example:
+ *
+ * map.addControl(new ol.control.ZoomSlider());
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.ZoomSliderOptions=} opt_options Zoom slider options.
+ * @api stable
+ */
+ol.control.ZoomSlider = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * Will hold the current resolution of the view.
+ *
+ * @type {number|undefined}
+ * @private
+ */
+ this.currentResolution_ = undefined;
+
+ /**
+ * The direction of the slider. Will be determined from actual display of the
+ * container and defaults to ol.control.ZoomSlider.direction.VERTICAL.
+ *
+ * @type {ol.control.ZoomSlider.direction}
+ * @private
+ */
+ this.direction_ = ol.control.ZoomSlider.direction.VERTICAL;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.dragging_;
+
+ /**
+ * @type {!Array.<ol.EventsKey>}
+ * @private
+ */
+ this.dragListenerKeys_ = [];
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.heightLimit_ = 0;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.widthLimit_ = 0;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.previousX_;
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.previousY_;
+
+ /**
+ * The calculated thumb size (border box plus margins). Set when initSlider_
+ * is called.
+ * @type {ol.Size}
+ * @private
+ */
+ this.thumbSize_ = null;
+
+ /**
+ * Whether the slider is initialized.
+ * @type {boolean}
+ * @private
+ */
+ this.sliderInitialized_ = false;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 200;
+
+ var className = options.className !== undefined ? options.className : 'ol-zoomslider';
+ var thumbElement = document.createElement('button');
+ thumbElement.setAttribute('type', 'button');
+ thumbElement.className = className + '-thumb ' + ol.css.CLASS_UNSELECTABLE;
+ var containerElement = document.createElement('div');
+ containerElement.className = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + ol.css.CLASS_CONTROL;
+ containerElement.appendChild(thumbElement);
+ /**
+ * @type {ol.pointer.PointerEventHandler}
+ * @private
+ */
+ this.dragger_ = new ol.pointer.PointerEventHandler(containerElement);
+
+ ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERDOWN,
+ this.handleDraggerStart_, this);
+ ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERMOVE,
+ this.handleDraggerDrag_, this);
+ ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERUP,
+ this.handleDraggerEnd_, this);
+
+ ol.events.listen(containerElement, ol.events.EventType.CLICK,
+ this.handleContainerClick_, this);
+ ol.events.listen(thumbElement, ol.events.EventType.CLICK,
+ ol.events.Event.stopPropagation);
+
+ var render = options.render ? options.render : ol.control.ZoomSlider.render;
+
+ ol.control.Control.call(this, {
+ element: containerElement,
+ render: render
+ });
+};
+ol.inherits(ol.control.ZoomSlider, ol.control.Control);
+
+
+/**
+ * @inheritDoc
+ */
+ol.control.ZoomSlider.prototype.disposeInternal = function() {
+ this.dragger_.dispose();
+ ol.control.Control.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * The enum for available directions.
+ *
+ * @enum {number}
+ */
+ol.control.ZoomSlider.direction = {
+ VERTICAL: 0,
+ HORIZONTAL: 1
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.control.ZoomSlider.prototype.setMap = function(map) {
+ ol.control.Control.prototype.setMap.call(this, map);
+ if (map) {
+ map.render();
+ }
+};
+
+
+/**
+ * Initializes the slider element. This will determine and set this controls
+ * direction_ and also constrain the dragging of the thumb to always be within
+ * the bounds of the container.
+ *
+ * @private
+ */
+ol.control.ZoomSlider.prototype.initSlider_ = function() {
+ var container = this.element;
+ var containerSize = {
+ width: container.offsetWidth, height: container.offsetHeight
+ };
+
+ var thumb = container.firstElementChild;
+ var computedStyle = getComputedStyle(thumb);
+ var thumbWidth = thumb.offsetWidth +
+ parseFloat(computedStyle['marginRight']) +
+ parseFloat(computedStyle['marginLeft']);
+ var thumbHeight = thumb.offsetHeight +
+ parseFloat(computedStyle['marginTop']) +
+ parseFloat(computedStyle['marginBottom']);
+ this.thumbSize_ = [thumbWidth, thumbHeight];
+
+ if (containerSize.width > containerSize.height) {
+ this.direction_ = ol.control.ZoomSlider.direction.HORIZONTAL;
+ this.widthLimit_ = containerSize.width - thumbWidth;
+ } else {
+ this.direction_ = ol.control.ZoomSlider.direction.VERTICAL;
+ this.heightLimit_ = containerSize.height - thumbHeight;
+ }
+ this.sliderInitialized_ = true;
+};
+
+
+/**
+ * Update the zoomslider element.
+ * @param {ol.MapEvent} mapEvent Map event.
+ * @this {ol.control.ZoomSlider}
+ * @api
+ */
+ol.control.ZoomSlider.render = function(mapEvent) {
+ if (!mapEvent.frameState) {
+ return;
+ }
+ if (!this.sliderInitialized_) {
+ this.initSlider_();
+ }
+ var res = mapEvent.frameState.viewState.resolution;
+ if (res !== this.currentResolution_) {
+ this.currentResolution_ = res;
+ this.setThumbPosition_(res);
+ }
+};
+
+
+/**
+ * @param {Event} event The browser event to handle.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.handleContainerClick_ = function(event) {
+ var view = this.getMap().getView();
+
+ var relativePosition = this.getRelativePosition_(
+ event.offsetX - this.thumbSize_[0] / 2,
+ event.offsetY - this.thumbSize_[1] / 2);
+
+ var resolution = this.getResolutionForPosition_(relativePosition);
+
+ view.animate({
+ resolution: view.constrainResolution(resolution),
+ duration: this.duration_,
+ easing: ol.easing.easeOut
+ });
+};
+
+
+/**
+ * Handle dragger start events.
+ * @param {ol.pointer.PointerEvent} event The drag event.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.handleDraggerStart_ = function(event) {
+ if (!this.dragging_ &&
+ event.originalEvent.target === this.element.firstElementChild) {
+ this.getMap().getView().setHint(ol.View.Hint.INTERACTING, 1);
+ this.previousX_ = event.clientX;
+ this.previousY_ = event.clientY;
+ this.dragging_ = true;
+
+ if (this.dragListenerKeys_.length === 0) {
+ var drag = this.handleDraggerDrag_;
+ var end = this.handleDraggerEnd_;
+ this.dragListenerKeys_.push(
+ ol.events.listen(document, ol.events.EventType.MOUSEMOVE, drag, this),
+ ol.events.listen(document, ol.events.EventType.TOUCHMOVE, drag, this),
+ ol.events.listen(document, ol.pointer.EventType.POINTERMOVE, drag, this),
+ ol.events.listen(document, ol.events.EventType.MOUSEUP, end, this),
+ ol.events.listen(document, ol.events.EventType.TOUCHEND, end, this),
+ ol.events.listen(document, ol.pointer.EventType.POINTERUP, end, this)
+ );
+ }
+ }
+};
+
+
+/**
+ * Handle dragger drag events.
+ *
+ * @param {ol.pointer.PointerEvent|Event} event The drag event.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.handleDraggerDrag_ = function(event) {
+ if (this.dragging_) {
+ var element = this.element.firstElementChild;
+ var deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10);
+ var deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10);
+ var relativePosition = this.getRelativePosition_(deltaX, deltaY);
+ this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
+ this.getMap().getView().setResolution(this.currentResolution_);
+ this.setThumbPosition_(this.currentResolution_);
+ this.previousX_ = event.clientX;
+ this.previousY_ = event.clientY;
+ }
+};
+
+
+/**
+ * Handle dragger end events.
+ * @param {ol.pointer.PointerEvent|Event} event The drag event.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.handleDraggerEnd_ = function(event) {
+ if (this.dragging_) {
+ var view = this.getMap().getView();
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+
+ view.animate({
+ resolution: view.constrainResolution(this.currentResolution_),
+ duration: this.duration_,
+ easing: ol.easing.easeOut
+ });
+
+ this.dragging_ = false;
+ this.previousX_ = undefined;
+ this.previousY_ = undefined;
+ this.dragListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.dragListenerKeys_.length = 0;
+ }
+};
+
+
+/**
+ * Positions the thumb inside its container according to the given resolution.
+ *
+ * @param {number} res The res.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.setThumbPosition_ = function(res) {
+ var position = this.getPositionForResolution_(res);
+ var thumb = this.element.firstElementChild;
+
+ if (this.direction_ == ol.control.ZoomSlider.direction.HORIZONTAL) {
+ thumb.style.left = this.widthLimit_ * position + 'px';
+ } else {
+ thumb.style.top = this.heightLimit_ * position + 'px';
+ }
+};
+
+
+/**
+ * Calculates the relative position of the thumb given x and y offsets. The
+ * relative position scales from 0 to 1. The x and y offsets are assumed to be
+ * in pixel units within the dragger limits.
+ *
+ * @param {number} x Pixel position relative to the left of the slider.
+ * @param {number} y Pixel position relative to the top of the slider.
+ * @return {number} The relative position of the thumb.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.getRelativePosition_ = function(x, y) {
+ var amount;
+ if (this.direction_ === ol.control.ZoomSlider.direction.HORIZONTAL) {
+ amount = x / this.widthLimit_;
+ } else {
+ amount = y / this.heightLimit_;
+ }
+ return ol.math.clamp(amount, 0, 1);
+};
+
+
+/**
+ * Calculates the corresponding resolution of the thumb given its relative
+ * position (where 0 is the minimum and 1 is the maximum).
+ *
+ * @param {number} position The relative position of the thumb.
+ * @return {number} The corresponding resolution.
+ * @private
+ */
+ol.control.ZoomSlider.prototype.getResolutionForPosition_ = function(position) {
+ var fn = this.getMap().getView().getResolutionForValueFunction();
+ return fn(1 - position);
+};
+
+
+/**
+ * Determines the relative position of the slider for the given resolution. A
+ * relative position of 0 corresponds to the minimum view resolution. A
+ * relative position of 1 corresponds to the maximum view resolution.
+ *
+ * @param {number} res The resolution.
+ * @return {number} The relative position value (between 0 and 1).
+ * @private
+ */
+ol.control.ZoomSlider.prototype.getPositionForResolution_ = function(res) {
+ var fn = this.getMap().getView().getValueForResolutionFunction();
+ return 1 - fn(res);
+};
+
+goog.provide('ol.control.ZoomToExtent');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.control.Control');
+goog.require('ol.css');
+
+
+/**
+ * @classdesc
+ * A button control which, when pressed, changes the map view to a specific
+ * extent. To style this control use the css selector `.ol-zoom-extent`.
+ *
+ * @constructor
+ * @extends {ol.control.Control}
+ * @param {olx.control.ZoomToExtentOptions=} opt_options Options.
+ * @api stable
+ */
+ol.control.ZoomToExtent = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @type {ol.Extent}
+ * @private
+ */
+ this.extent_ = options.extent ? options.extent : null;
+
+ var className = options.className !== undefined ? options.className :
+ 'ol-zoom-extent';
+
+ var label = options.label !== undefined ? options.label : 'E';
+ var tipLabel = options.tipLabel !== undefined ?
+ options.tipLabel : 'Fit to extent';
+ var button = document.createElement('button');
+ button.setAttribute('type', 'button');
+ button.title = tipLabel;
+ button.appendChild(
+ typeof label === 'string' ? document.createTextNode(label) : label
+ );
+
+ ol.events.listen(button, ol.events.EventType.CLICK,
+ this.handleClick_, this);
+
+ var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
+ ol.css.CLASS_CONTROL;
+ var element = document.createElement('div');
+ element.className = cssClasses;
+ element.appendChild(button);
+
+ ol.control.Control.call(this, {
+ element: element,
+ target: options.target
+ });
+};
+ol.inherits(ol.control.ZoomToExtent, ol.control.Control);
+
+
+/**
+ * @param {Event} event The event to handle
+ * @private
+ */
+ol.control.ZoomToExtent.prototype.handleClick_ = function(event) {
+ event.preventDefault();
+ this.handleZoomToExtent_();
+};
+
+
+/**
+ * @private
+ */
+ol.control.ZoomToExtent.prototype.handleZoomToExtent_ = function() {
+ var map = this.getMap();
+ var view = map.getView();
+ var extent = !this.extent_ ? view.getProjection().getExtent() : this.extent_;
+ var size = /** @type {ol.Size} */ (map.getSize());
+ view.fit(extent, size);
+};
+
+goog.provide('ol.DeviceOrientation');
+
+goog.require('ol.events');
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.has');
+goog.require('ol.math');
+
+
+/**
+ * @classdesc
+ * The ol.DeviceOrientation class provides access to information from
+ * DeviceOrientation events. See the [HTML 5 DeviceOrientation Specification](
+ * http://www.w3.org/TR/orientation-event/) for more details.
+ *
+ * Many new computers, and especially mobile phones
+ * and tablets, provide hardware support for device orientation. Web
+ * developers targeting mobile devices will be especially interested in this
+ * class.
+ *
+ * Device orientation data are relative to a common starting point. For mobile
+ * devices, the starting point is to lay your phone face up on a table with the
+ * top of the phone pointing north. This represents the zero state. All
+ * angles are then relative to this state. For computers, it is the same except
+ * the screen is open at 90 degrees.
+ *
+ * Device orientation is reported as three angles - `alpha`, `beta`, and
+ * `gamma` - relative to the starting position along the three planar axes X, Y
+ * and Z. The X axis runs from the left edge to the right edge through the
+ * middle of the device. Similarly, the Y axis runs from the bottom to the top
+ * of the device through the middle. The Z axis runs from the back to the front
+ * through the middle. In the starting position, the X axis points to the
+ * right, the Y axis points away from you and the Z axis points straight up
+ * from the device lying flat.
+ *
+ * The three angles representing the device orientation are relative to the
+ * three axes. `alpha` indicates how much the device has been rotated around the
+ * Z axis, which is commonly interpreted as the compass heading (see note
+ * below). `beta` indicates how much the device has been rotated around the X
+ * axis, or how much it is tilted from front to back. `gamma` indicates how
+ * much the device has been rotated around the Y axis, or how much it is tilted
+ * from left to right.
+ *
+ * For most browsers, the `alpha` value returns the compass heading so if the
+ * device points north, it will be 0. With Safari on iOS, the 0 value of
+ * `alpha` is calculated from when device orientation was first requested.
+ * ol.DeviceOrientation provides the `heading` property which normalizes this
+ * behavior across all browsers for you.
+ *
+ * It is important to note that the HTML 5 DeviceOrientation specification
+ * indicates that `alpha`, `beta` and `gamma` are in degrees while the
+ * equivalent properties in ol.DeviceOrientation are in radians for consistency
+ * with all other uses of angles throughout OpenLayers.
+ *
+ * To get notified of device orientation changes, register a listener for the
+ * generic `change` event on your `ol.DeviceOrientation` instance.
+ *
+ * @see {@link http://www.w3.org/TR/orientation-event/}
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {olx.DeviceOrientationOptions=} opt_options Options.
+ * @api
+ */
+ol.DeviceOrientation = function(opt_options) {
+
+ ol.Object.call(this);
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.listenerKey_ = null;
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.DeviceOrientation.Property.TRACKING),
+ this.handleTrackingChanged_, this);
+
+ this.setTracking(options.tracking !== undefined ? options.tracking : false);
+
+};
+ol.inherits(ol.DeviceOrientation, ol.Object);
+
+
+/**
+ * @inheritDoc
+ */
+ol.DeviceOrientation.prototype.disposeInternal = function() {
+ this.setTracking(false);
+ ol.Object.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * @private
+ * @param {Event} originalEvent Event.
+ */
+ol.DeviceOrientation.prototype.orientationChange_ = function(originalEvent) {
+ var event = /** @type {DeviceOrientationEvent} */ (originalEvent);
+ if (event.alpha !== null) {
+ var alpha = ol.math.toRadians(event.alpha);
+ this.set(ol.DeviceOrientation.Property.ALPHA, alpha);
+ // event.absolute is undefined in iOS.
+ if (typeof event.absolute === 'boolean' && event.absolute) {
+ this.set(ol.DeviceOrientation.Property.HEADING, alpha);
+ } else if (typeof event.webkitCompassHeading === 'number' &&
+ event.webkitCompassAccuracy != -1) {
+ var heading = ol.math.toRadians(event.webkitCompassHeading);
+ this.set(ol.DeviceOrientation.Property.HEADING, heading);
+ }
+ }
+ if (event.beta !== null) {
+ this.set(ol.DeviceOrientation.Property.BETA,
+ ol.math.toRadians(event.beta));
+ }
+ if (event.gamma !== null) {
+ this.set(ol.DeviceOrientation.Property.GAMMA,
+ ol.math.toRadians(event.gamma));
+ }
+ this.changed();
+};
+
+
+/**
+ * Rotation around the device z-axis (in radians).
+ * @return {number|undefined} The euler angle in radians of the device from the
+ * standard Z axis.
+ * @observable
+ * @api
+ */
+ol.DeviceOrientation.prototype.getAlpha = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.DeviceOrientation.Property.ALPHA));
+};
+
+
+/**
+ * Rotation around the device x-axis (in radians).
+ * @return {number|undefined} The euler angle in radians of the device from the
+ * planar X axis.
+ * @observable
+ * @api
+ */
+ol.DeviceOrientation.prototype.getBeta = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.DeviceOrientation.Property.BETA));
+};
+
+
+/**
+ * Rotation around the device y-axis (in radians).
+ * @return {number|undefined} The euler angle in radians of the device from the
+ * planar Y axis.
+ * @observable
+ * @api
+ */
+ol.DeviceOrientation.prototype.getGamma = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.DeviceOrientation.Property.GAMMA));
+};
+
+
+/**
+ * The heading of the device relative to north (in radians).
+ * @return {number|undefined} The heading of the device relative to north, in
+ * radians, normalizing for different browser behavior.
+ * @observable
+ * @api
+ */
+ol.DeviceOrientation.prototype.getHeading = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.DeviceOrientation.Property.HEADING));
+};
+
+
+/**
+ * Determine if orientation is being tracked.
+ * @return {boolean} Changes in device orientation are being tracked.
+ * @observable
+ * @api
+ */
+ol.DeviceOrientation.prototype.getTracking = function() {
+ return /** @type {boolean} */ (
+ this.get(ol.DeviceOrientation.Property.TRACKING));
+};
+
+
+/**
+ * @private
+ */
+ol.DeviceOrientation.prototype.handleTrackingChanged_ = function() {
+ if (ol.has.DEVICE_ORIENTATION) {
+ var tracking = this.getTracking();
+ if (tracking && !this.listenerKey_) {
+ this.listenerKey_ = ol.events.listen(window, 'deviceorientation',
+ this.orientationChange_, this);
+ } else if (!tracking && this.listenerKey_ !== null) {
+ ol.events.unlistenByKey(this.listenerKey_);
+ this.listenerKey_ = null;
+ }
+ }
+};
+
+
+/**
+ * Enable or disable tracking of device orientation events.
+ * @param {boolean} tracking The status of tracking changes to alpha, beta and
+ * gamma. If true, changes are tracked and reported immediately.
+ * @observable
+ * @api
+ */
+ol.DeviceOrientation.prototype.setTracking = function(tracking) {
+ this.set(ol.DeviceOrientation.Property.TRACKING, tracking);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.DeviceOrientation.Property = {
+ ALPHA: 'alpha',
+ BETA: 'beta',
+ GAMMA: 'gamma',
+ HEADING: 'heading',
+ TRACKING: 'tracking'
+};
+
+goog.provide('ol.Feature');
+
+goog.require('ol.asserts');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.geom.Geometry');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * A vector object for geographic features with a geometry and other
+ * attribute properties, similar to the features in vector file formats like
+ * GeoJSON.
+ *
+ * Features can be styled individually with `setStyle`; otherwise they use the
+ * style of their vector layer.
+ *
+ * Note that attribute properties are set as {@link ol.Object} properties on
+ * the feature object, so they are observable, and have get/set accessors.
+ *
+ * Typically, a feature has a single geometry property. You can set the
+ * geometry using the `setGeometry` method and get it with `getGeometry`.
+ * It is possible to store more than one geometry on a feature using attribute
+ * properties. By default, the geometry used for rendering is identified by
+ * the property name `geometry`. If you want to use another geometry property
+ * for rendering, use the `setGeometryName` method to change the attribute
+ * property associated with the geometry for the feature. For example:
+ *
+ * ```js
+ * var feature = new ol.Feature({
+ * geometry: new ol.geom.Polygon(polyCoords),
+ * labelPoint: new ol.geom.Point(labelCoords),
+ * name: 'My Polygon'
+ * });
+ *
+ * // get the polygon geometry
+ * var poly = feature.getGeometry();
+ *
+ * // Render the feature as a point using the coordinates from labelPoint
+ * feature.setGeometryName('labelPoint');
+ *
+ * // get the point geometry
+ * var point = feature.getGeometry();
+ * ```
+ *
+ * @constructor
+ * @extends {ol.Object}
+ * @param {ol.geom.Geometry|Object.<string, *>=} opt_geometryOrProperties
+ * You may pass a Geometry object directly, or an object literal
+ * containing properties. If you pass an object literal, you may
+ * include a Geometry associated with a `geometry` key.
+ * @api stable
+ */
+ol.Feature = function(opt_geometryOrProperties) {
+
+ ol.Object.call(this);
+
+ /**
+ * @private
+ * @type {number|string|undefined}
+ */
+ this.id_ = undefined;
+
+ /**
+ * @type {string}
+ * @private
+ */
+ this.geometryName_ = 'geometry';
+
+ /**
+ * User provided style.
+ * @private
+ * @type {ol.style.Style|Array.<ol.style.Style>|
+ * ol.FeatureStyleFunction}
+ */
+ this.style_ = null;
+
+ /**
+ * @private
+ * @type {ol.FeatureStyleFunction|undefined}
+ */
+ this.styleFunction_ = undefined;
+
+ /**
+ * @private
+ * @type {?ol.EventsKey}
+ */
+ this.geometryChangeKey_ = null;
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(this.geometryName_),
+ this.handleGeometryChanged_, this);
+
+ if (opt_geometryOrProperties !== undefined) {
+ if (opt_geometryOrProperties instanceof ol.geom.Geometry ||
+ !opt_geometryOrProperties) {
+ var geometry = opt_geometryOrProperties;
+ this.setGeometry(geometry);
+ } else {
+ /** @type {Object.<string, *>} */
+ var properties = opt_geometryOrProperties;
+ this.setProperties(properties);
+ }
+ }
+};
+ol.inherits(ol.Feature, ol.Object);
+
+
+/**
+ * Clone this feature. If the original feature has a geometry it
+ * is also cloned. The feature id is not set in the clone.
+ * @return {ol.Feature} The clone.
+ * @api stable
+ */
+ol.Feature.prototype.clone = function() {
+ var clone = new ol.Feature(this.getProperties());
+ clone.setGeometryName(this.getGeometryName());
+ var geometry = this.getGeometry();
+ if (geometry) {
+ clone.setGeometry(geometry.clone());
+ }
+ var style = this.getStyle();
+ if (style) {
+ clone.setStyle(style);
+ }
+ return clone;
+};
+
+
+/**
+ * Get the feature's default geometry. A feature may have any number of named
+ * geometries. The "default" geometry (the one that is rendered by default) is
+ * set when calling {@link ol.Feature#setGeometry}.
+ * @return {ol.geom.Geometry|undefined} The default geometry for the feature.
+ * @api stable
+ * @observable
+ */
+ol.Feature.prototype.getGeometry = function() {
+ return /** @type {ol.geom.Geometry|undefined} */ (
+ this.get(this.geometryName_));
+};
+
+
+/**
+ * Get the feature identifier. This is a stable identifier for the feature and
+ * is either set when reading data from a remote source or set explicitly by
+ * calling {@link ol.Feature#setId}.
+ * @return {number|string|undefined} Id.
+ * @api stable
+ * @observable
+ */
+ol.Feature.prototype.getId = function() {
+ return this.id_;
+};
+
+
+/**
+ * Get the name of the feature's default geometry. By default, the default
+ * geometry is named `geometry`.
+ * @return {string} Get the property name associated with the default geometry
+ * for this feature.
+ * @api stable
+ */
+ol.Feature.prototype.getGeometryName = function() {
+ return this.geometryName_;
+};
+
+
+/**
+ * Get the feature's style. Will return what was provided to the
+ * {@link ol.Feature#setStyle} method.
+ * @return {ol.style.Style|Array.<ol.style.Style>|
+ * ol.FeatureStyleFunction} The feature style.
+ * @api stable
+ * @observable
+ */
+ol.Feature.prototype.getStyle = function() {
+ return this.style_;
+};
+
+
+/**
+ * Get the feature's style function.
+ * @return {ol.FeatureStyleFunction|undefined} Return a function
+ * representing the current style of this feature.
+ * @api stable
+ */
+ol.Feature.prototype.getStyleFunction = function() {
+ return this.styleFunction_;
+};
+
+
+/**
+ * @private
+ */
+ol.Feature.prototype.handleGeometryChange_ = function() {
+ this.changed();
+};
+
+
+/**
+ * @private
+ */
+ol.Feature.prototype.handleGeometryChanged_ = function() {
+ if (this.geometryChangeKey_) {
+ ol.events.unlistenByKey(this.geometryChangeKey_);
+ this.geometryChangeKey_ = null;
+ }
+ var geometry = this.getGeometry();
+ if (geometry) {
+ this.geometryChangeKey_ = ol.events.listen(geometry,
+ ol.events.EventType.CHANGE, this.handleGeometryChange_, this);
+ }
+ this.changed();
+};
+
+
+/**
+ * Set the default geometry for the feature. This will update the property
+ * with the name returned by {@link ol.Feature#getGeometryName}.
+ * @param {ol.geom.Geometry|undefined} geometry The new geometry.
+ * @api stable
+ * @observable
+ */
+ol.Feature.prototype.setGeometry = function(geometry) {
+ this.set(this.geometryName_, geometry);
+};
+
+
+/**
+ * Set the style for the feature. This can be a single style object, an array
+ * of styles, or a function that takes a resolution and returns an array of
+ * styles. If it is `null` the feature has no style (a `null` style).
+ * @param {ol.style.Style|Array.<ol.style.Style>|
+ * ol.FeatureStyleFunction} style Style for this feature.
+ * @api stable
+ * @observable
+ */
+ol.Feature.prototype.setStyle = function(style) {
+ this.style_ = style;
+ this.styleFunction_ = !style ?
+ undefined : ol.Feature.createStyleFunction(style);
+ this.changed();
+};
+
+
+/**
+ * Set the feature id. The feature id is considered stable and may be used when
+ * requesting features or comparing identifiers returned from a remote source.
+ * The feature id can be used with the {@link ol.source.Vector#getFeatureById}
+ * method.
+ * @param {number|string|undefined} id The feature id.
+ * @api stable
+ * @observable
+ */
+ol.Feature.prototype.setId = function(id) {
+ this.id_ = id;
+ this.changed();
+};
+
+
+/**
+ * Set the property name to be used when getting the feature's default geometry.
+ * When calling {@link ol.Feature#getGeometry}, the value of the property with
+ * this name will be returned.
+ * @param {string} name The property name of the default geometry.
+ * @api stable
+ */
+ol.Feature.prototype.setGeometryName = function(name) {
+ ol.events.unlisten(
+ this, ol.Object.getChangeEventType(this.geometryName_),
+ this.handleGeometryChanged_, this);
+ this.geometryName_ = name;
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(this.geometryName_),
+ this.handleGeometryChanged_, this);
+ this.handleGeometryChanged_();
+};
+
+
+/**
+ * Convert the provided object into a feature style function. Functions passed
+ * through unchanged. Arrays of ol.style.Style or single style objects wrapped
+ * in a new feature style function.
+ * @param {ol.FeatureStyleFunction|!Array.<ol.style.Style>|!ol.style.Style} obj
+ * A feature style function, a single style, or an array of styles.
+ * @return {ol.FeatureStyleFunction} A style function.
+ */
+ol.Feature.createStyleFunction = function(obj) {
+ var styleFunction;
+
+ if (typeof obj === 'function') {
+ styleFunction = obj;
+ } else {
+ /**
+ * @type {Array.<ol.style.Style>}
+ */
+ var styles;
+ if (Array.isArray(obj)) {
+ styles = obj;
+ } else {
+ ol.asserts.assert(obj instanceof ol.style.Style,
+ 41); // Expected an `ol.style.Style` or an array of `ol.style.Style`
+ styles = [obj];
+ }
+ styleFunction = function() {
+ return styles;
+ };
+ }
+ return styleFunction;
+};
+
+goog.provide('ol.format.FormatType');
+
+
+/**
+ * @enum {string}
+ */
+ol.format.FormatType = {
+ ARRAY_BUFFER: 'arraybuffer',
+ JSON: 'json',
+ TEXT: 'text',
+ XML: 'xml'
+};
+
+goog.provide('ol.xml');
+
+goog.require('ol');
+goog.require('ol.array');
+
+
+/**
+ * This document should be used when creating nodes for XML serializations. This
+ * document is also used by {@link ol.xml.createElementNS} and
+ * {@link ol.xml.setAttributeNS}
+ * @const
+ * @type {Document}
+ */
+ol.xml.DOCUMENT = document.implementation.createDocument('', '', null);
+
+
+/**
+ * @param {string} namespaceURI Namespace URI.
+ * @param {string} qualifiedName Qualified name.
+ * @return {Node} Node.
+ */
+ol.xml.createElementNS = function(namespaceURI, qualifiedName) {
+ return ol.xml.DOCUMENT.createElementNS(namespaceURI, qualifiedName);
+};
+
+
+/**
+ * Recursively grab all text content of child nodes into a single string.
+ * @param {Node} node Node.
+ * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line
+ * breaks.
+ * @return {string} All text content.
+ * @api
+ */
+ol.xml.getAllTextContent = function(node, normalizeWhitespace) {
+ return ol.xml.getAllTextContent_(node, normalizeWhitespace, []).join('');
+};
+
+
+/**
+ * Recursively grab all text content of child nodes into a single string.
+ * @param {Node} node Node.
+ * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line
+ * breaks.
+ * @param {Array.<string>} accumulator Accumulator.
+ * @private
+ * @return {Array.<string>} Accumulator.
+ */
+ol.xml.getAllTextContent_ = function(node, normalizeWhitespace, accumulator) {
+ if (node.nodeType == Node.CDATA_SECTION_NODE ||
+ node.nodeType == Node.TEXT_NODE) {
+ if (normalizeWhitespace) {
+ accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
+ } else {
+ accumulator.push(node.nodeValue);
+ }
+ } else {
+ var n;
+ for (n = node.firstChild; n; n = n.nextSibling) {
+ ol.xml.getAllTextContent_(n, normalizeWhitespace, accumulator);
+ }
+ }
+ return accumulator;
+};
+
+
+/**
+ * @param {?} value Value.
+ * @return {boolean} Is document.
+ */
+ol.xml.isDocument = function(value) {
+ return value instanceof Document;
+};
+
+
+/**
+ * @param {?} value Value.
+ * @return {boolean} Is node.
+ */
+ol.xml.isNode = function(value) {
+ return value instanceof Node;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {?string} namespaceURI Namespace URI.
+ * @param {string} name Attribute name.
+ * @return {string} Value
+ */
+ol.xml.getAttributeNS = function(node, namespaceURI, name) {
+ return node.getAttributeNS(namespaceURI, name) || '';
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {?string} namespaceURI Namespace URI.
+ * @param {string} name Attribute name.
+ * @param {string|number} value Value.
+ */
+ol.xml.setAttributeNS = function(node, namespaceURI, name, value) {
+ node.setAttributeNS(namespaceURI, name, value);
+};
+
+
+/**
+ * Parse an XML string to an XML Document.
+ * @param {string} xml XML.
+ * @return {Document} Document.
+ * @api
+ */
+ol.xml.parse = function(xml) {
+ return new DOMParser().parseFromString(xml, 'application/xml');
+};
+
+
+/**
+ * Make an array extender function for extending the array at the top of the
+ * object stack.
+ * @param {function(this: T, Node, Array.<*>): (Array.<*>|undefined)}
+ * valueReader Value reader.
+ * @param {T=} opt_this The object to use as `this` in `valueReader`.
+ * @return {ol.XmlParser} Parser.
+ * @template T
+ */
+ol.xml.makeArrayExtender = function(valueReader, opt_this) {
+ return (
+ /**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ */
+ function(node, objectStack) {
+ var value = valueReader.call(opt_this, node, objectStack);
+ if (value !== undefined) {
+ ol.DEBUG && console.assert(Array.isArray(value),
+ 'valueReader function is expected to return an array of values');
+ var array = /** @type {Array.<*>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(array),
+ 'objectStack is supposed to be an array of arrays');
+ ol.array.extend(array, value);
+ }
+ });
+};
+
+
+/**
+ * Make an array pusher function for pushing to the array at the top of the
+ * object stack.
+ * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
+ * @param {T=} opt_this The object to use as `this` in `valueReader`.
+ * @return {ol.XmlParser} Parser.
+ * @template T
+ */
+ol.xml.makeArrayPusher = function(valueReader, opt_this) {
+ return (
+ /**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ */
+ function(node, objectStack) {
+ var value = valueReader.call(opt_this !== undefined ? opt_this : this,
+ node, objectStack);
+ if (value !== undefined) {
+ var array = objectStack[objectStack.length - 1];
+ ol.DEBUG && console.assert(Array.isArray(array),
+ 'objectStack is supposed to be an array of arrays');
+ array.push(value);
+ }
+ });
+};
+
+
+/**
+ * Make an object stack replacer function for replacing the object at the
+ * top of the stack.
+ * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
+ * @param {T=} opt_this The object to use as `this` in `valueReader`.
+ * @return {ol.XmlParser} Parser.
+ * @template T
+ */
+ol.xml.makeReplacer = function(valueReader, opt_this) {
+ return (
+ /**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ */
+ function(node, objectStack) {
+ var value = valueReader.call(opt_this !== undefined ? opt_this : this,
+ node, objectStack);
+ if (value !== undefined) {
+ objectStack[objectStack.length - 1] = value;
+ }
+ });
+};
+
+
+/**
+ * Make an object property pusher function for adding a property to the
+ * object at the top of the stack.
+ * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
+ * @param {string=} opt_property Property.
+ * @param {T=} opt_this The object to use as `this` in `valueReader`.
+ * @return {ol.XmlParser} Parser.
+ * @template T
+ */
+ol.xml.makeObjectPropertyPusher = function(valueReader, opt_property, opt_this) {
+ ol.DEBUG && console.assert(valueReader !== undefined,
+ 'undefined valueReader, expected function(this: T, Node, Array.<*>)');
+ return (
+ /**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ */
+ function(node, objectStack) {
+ var value = valueReader.call(opt_this !== undefined ? opt_this : this,
+ node, objectStack);
+ if (value !== undefined) {
+ var object = /** @type {Object} */
+ (objectStack[objectStack.length - 1]);
+ var property = opt_property !== undefined ?
+ opt_property : node.localName;
+ var array;
+ if (property in object) {
+ array = object[property];
+ } else {
+ array = object[property] = [];
+ }
+ array.push(value);
+ }
+ });
+};
+
+
+/**
+ * Make an object property setter function.
+ * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
+ * @param {string=} opt_property Property.
+ * @param {T=} opt_this The object to use as `this` in `valueReader`.
+ * @return {ol.XmlParser} Parser.
+ * @template T
+ */
+ol.xml.makeObjectPropertySetter = function(valueReader, opt_property, opt_this) {
+ ol.DEBUG && console.assert(valueReader !== undefined,
+ 'undefined valueReader, expected function(this: T, Node, Array.<*>)');
+ return (
+ /**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ */
+ function(node, objectStack) {
+ var value = valueReader.call(opt_this !== undefined ? opt_this : this,
+ node, objectStack);
+ if (value !== undefined) {
+ var object = /** @type {Object} */
+ (objectStack[objectStack.length - 1]);
+ var property = opt_property !== undefined ?
+ opt_property : node.localName;
+ object[property] = value;
+ }
+ });
+};
+
+
+/**
+ * Create a serializer that appends nodes written by its `nodeWriter` to its
+ * designated parent. The parent is the `node` of the
+ * {@link ol.XmlNodeStackItem} at the top of the `objectStack`.
+ * @param {function(this: T, Node, V, Array.<*>)}
+ * nodeWriter Node writer.
+ * @param {T=} opt_this The object to use as `this` in `nodeWriter`.
+ * @return {ol.XmlSerializer} Serializer.
+ * @template T, V
+ */
+ol.xml.makeChildAppender = function(nodeWriter, opt_this) {
+ return function(node, value, objectStack) {
+ nodeWriter.call(opt_this !== undefined ? opt_this : this,
+ node, value, objectStack);
+ var parent = objectStack[objectStack.length - 1];
+ var parentNode = parent.node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode) ||
+ ol.xml.isDocument(parentNode),
+ 'expected parentNode %s to be a Node or a Document', parentNode);
+ parentNode.appendChild(node);
+ };
+};
+
+
+/**
+ * Create a serializer that calls the provided `nodeWriter` from
+ * {@link ol.xml.serialize}. This can be used by the parent writer to have the
+ * 'nodeWriter' called with an array of values when the `nodeWriter` was
+ * designed to serialize a single item. An example would be a LineString
+ * geometry writer, which could be reused for writing MultiLineString
+ * geometries.
+ * @param {function(this: T, Node, V, Array.<*>)}
+ * nodeWriter Node writer.
+ * @param {T=} opt_this The object to use as `this` in `nodeWriter`.
+ * @return {ol.XmlSerializer} Serializer.
+ * @template T, V
+ */
+ol.xml.makeArraySerializer = function(nodeWriter, opt_this) {
+ var serializersNS, nodeFactory;
+ return function(node, value, objectStack) {
+ if (serializersNS === undefined) {
+ serializersNS = {};
+ var serializers = {};
+ serializers[node.localName] = nodeWriter;
+ serializersNS[node.namespaceURI] = serializers;
+ nodeFactory = ol.xml.makeSimpleNodeFactory(node.localName);
+ }
+ ol.xml.serialize(serializersNS, nodeFactory, value, objectStack);
+ };
+};
+
+
+/**
+ * Create a node factory which can use the `opt_keys` passed to
+ * {@link ol.xml.serialize} or {@link ol.xml.pushSerializeAndPop} as node names,
+ * or a fixed node name. The namespace of the created nodes can either be fixed,
+ * or the parent namespace will be used.
+ * @param {string=} opt_nodeName Fixed node name which will be used for all
+ * created nodes. If not provided, the 3rd argument to the resulting node
+ * factory needs to be provided and will be the nodeName.
+ * @param {string=} opt_namespaceURI Fixed namespace URI which will be used for
+ * all created nodes. If not provided, the namespace of the parent node will
+ * be used.
+ * @return {function(*, Array.<*>, string=): (Node|undefined)} Node factory.
+ */
+ol.xml.makeSimpleNodeFactory = function(opt_nodeName, opt_namespaceURI) {
+ var fixedNodeName = opt_nodeName;
+ return (
+ /**
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node} Node.
+ */
+ function(value, objectStack, opt_nodeName) {
+ var context = objectStack[objectStack.length - 1];
+ var node = context.node;
+ ol.DEBUG && console.assert(ol.xml.isNode(node) || ol.xml.isDocument(node),
+ 'expected node %s to be a Node or a Document', node);
+ var nodeName = fixedNodeName;
+ if (nodeName === undefined) {
+ nodeName = opt_nodeName;
+ }
+ var namespaceURI = opt_namespaceURI;
+ if (opt_namespaceURI === undefined) {
+ namespaceURI = node.namespaceURI;
+ }
+ ol.DEBUG && console.assert(nodeName !== undefined, 'nodeName was undefined');
+ return ol.xml.createElementNS(namespaceURI, /** @type {string} */ (nodeName));
+ }
+ );
+};
+
+
+/**
+ * A node factory that creates a node using the parent's `namespaceURI` and the
+ * `nodeName` passed by {@link ol.xml.serialize} or
+ * {@link ol.xml.pushSerializeAndPop} to the node factory.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ */
+ol.xml.OBJECT_PROPERTY_NODE_FACTORY = ol.xml.makeSimpleNodeFactory();
+
+
+/**
+ * Create an array of `values` to be used with {@link ol.xml.serialize} or
+ * {@link ol.xml.pushSerializeAndPop}, where `orderedKeys` has to be provided as
+ * `opt_key` argument.
+ * @param {Object.<string, V>} object Key-value pairs for the sequence. Keys can
+ * be a subset of the `orderedKeys`.
+ * @param {Array.<string>} orderedKeys Keys in the order of the sequence.
+ * @return {Array.<V>} Values in the order of the sequence. The resulting array
+ * has the same length as the `orderedKeys` array. Values that are not
+ * present in `object` will be `undefined` in the resulting array.
+ * @template V
+ */
+ol.xml.makeSequence = function(object, orderedKeys) {
+ var length = orderedKeys.length;
+ var sequence = new Array(length);
+ for (var i = 0; i < length; ++i) {
+ sequence[i] = object[orderedKeys[i]];
+ }
+ return sequence;
+};
+
+
+/**
+ * Create a namespaced structure, using the same values for each namespace.
+ * This can be used as a starting point for versioned parsers, when only a few
+ * values are version specific.
+ * @param {Array.<string>} namespaceURIs Namespace URIs.
+ * @param {T} structure Structure.
+ * @param {Object.<string, T>=} opt_structureNS Namespaced structure to add to.
+ * @return {Object.<string, T>} Namespaced structure.
+ * @template T
+ */
+ol.xml.makeStructureNS = function(namespaceURIs, structure, opt_structureNS) {
+ /**
+ * @type {Object.<string, *>}
+ */
+ var structureNS = opt_structureNS !== undefined ? opt_structureNS : {};
+ var i, ii;
+ for (i = 0, ii = namespaceURIs.length; i < ii; ++i) {
+ structureNS[namespaceURIs[i]] = structure;
+ }
+ return structureNS;
+};
+
+
+/**
+ * Parse a node using the parsers and object stack.
+ * @param {Object.<string, Object.<string, ol.XmlParser>>} parsersNS
+ * Parsers by namespace.
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {*=} opt_this The object to use as `this`.
+ */
+ol.xml.parseNode = function(parsersNS, node, objectStack, opt_this) {
+ var n;
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ var parsers = parsersNS[n.namespaceURI];
+ if (parsers !== undefined) {
+ var parser = parsers[n.localName];
+ if (parser !== undefined) {
+ parser.call(opt_this, n, objectStack);
+ }
+ }
+ }
+};
+
+
+/**
+ * Push an object on top of the stack, parse and return the popped object.
+ * @param {T} object Object.
+ * @param {Object.<string, Object.<string, ol.XmlParser>>} parsersNS
+ * Parsers by namespace.
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {*=} opt_this The object to use as `this`.
+ * @return {T} Object.
+ * @template T
+ */
+ol.xml.pushParseAndPop = function(
+ object, parsersNS, node, objectStack, opt_this) {
+ objectStack.push(object);
+ ol.xml.parseNode(parsersNS, node, objectStack, opt_this);
+ return objectStack.pop();
+};
+
+
+/**
+ * Walk through an array of `values` and call a serializer for each value.
+ * @param {Object.<string, Object.<string, ol.XmlSerializer>>} serializersNS
+ * Namespaced serializers.
+ * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory
+ * Node factory. The `nodeFactory` creates the node whose namespace and name
+ * will be used to choose a node writer from `serializersNS`. This
+ * separation allows us to decide what kind of node to create, depending on
+ * the value we want to serialize. An example for this would be different
+ * geometry writers based on the geometry type.
+ * @param {Array.<*>} values Values to serialize. An example would be an array
+ * of {@link ol.Feature} instances.
+ * @param {Array.<*>} objectStack Node stack.
+ * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the
+ * `nodeFactory`. This is used for serializing object literals where the
+ * node name relates to the property key. The array length of `opt_keys` has
+ * to match the length of `values`. For serializing a sequence, `opt_keys`
+ * determines the order of the sequence.
+ * @param {T=} opt_this The object to use as `this` for the node factory and
+ * serializers.
+ * @template T
+ */
+ol.xml.serialize = function(
+ serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) {
+ var length = (opt_keys !== undefined ? opt_keys : values).length;
+ var value, node;
+ for (var i = 0; i < length; ++i) {
+ value = values[i];
+ if (value !== undefined) {
+ node = nodeFactory.call(opt_this, value, objectStack,
+ opt_keys !== undefined ? opt_keys[i] : undefined);
+ if (node !== undefined) {
+ serializersNS[node.namespaceURI][node.localName]
+ .call(opt_this, node, value, objectStack);
+ }
+ }
+ }
+};
+
+
+/**
+ * @param {O} object Object.
+ * @param {Object.<string, Object.<string, ol.XmlSerializer>>} serializersNS
+ * Namespaced serializers.
+ * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory
+ * Node factory. The `nodeFactory` creates the node whose namespace and name
+ * will be used to choose a node writer from `serializersNS`. This
+ * separation allows us to decide what kind of node to create, depending on
+ * the value we want to serialize. An example for this would be different
+ * geometry writers based on the geometry type.
+ * @param {Array.<*>} values Values to serialize. An example would be an array
+ * of {@link ol.Feature} instances.
+ * @param {Array.<*>} objectStack Node stack.
+ * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the
+ * `nodeFactory`. This is used for serializing object literals where the
+ * node name relates to the property key. The array length of `opt_keys` has
+ * to match the length of `values`. For serializing a sequence, `opt_keys`
+ * determines the order of the sequence.
+ * @param {T=} opt_this The object to use as `this` for the node factory and
+ * serializers.
+ * @return {O|undefined} Object.
+ * @template O, T
+ */
+ol.xml.pushSerializeAndPop = function(object,
+ serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) {
+ objectStack.push(object);
+ ol.xml.serialize(
+ serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this);
+ return objectStack.pop();
+};
+
+goog.provide('ol.featureloader');
+
+goog.require('ol');
+goog.require('ol.format.FormatType');
+goog.require('ol.xml');
+
+
+/**
+ * @param {string|ol.FeatureUrlFunction} url Feature URL service.
+ * @param {ol.format.Feature} format Feature format.
+ * @param {function(this:ol.VectorTile, Array.<ol.Feature>, ol.proj.Projection)|function(this:ol.source.Vector, Array.<ol.Feature>)} success
+ * Function called with the loaded features and optionally with the data
+ * projection. Called with the vector tile or source as `this`.
+ * @param {function(this:ol.VectorTile)|function(this:ol.source.Vector)} failure
+ * Function called when loading failed. Called with the vector tile or
+ * source as `this`.
+ * @return {ol.FeatureLoader} The feature loader.
+ */
+ol.featureloader.loadFeaturesXhr = function(url, format, success, failure) {
+ return (
+ /**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {ol.proj.Projection} projection Projection.
+ * @this {ol.source.Vector|ol.VectorTile}
+ */
+ function(extent, resolution, projection) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET',
+ typeof url === 'function' ? url(extent, resolution, projection) : url,
+ true);
+ if (format.getType() == ol.format.FormatType.ARRAY_BUFFER) {
+ xhr.responseType = 'arraybuffer';
+ }
+ /**
+ * @param {Event} event Event.
+ * @private
+ */
+ xhr.onload = function(event) {
+ // status will be 0 for file:// urls
+ if (!xhr.status || xhr.status >= 200 && xhr.status < 300) {
+ var type = format.getType();
+ /** @type {Document|Node|Object|string|undefined} */
+ var source;
+ if (type == ol.format.FormatType.JSON ||
+ type == ol.format.FormatType.TEXT) {
+ source = xhr.responseText;
+ } else if (type == ol.format.FormatType.XML) {
+ source = xhr.responseXML;
+ if (!source) {
+ source = ol.xml.parse(xhr.responseText);
+ }
+ } else if (type == ol.format.FormatType.ARRAY_BUFFER) {
+ source = /** @type {ArrayBuffer} */ (xhr.response);
+ }
+ if (source) {
+ success.call(this, format.readFeatures(source,
+ {featureProjection: projection}),
+ format.readProjection(source));
+ } else {
+ failure.call(this);
+ }
+ } else {
+ failure.call(this);
+ }
+ }.bind(this);
+ xhr.send();
+ });
+};
+
+
+/**
+ * Create an XHR feature loader for a `url` and `format`. The feature loader
+ * loads features (with XHR), parses the features, and adds them to the
+ * vector source.
+ * @param {string|ol.FeatureUrlFunction} url Feature URL service.
+ * @param {ol.format.Feature} format Feature format.
+ * @return {ol.FeatureLoader} The feature loader.
+ * @api
+ */
+ol.featureloader.xhr = function(url, format) {
+ return ol.featureloader.loadFeaturesXhr(url, format,
+ /**
+ * @param {Array.<ol.Feature>} features The loaded features.
+ * @param {ol.proj.Projection} dataProjection Data projection.
+ * @this {ol.source.Vector}
+ */
+ function(features, dataProjection) {
+ this.addFeatures(features);
+ }, /* FIXME handle error */ ol.nullFunction);
+};
+
+goog.provide('ol.format.Feature');
+
+goog.require('ol.geom.Geometry');
+goog.require('ol.obj');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for feature formats.
+ * {ol.format.Feature} subclasses provide the ability to decode and encode
+ * {@link ol.Feature} objects from a variety of commonly used geospatial
+ * file formats. See the documentation for each format for more details.
+ *
+ * @constructor
+ * @api stable
+ */
+ol.format.Feature = function() {
+
+ /**
+ * @protected
+ * @type {ol.proj.Projection}
+ */
+ this.defaultDataProjection = null;
+
+ /**
+ * @protected
+ * @type {ol.proj.Projection}
+ */
+ this.defaultFeatureProjection = null;
+
+};
+
+
+/**
+ * @abstract
+ * @return {Array.<string>} Extensions.
+ */
+ol.format.Feature.prototype.getExtensions = function() {};
+
+
+/**
+ * Adds the data projection to the read options.
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @return {olx.format.ReadOptions|undefined} Options.
+ * @protected
+ */
+ol.format.Feature.prototype.getReadOptions = function(source, opt_options) {
+ var options;
+ if (opt_options) {
+ options = {
+ dataProjection: opt_options.dataProjection ?
+ opt_options.dataProjection : this.readProjection(source),
+ featureProjection: opt_options.featureProjection
+ };
+ }
+ return this.adaptOptions(options);
+};
+
+
+/**
+ * Sets the `defaultDataProjection` on the options, if no `dataProjection`
+ * is set.
+ * @param {olx.format.WriteOptions|olx.format.ReadOptions|undefined} options
+ * Options.
+ * @protected
+ * @return {olx.format.WriteOptions|olx.format.ReadOptions|undefined}
+ * Updated options.
+ */
+ol.format.Feature.prototype.adaptOptions = function(options) {
+ return ol.obj.assign({
+ dataProjection: this.defaultDataProjection,
+ featureProjection: this.defaultFeatureProjection
+ }, options);
+};
+
+
+/**
+ * @abstract
+ * @return {ol.format.FormatType} Format.
+ */
+ol.format.Feature.prototype.getType = function() {};
+
+
+/**
+ * Read a single feature from a source.
+ *
+ * @abstract
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ */
+ol.format.Feature.prototype.readFeature = function(source, opt_options) {};
+
+
+/**
+ * Read all features from a source.
+ *
+ * @abstract
+ * @param {Document|Node|ArrayBuffer|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.format.Feature.prototype.readFeatures = function(source, opt_options) {};
+
+
+/**
+ * Read a single geometry from a source.
+ *
+ * @abstract
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.Feature.prototype.readGeometry = function(source, opt_options) {};
+
+
+/**
+ * Read the projection from a source.
+ *
+ * @abstract
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.format.Feature.prototype.readProjection = function(source) {};
+
+
+/**
+ * Encode a feature in this format.
+ *
+ * @abstract
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} Result.
+ */
+ol.format.Feature.prototype.writeFeature = function(feature, opt_options) {};
+
+
+/**
+ * Encode an array of features in this format.
+ *
+ * @abstract
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} Result.
+ */
+ol.format.Feature.prototype.writeFeatures = function(features, opt_options) {};
+
+
+/**
+ * Write a single geometry in this format.
+ *
+ * @abstract
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} Result.
+ */
+ol.format.Feature.prototype.writeGeometry = function(geometry, opt_options) {};
+
+
+/**
+ * @param {ol.geom.Geometry|ol.Extent} geometry Geometry.
+ * @param {boolean} write Set to true for writing, false for reading.
+ * @param {(olx.format.WriteOptions|olx.format.ReadOptions)=} opt_options
+ * Options.
+ * @return {ol.geom.Geometry|ol.Extent} Transformed geometry.
+ * @protected
+ */
+ol.format.Feature.transformWithOptions = function(
+ geometry, write, opt_options) {
+ var featureProjection = opt_options ?
+ ol.proj.get(opt_options.featureProjection) : null;
+ var dataProjection = opt_options ?
+ ol.proj.get(opt_options.dataProjection) : null;
+ /**
+ * @type {ol.geom.Geometry|ol.Extent}
+ */
+ var transformed;
+ if (featureProjection && dataProjection &&
+ !ol.proj.equivalent(featureProjection, dataProjection)) {
+ if (geometry instanceof ol.geom.Geometry) {
+ transformed = (write ? geometry.clone() : geometry).transform(
+ write ? featureProjection : dataProjection,
+ write ? dataProjection : featureProjection);
+ } else {
+ // FIXME this is necessary because ol.format.GML treats extents
+ // as geometries
+ transformed = ol.proj.transformExtent(
+ write ? geometry.slice() : geometry,
+ write ? featureProjection : dataProjection,
+ write ? dataProjection : featureProjection);
+ }
+ } else {
+ transformed = geometry;
+ }
+ if (write && opt_options && opt_options.decimals) {
+ var power = Math.pow(10, opt_options.decimals);
+ // if decimals option on write, round each coordinate appropriately
+ /**
+ * @param {Array.<number>} coordinates Coordinates.
+ * @return {Array.<number>} Transformed coordinates.
+ */
+ var transform = function(coordinates) {
+ for (var i = 0, ii = coordinates.length; i < ii; ++i) {
+ coordinates[i] = Math.round(coordinates[i] * power) / power;
+ }
+ return coordinates;
+ };
+ if (Array.isArray(transformed)) {
+ transform(transformed);
+ } else {
+ transformed.applyTransform(transform);
+ }
+ }
+ return transformed;
+};
+
+goog.provide('ol.format.JSONFeature');
+
+goog.require('ol');
+goog.require('ol.format.Feature');
+goog.require('ol.format.FormatType');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for JSON feature formats.
+ *
+ * @constructor
+ * @extends {ol.format.Feature}
+ */
+ol.format.JSONFeature = function() {
+ ol.format.Feature.call(this);
+};
+ol.inherits(ol.format.JSONFeature, ol.format.Feature);
+
+
+/**
+ * @param {Document|Node|Object|string} source Source.
+ * @private
+ * @return {Object} Object.
+ */
+ol.format.JSONFeature.prototype.getObject_ = function(source) {
+ if (typeof source === 'string') {
+ var object = JSON.parse(source);
+ return object ? /** @type {Object} */ (object) : null;
+ } else if (source !== null) {
+ return source;
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.getType = function() {
+ return ol.format.FormatType.JSON;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.readFeature = function(source, opt_options) {
+ return this.readFeatureFromObject(
+ this.getObject_(source), this.getReadOptions(source, opt_options));
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.readFeatures = function(source, opt_options) {
+ return this.readFeaturesFromObject(
+ this.getObject_(source), this.getReadOptions(source, opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {Object} object Object.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @protected
+ * @return {ol.Feature} Feature.
+ */
+ol.format.JSONFeature.prototype.readFeatureFromObject = function(object, opt_options) {};
+
+
+/**
+ * @abstract
+ * @param {Object} object Object.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @protected
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.format.JSONFeature.prototype.readFeaturesFromObject = function(object, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.readGeometry = function(source, opt_options) {
+ return this.readGeometryFromObject(
+ this.getObject_(source), this.getReadOptions(source, opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {Object} object Object.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @protected
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.JSONFeature.prototype.readGeometryFromObject = function(object, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.readProjection = function(source) {
+ return this.readProjectionFromObject(this.getObject_(source));
+};
+
+
+/**
+ * @abstract
+ * @param {Object} object Object.
+ * @protected
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.format.JSONFeature.prototype.readProjectionFromObject = function(object) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.writeFeature = function(feature, opt_options) {
+ return JSON.stringify(this.writeFeatureObject(feature, opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {Object} Object.
+ */
+ol.format.JSONFeature.prototype.writeFeatureObject = function(feature, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.writeFeatures = function(features, opt_options) {
+ return JSON.stringify(this.writeFeaturesObject(features, opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {Object} Object.
+ */
+ol.format.JSONFeature.prototype.writeFeaturesObject = function(features, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.JSONFeature.prototype.writeGeometry = function(geometry, opt_options) {
+ return JSON.stringify(this.writeGeometryObject(geometry, opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {Object} Object.
+ */
+ol.format.JSONFeature.prototype.writeGeometryObject = function(geometry, opt_options) {};
+
+goog.provide('ol.geom.flat.interpolate');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.math');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} fraction Fraction.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @return {Array.<number>} Destination.
+ */
+ol.geom.flat.interpolate.lineString = function(flatCoordinates, offset, end, stride, fraction, opt_dest) {
+ // FIXME does not work when vertices are repeated
+ // FIXME interpolate extra dimensions
+ ol.DEBUG && console.assert(0 <= fraction && fraction <= 1,
+ 'fraction should be in between 0 and 1');
+ var pointX = NaN;
+ var pointY = NaN;
+ var n = (end - offset) / stride;
+ if (n === 0) {
+ ol.DEBUG && console.assert(false, 'n cannot be 0');
+ } else if (n == 1) {
+ pointX = flatCoordinates[offset];
+ pointY = flatCoordinates[offset + 1];
+ } else if (n == 2) {
+ pointX = (1 - fraction) * flatCoordinates[offset] +
+ fraction * flatCoordinates[offset + stride];
+ pointY = (1 - fraction) * flatCoordinates[offset + 1] +
+ fraction * flatCoordinates[offset + stride + 1];
+ } else {
+ var x1 = flatCoordinates[offset];
+ var y1 = flatCoordinates[offset + 1];
+ var length = 0;
+ var cumulativeLengths = [0];
+ var i;
+ for (i = offset + stride; i < end; i += stride) {
+ var x2 = flatCoordinates[i];
+ var y2 = flatCoordinates[i + 1];
+ length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
+ cumulativeLengths.push(length);
+ x1 = x2;
+ y1 = y2;
+ }
+ var target = fraction * length;
+ var index = ol.array.binarySearch(cumulativeLengths, target);
+ if (index < 0) {
+ var t = (target - cumulativeLengths[-index - 2]) /
+ (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]);
+ var o = offset + (-index - 2) * stride;
+ pointX = ol.math.lerp(
+ flatCoordinates[o], flatCoordinates[o + stride], t);
+ pointY = ol.math.lerp(
+ flatCoordinates[o + 1], flatCoordinates[o + stride + 1], t);
+ } else {
+ pointX = flatCoordinates[offset + index * stride];
+ pointY = flatCoordinates[offset + index * stride + 1];
+ }
+ }
+ if (opt_dest) {
+ opt_dest[0] = pointX;
+ opt_dest[1] = pointY;
+ return opt_dest;
+ } else {
+ return [pointX, pointY];
+ }
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {number} m M.
+ * @param {boolean} extrapolate Extrapolate.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ol.geom.flat.interpolate.lineStringCoordinateAtM = function(flatCoordinates, offset, end, stride, m, extrapolate) {
+ if (end == offset) {
+ return null;
+ }
+ var coordinate;
+ if (m < flatCoordinates[offset + stride - 1]) {
+ if (extrapolate) {
+ coordinate = flatCoordinates.slice(offset, offset + stride);
+ coordinate[stride - 1] = m;
+ return coordinate;
+ } else {
+ return null;
+ }
+ } else if (flatCoordinates[end - 1] < m) {
+ if (extrapolate) {
+ coordinate = flatCoordinates.slice(end - stride, end);
+ coordinate[stride - 1] = m;
+ return coordinate;
+ } else {
+ return null;
+ }
+ }
+ // FIXME use O(1) search
+ if (m == flatCoordinates[offset + stride - 1]) {
+ return flatCoordinates.slice(offset, offset + stride);
+ }
+ var lo = offset / stride;
+ var hi = end / stride;
+ while (lo < hi) {
+ var mid = (lo + hi) >> 1;
+ if (m < flatCoordinates[(mid + 1) * stride - 1]) {
+ hi = mid;
+ } else {
+ lo = mid + 1;
+ }
+ }
+ var m0 = flatCoordinates[lo * stride - 1];
+ if (m == m0) {
+ return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride);
+ }
+ var m1 = flatCoordinates[(lo + 1) * stride - 1];
+ ol.DEBUG && console.assert(m0 < m, 'm0 should be less than m');
+ ol.DEBUG && console.assert(m <= m1, 'm should be less than or equal to m1');
+ var t = (m - m0) / (m1 - m0);
+ coordinate = [];
+ var i;
+ for (i = 0; i < stride - 1; ++i) {
+ coordinate.push(ol.math.lerp(flatCoordinates[(lo - 1) * stride + i],
+ flatCoordinates[lo * stride + i], t));
+ }
+ coordinate.push(m);
+ ol.DEBUG && console.assert(coordinate.length == stride,
+ 'length of coordinate array should match stride');
+ return coordinate;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<number>} ends Ends.
+ * @param {number} stride Stride.
+ * @param {number} m M.
+ * @param {boolean} extrapolate Extrapolate.
+ * @param {boolean} interpolate Interpolate.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ol.geom.flat.interpolate.lineStringsCoordinateAtM = function(
+ flatCoordinates, offset, ends, stride, m, extrapolate, interpolate) {
+ if (interpolate) {
+ return ol.geom.flat.interpolate.lineStringCoordinateAtM(
+ flatCoordinates, offset, ends[ends.length - 1], stride, m, extrapolate);
+ }
+ var coordinate;
+ if (m < flatCoordinates[stride - 1]) {
+ if (extrapolate) {
+ coordinate = flatCoordinates.slice(0, stride);
+ coordinate[stride - 1] = m;
+ return coordinate;
+ } else {
+ return null;
+ }
+ }
+ if (flatCoordinates[flatCoordinates.length - 1] < m) {
+ if (extrapolate) {
+ coordinate = flatCoordinates.slice(flatCoordinates.length - stride);
+ coordinate[stride - 1] = m;
+ return coordinate;
+ } else {
+ return null;
+ }
+ }
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ if (offset == end) {
+ continue;
+ }
+ if (m < flatCoordinates[offset + stride - 1]) {
+ return null;
+ } else if (m <= flatCoordinates[end - 1]) {
+ return ol.geom.flat.interpolate.lineStringCoordinateAtM(
+ flatCoordinates, offset, end, stride, m, false);
+ }
+ offset = end;
+ }
+ ol.DEBUG && console.assert(false,
+ 'ol.geom.flat.interpolate.lineStringsCoordinateAtM should have returned');
+ return null;
+};
+
+goog.provide('ol.geom.flat.length');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {number} Length.
+ */
+ol.geom.flat.length.lineString = function(flatCoordinates, offset, end, stride) {
+ var x1 = flatCoordinates[offset];
+ var y1 = flatCoordinates[offset + 1];
+ var length = 0;
+ var i;
+ for (i = offset + stride; i < end; i += stride) {
+ var x2 = flatCoordinates[i];
+ var y2 = flatCoordinates[i + 1];
+ length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
+ x1 = x2;
+ y1 = y2;
+ }
+ return length;
+};
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @return {number} Perimeter.
+ */
+ol.geom.flat.length.linearRing = function(flatCoordinates, offset, end, stride) {
+ var perimeter =
+ ol.geom.flat.length.lineString(flatCoordinates, offset, end, stride);
+ var dx = flatCoordinates[end - stride] - flatCoordinates[offset];
+ var dy = flatCoordinates[end - stride + 1] - flatCoordinates[offset + 1];
+ perimeter += Math.sqrt(dx * dx + dy * dy);
+ return perimeter;
+};
+
+goog.provide('ol.geom.LineString');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.closest');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.geom.flat.interpolate');
+goog.require('ol.geom.flat.intersectsextent');
+goog.require('ol.geom.flat.length');
+goog.require('ol.geom.flat.segments');
+goog.require('ol.geom.flat.simplify');
+
+
+/**
+ * @classdesc
+ * Linestring geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.LineString = function(coordinates, opt_layout) {
+
+ ol.geom.SimpleGeometry.call(this);
+
+ /**
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.flatMidpoint_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.flatMidpointRevision_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDelta_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDeltaRevision_ = -1;
+
+ this.setCoordinates(coordinates, opt_layout);
+
+};
+ol.inherits(ol.geom.LineString, ol.geom.SimpleGeometry);
+
+
+/**
+ * Append the passed coordinate to the coordinates of the linestring.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @api stable
+ */
+ol.geom.LineString.prototype.appendCoordinate = function(coordinate) {
+ ol.DEBUG && console.assert(coordinate.length == this.stride,
+ 'length of coordinate array should match stride');
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = coordinate.slice();
+ } else {
+ ol.array.extend(this.flatCoordinates, coordinate);
+ }
+ this.changed();
+};
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.LineString} Clone.
+ * @api stable
+ */
+ol.geom.LineString.prototype.clone = function() {
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
+ return lineString;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.LineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ if (this.maxDeltaRevision_ != this.getRevision()) {
+ this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
+ this.maxDeltaRevision_ = this.getRevision();
+ }
+ return ol.geom.flat.closest.getClosestPoint(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
+};
+
+
+/**
+ * Iterate over each segment, calling the provided callback.
+ * If the callback returns a truthy value the function returns that
+ * value immediately. Otherwise the function returns `false`.
+ *
+ * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function
+ * called for each segment.
+ * @param {S=} opt_this The object to be used as the value of 'this'
+ * within callback.
+ * @return {T|boolean} Value.
+ * @template T,S
+ * @api
+ */
+ol.geom.LineString.prototype.forEachSegment = function(callback, opt_this) {
+ return ol.geom.flat.segments.forEach(this.flatCoordinates, 0,
+ this.flatCoordinates.length, this.stride, callback, opt_this);
+};
+
+
+/**
+ * Returns the coordinate at `m` using linear interpolation, or `null` if no
+ * such coordinate exists.
+ *
+ * `opt_extrapolate` controls extrapolation beyond the range of Ms in the
+ * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
+ * M will return the first coordinate and Ms greater than the last M will
+ * return the last coordinate.
+ *
+ * @param {number} m M.
+ * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
+ * @return {ol.Coordinate} Coordinate.
+ * @api stable
+ */
+ol.geom.LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) {
+ if (this.layout != ol.geom.GeometryLayout.XYM &&
+ this.layout != ol.geom.GeometryLayout.XYZM) {
+ return null;
+ }
+ var extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false;
+ return ol.geom.flat.interpolate.lineStringCoordinateAtM(this.flatCoordinates, 0,
+ this.flatCoordinates.length, this.stride, m, extrapolate);
+};
+
+
+/**
+ * Return the coordinates of the linestring.
+ * @return {Array.<ol.Coordinate>} Coordinates.
+ * @api stable
+ */
+ol.geom.LineString.prototype.getCoordinates = function() {
+ return ol.geom.flat.inflate.coordinates(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
+};
+
+
+/**
+ * Return the coordinate at the provided fraction along the linestring.
+ * The `fraction` is a number between 0 and 1, where 0 is the start of the
+ * linestring and 1 is the end.
+ * @param {number} fraction Fraction.
+ * @param {ol.Coordinate=} opt_dest Optional coordinate whose values will
+ * be modified. If not provided, a new coordinate will be returned.
+ * @return {ol.Coordinate} Coordinate of the interpolated point.
+ * @api
+ */
+ol.geom.LineString.prototype.getCoordinateAt = function(fraction, opt_dest) {
+ return ol.geom.flat.interpolate.lineString(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ fraction, opt_dest);
+};
+
+
+/**
+ * Return the length of the linestring on projected plane.
+ * @return {number} Length (on projected plane).
+ * @api stable
+ */
+ol.geom.LineString.prototype.getLength = function() {
+ return ol.geom.flat.length.lineString(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
+};
+
+
+/**
+ * @return {Array.<number>} Flat midpoint.
+ */
+ol.geom.LineString.prototype.getFlatMidpoint = function() {
+ if (this.flatMidpointRevision_ != this.getRevision()) {
+ this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_);
+ this.flatMidpointRevision_ = this.getRevision();
+ }
+ return this.flatMidpoint_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.LineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
+ var simplifiedFlatCoordinates = [];
+ simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ squaredTolerance, simplifiedFlatCoordinates, 0);
+ var simplifiedLineString = new ol.geom.LineString(null);
+ simplifiedLineString.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates);
+ return simplifiedLineString;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.LineString.prototype.getType = function() {
+ return ol.geom.GeometryType.LINE_STRING;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.LineString.prototype.intersectsExtent = function(extent) {
+ return ol.geom.flat.intersectsextent.lineString(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
+ extent);
+};
+
+
+/**
+ * Set the coordinates of the linestring.
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.LineString.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
+ } else {
+ this.setLayout(opt_layout, coordinates, 1);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ this.flatCoordinates.length = ol.geom.flat.deflate.coordinates(
+ this.flatCoordinates, 0, coordinates, this.stride);
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ */
+ol.geom.LineString.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.changed();
+};
+
+goog.provide('ol.geom.MultiLineString');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.closest');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.geom.flat.interpolate');
+goog.require('ol.geom.flat.intersectsextent');
+goog.require('ol.geom.flat.simplify');
+
+
+/**
+ * @classdesc
+ * Multi-linestring geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.MultiLineString = function(coordinates, opt_layout) {
+
+ ol.geom.SimpleGeometry.call(this);
+
+ /**
+ * @type {Array.<number>}
+ * @private
+ */
+ this.ends_ = [];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDelta_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDeltaRevision_ = -1;
+
+ this.setCoordinates(coordinates, opt_layout);
+
+};
+ol.inherits(ol.geom.MultiLineString, ol.geom.SimpleGeometry);
+
+
+/**
+ * Append the passed linestring to the multilinestring.
+ * @param {ol.geom.LineString} lineString LineString.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.appendLineString = function(lineString) {
+ ol.DEBUG && console.assert(lineString.getLayout() == this.layout,
+ 'layout of lineString should match the layout');
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = lineString.getFlatCoordinates().slice();
+ } else {
+ ol.array.extend(
+ this.flatCoordinates, lineString.getFlatCoordinates().slice());
+ }
+ this.ends_.push(this.flatCoordinates.length);
+ this.changed();
+};
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.MultiLineString} Clone.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.clone = function() {
+ var multiLineString = new ol.geom.MultiLineString(null);
+ multiLineString.setFlatCoordinates(
+ this.layout, this.flatCoordinates.slice(), this.ends_.slice());
+ return multiLineString;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.MultiLineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ if (this.maxDeltaRevision_ != this.getRevision()) {
+ this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta(
+ this.flatCoordinates, 0, this.ends_, this.stride, 0));
+ this.maxDeltaRevision_ = this.getRevision();
+ }
+ return ol.geom.flat.closest.getsClosestPoint(
+ this.flatCoordinates, 0, this.ends_, this.stride,
+ this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
+};
+
+
+/**
+ * Returns the coordinate at `m` using linear interpolation, or `null` if no
+ * such coordinate exists.
+ *
+ * `opt_extrapolate` controls extrapolation beyond the range of Ms in the
+ * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
+ * M will return the first coordinate and Ms greater than the last M will
+ * return the last coordinate.
+ *
+ * `opt_interpolate` controls interpolation between consecutive LineStrings
+ * within the MultiLineString. If `opt_interpolate` is `true` the coordinates
+ * will be linearly interpolated between the last coordinate of one LineString
+ * and the first coordinate of the next LineString. If `opt_interpolate` is
+ * `false` then the function will return `null` for Ms falling between
+ * LineStrings.
+ *
+ * @param {number} m M.
+ * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
+ * @param {boolean=} opt_interpolate Interpolate. Default is `false`.
+ * @return {ol.Coordinate} Coordinate.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.getCoordinateAtM = function(m, opt_extrapolate, opt_interpolate) {
+ if ((this.layout != ol.geom.GeometryLayout.XYM &&
+ this.layout != ol.geom.GeometryLayout.XYZM) ||
+ this.flatCoordinates.length === 0) {
+ return null;
+ }
+ var extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false;
+ var interpolate = opt_interpolate !== undefined ? opt_interpolate : false;
+ return ol.geom.flat.interpolate.lineStringsCoordinateAtM(this.flatCoordinates, 0,
+ this.ends_, this.stride, m, extrapolate, interpolate);
+};
+
+
+/**
+ * Return the coordinates of the multilinestring.
+ * @return {Array.<Array.<ol.Coordinate>>} Coordinates.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.getCoordinates = function() {
+ return ol.geom.flat.inflate.coordinatess(
+ this.flatCoordinates, 0, this.ends_, this.stride);
+};
+
+
+/**
+ * @return {Array.<number>} Ends.
+ */
+ol.geom.MultiLineString.prototype.getEnds = function() {
+ return this.ends_;
+};
+
+
+/**
+ * Return the linestring at the specified index.
+ * @param {number} index Index.
+ * @return {ol.geom.LineString} LineString.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.getLineString = function(index) {
+ ol.DEBUG && console.assert(0 <= index && index < this.ends_.length,
+ 'index should be in between 0 and length of the this.ends_ array');
+ if (index < 0 || this.ends_.length <= index) {
+ return null;
+ }
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice(
+ index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]));
+ return lineString;
+};
+
+
+/**
+ * Return the linestrings of this multilinestring.
+ * @return {Array.<ol.geom.LineString>} LineStrings.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.getLineStrings = function() {
+ var flatCoordinates = this.flatCoordinates;
+ var ends = this.ends_;
+ var layout = this.layout;
+ /** @type {Array.<ol.geom.LineString>} */
+ var lineStrings = [];
+ var offset = 0;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(layout, flatCoordinates.slice(offset, end));
+ lineStrings.push(lineString);
+ offset = end;
+ }
+ return lineStrings;
+};
+
+
+/**
+ * @return {Array.<number>} Flat midpoints.
+ */
+ol.geom.MultiLineString.prototype.getFlatMidpoints = function() {
+ var midpoints = [];
+ var flatCoordinates = this.flatCoordinates;
+ var offset = 0;
+ var ends = this.ends_;
+ var stride = this.stride;
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ var end = ends[i];
+ var midpoint = ol.geom.flat.interpolate.lineString(
+ flatCoordinates, offset, end, stride, 0.5);
+ ol.array.extend(midpoints, midpoint);
+ offset = end;
+ }
+ return midpoints;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.MultiLineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
+ var simplifiedFlatCoordinates = [];
+ var simplifiedEnds = [];
+ simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeuckers(
+ this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance,
+ simplifiedFlatCoordinates, 0, simplifiedEnds);
+ var simplifiedMultiLineString = new ol.geom.MultiLineString(null);
+ simplifiedMultiLineString.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds);
+ return simplifiedMultiLineString;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.getType = function() {
+ return ol.geom.GeometryType.MULTI_LINE_STRING;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.intersectsExtent = function(extent) {
+ return ol.geom.flat.intersectsextent.lineStrings(
+ this.flatCoordinates, 0, this.ends_, this.stride, extent);
+};
+
+
+/**
+ * Set the coordinates of the multilinestring.
+ * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.MultiLineString.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_);
+ } else {
+ this.setLayout(opt_layout, coordinates, 2);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ var ends = ol.geom.flat.deflate.coordinatess(
+ this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
+ this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {Array.<number>} ends Ends.
+ */
+ol.geom.MultiLineString.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) {
+ if (!flatCoordinates) {
+ ol.DEBUG && console.assert(ends && ends.length === 0,
+ 'ends must be truthy and ends.length should be 0');
+ } else if (ends.length === 0) {
+ ol.DEBUG && console.assert(flatCoordinates.length === 0,
+ 'flatCoordinates should be an empty array');
+ } else {
+ ol.DEBUG && console.assert(flatCoordinates.length == ends[ends.length - 1],
+ 'length of flatCoordinates array should match the last value of ends');
+ }
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.ends_ = ends;
+ this.changed();
+};
+
+
+/**
+ * @param {Array.<ol.geom.LineString>} lineStrings LineStrings.
+ */
+ol.geom.MultiLineString.prototype.setLineStrings = function(lineStrings) {
+ var layout = this.getLayout();
+ var flatCoordinates = [];
+ var ends = [];
+ var i, ii;
+ for (i = 0, ii = lineStrings.length; i < ii; ++i) {
+ var lineString = lineStrings[i];
+ if (i === 0) {
+ layout = lineString.getLayout();
+ } else {
+ // FIXME better handle the case of non-matching layouts
+ ol.DEBUG && console.assert(lineString.getLayout() == layout,
+ 'layout of lineString should match layout');
+ }
+ ol.array.extend(flatCoordinates, lineString.getFlatCoordinates());
+ ends.push(flatCoordinates.length);
+ }
+ this.setFlatCoordinates(layout, flatCoordinates, ends);
+};
+
+goog.provide('ol.geom.MultiPoint');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.math');
+
+
+/**
+ * @classdesc
+ * Multi-point geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.MultiPoint = function(coordinates, opt_layout) {
+ ol.geom.SimpleGeometry.call(this);
+ this.setCoordinates(coordinates, opt_layout);
+};
+ol.inherits(ol.geom.MultiPoint, ol.geom.SimpleGeometry);
+
+
+/**
+ * Append the passed point to this multipoint.
+ * @param {ol.geom.Point} point Point.
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.appendPoint = function(point) {
+ ol.DEBUG && console.assert(point.getLayout() == this.layout,
+ 'the layout of point should match layout');
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = point.getFlatCoordinates().slice();
+ } else {
+ ol.array.extend(this.flatCoordinates, point.getFlatCoordinates());
+ }
+ this.changed();
+};
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.MultiPoint} Clone.
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.clone = function() {
+ var multiPoint = new ol.geom.MultiPoint(null);
+ multiPoint.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
+ return multiPoint;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.MultiPoint.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ var flatCoordinates = this.flatCoordinates;
+ var stride = this.stride;
+ var i, ii, j;
+ for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
+ var squaredDistance = ol.math.squaredDistance(
+ x, y, flatCoordinates[i], flatCoordinates[i + 1]);
+ if (squaredDistance < minSquaredDistance) {
+ minSquaredDistance = squaredDistance;
+ for (j = 0; j < stride; ++j) {
+ closestPoint[j] = flatCoordinates[i + j];
+ }
+ closestPoint.length = stride;
+ }
+ }
+ return minSquaredDistance;
+};
+
+
+/**
+ * Return the coordinates of the multipoint.
+ * @return {Array.<ol.Coordinate>} Coordinates.
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.getCoordinates = function() {
+ return ol.geom.flat.inflate.coordinates(
+ this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
+};
+
+
+/**
+ * Return the point at the specified index.
+ * @param {number} index Index.
+ * @return {ol.geom.Point} Point.
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.getPoint = function(index) {
+ var n = !this.flatCoordinates ?
+ 0 : this.flatCoordinates.length / this.stride;
+ ol.DEBUG && console.assert(0 <= index && index < n,
+ 'index should be in between 0 and n');
+ if (index < 0 || n <= index) {
+ return null;
+ }
+ var point = new ol.geom.Point(null);
+ point.setFlatCoordinates(this.layout, this.flatCoordinates.slice(
+ index * this.stride, (index + 1) * this.stride));
+ return point;
+};
+
+
+/**
+ * Return the points of this multipoint.
+ * @return {Array.<ol.geom.Point>} Points.
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.getPoints = function() {
+ var flatCoordinates = this.flatCoordinates;
+ var layout = this.layout;
+ var stride = this.stride;
+ /** @type {Array.<ol.geom.Point>} */
+ var points = [];
+ var i, ii;
+ for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
+ var point = new ol.geom.Point(null);
+ point.setFlatCoordinates(layout, flatCoordinates.slice(i, i + stride));
+ points.push(point);
+ }
+ return points;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.getType = function() {
+ return ol.geom.GeometryType.MULTI_POINT;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.intersectsExtent = function(extent) {
+ var flatCoordinates = this.flatCoordinates;
+ var stride = this.stride;
+ var i, ii, x, y;
+ for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
+ x = flatCoordinates[i];
+ y = flatCoordinates[i + 1];
+ if (ol.extent.containsXY(extent, x, y)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Set the coordinates of the multipoint.
+ * @param {Array.<ol.Coordinate>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.MultiPoint.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
+ } else {
+ this.setLayout(opt_layout, coordinates, 1);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ this.flatCoordinates.length = ol.geom.flat.deflate.coordinates(
+ this.flatCoordinates, 0, coordinates, this.stride);
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ */
+ol.geom.MultiPoint.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.changed();
+};
+
+goog.provide('ol.geom.flat.center');
+
+goog.require('ol.extent');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {Array.<Array.<number>>} endss Endss.
+ * @param {number} stride Stride.
+ * @return {Array.<number>} Flat centers.
+ */
+ol.geom.flat.center.linearRingss = function(flatCoordinates, offset, endss, stride) {
+ var flatCenters = [];
+ var i, ii;
+ var extent = ol.extent.createEmpty();
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i];
+ extent = ol.extent.createOrUpdateFromFlatCoordinates(
+ flatCoordinates, offset, ends[0], stride);
+ flatCenters.push((extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2);
+ offset = ends[ends.length - 1];
+ }
+ return flatCenters;
+};
+
+goog.provide('ol.geom.MultiPolygon');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.Polygon');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.area');
+goog.require('ol.geom.flat.center');
+goog.require('ol.geom.flat.closest');
+goog.require('ol.geom.flat.contains');
+goog.require('ol.geom.flat.deflate');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.geom.flat.interiorpoint');
+goog.require('ol.geom.flat.intersectsextent');
+goog.require('ol.geom.flat.orient');
+goog.require('ol.geom.flat.simplify');
+
+
+/**
+ * @classdesc
+ * Multi-polygon geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.MultiPolygon = function(coordinates, opt_layout) {
+
+ ol.geom.SimpleGeometry.call(this);
+
+ /**
+ * @type {Array.<Array.<number>>}
+ * @private
+ */
+ this.endss_ = [];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.flatInteriorPointsRevision_ = -1;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.flatInteriorPoints_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDelta_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxDeltaRevision_ = -1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.orientedRevision_ = -1;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.orientedFlatCoordinates_ = null;
+
+ this.setCoordinates(coordinates, opt_layout);
+
+};
+ol.inherits(ol.geom.MultiPolygon, ol.geom.SimpleGeometry);
+
+
+/**
+ * Append the passed polygon to this multipolygon.
+ * @param {ol.geom.Polygon} polygon Polygon.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.appendPolygon = function(polygon) {
+ ol.DEBUG && console.assert(polygon.getLayout() == this.layout,
+ 'layout of polygon should match layout');
+ /** @type {Array.<number>} */
+ var ends;
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = polygon.getFlatCoordinates().slice();
+ ends = polygon.getEnds().slice();
+ this.endss_.push();
+ } else {
+ var offset = this.flatCoordinates.length;
+ ol.array.extend(this.flatCoordinates, polygon.getFlatCoordinates());
+ ends = polygon.getEnds().slice();
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ ends[i] += offset;
+ }
+ }
+ this.endss_.push(ends);
+ this.changed();
+};
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.MultiPolygon} Clone.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.clone = function() {
+ var multiPolygon = new ol.geom.MultiPolygon(null);
+
+ var len = this.endss_.length;
+ var newEndss = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ newEndss[i] = this.endss_[i].slice();
+ }
+
+ multiPolygon.setFlatCoordinates(
+ this.layout, this.flatCoordinates.slice(), newEndss);
+ return multiPolygon;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.MultiPolygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ if (this.maxDeltaRevision_ != this.getRevision()) {
+ this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getssMaxSquaredDelta(
+ this.flatCoordinates, 0, this.endss_, this.stride, 0));
+ this.maxDeltaRevision_ = this.getRevision();
+ }
+ return ol.geom.flat.closest.getssClosestPoint(
+ this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
+ this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.MultiPolygon.prototype.containsXY = function(x, y) {
+ return ol.geom.flat.contains.linearRingssContainsXY(
+ this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y);
+};
+
+
+/**
+ * Return the area of the multipolygon on projected plane.
+ * @return {number} Area (on projected plane).
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.getArea = function() {
+ return ol.geom.flat.area.linearRingss(
+ this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride);
+};
+
+
+/**
+ * Get the coordinate array for this geometry. This array has the structure
+ * of a GeoJSON coordinate array for multi-polygons.
+ *
+ * @param {boolean=} opt_right Orient coordinates according to the right-hand
+ * rule (counter-clockwise for exterior and clockwise for interior rings).
+ * If `false`, coordinates will be oriented according to the left-hand rule
+ * (clockwise for exterior and counter-clockwise for interior rings).
+ * By default, coordinate orientation will depend on how the geometry was
+ * constructed.
+ * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinates.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.getCoordinates = function(opt_right) {
+ var flatCoordinates;
+ if (opt_right !== undefined) {
+ flatCoordinates = this.getOrientedFlatCoordinates().slice();
+ ol.geom.flat.orient.orientLinearRingss(
+ flatCoordinates, 0, this.endss_, this.stride, opt_right);
+ } else {
+ flatCoordinates = this.flatCoordinates;
+ }
+
+ return ol.geom.flat.inflate.coordinatesss(
+ flatCoordinates, 0, this.endss_, this.stride);
+};
+
+
+/**
+ * @return {Array.<Array.<number>>} Endss.
+ */
+ol.geom.MultiPolygon.prototype.getEndss = function() {
+ return this.endss_;
+};
+
+
+/**
+ * @return {Array.<number>} Flat interior points.
+ */
+ol.geom.MultiPolygon.prototype.getFlatInteriorPoints = function() {
+ if (this.flatInteriorPointsRevision_ != this.getRevision()) {
+ var flatCenters = ol.geom.flat.center.linearRingss(
+ this.flatCoordinates, 0, this.endss_, this.stride);
+ this.flatInteriorPoints_ = ol.geom.flat.interiorpoint.linearRingss(
+ this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
+ flatCenters);
+ this.flatInteriorPointsRevision_ = this.getRevision();
+ }
+ return this.flatInteriorPoints_;
+};
+
+
+/**
+ * Return the interior points as {@link ol.geom.MultiPoint multipoint}.
+ * @return {ol.geom.MultiPoint} Interior points.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.getInteriorPoints = function() {
+ var interiorPoints = new ol.geom.MultiPoint(null);
+ interiorPoints.setFlatCoordinates(ol.geom.GeometryLayout.XY,
+ this.getFlatInteriorPoints().slice());
+ return interiorPoints;
+};
+
+
+/**
+ * @return {Array.<number>} Oriented flat coordinates.
+ */
+ol.geom.MultiPolygon.prototype.getOrientedFlatCoordinates = function() {
+ if (this.orientedRevision_ != this.getRevision()) {
+ var flatCoordinates = this.flatCoordinates;
+ if (ol.geom.flat.orient.linearRingssAreOriented(
+ flatCoordinates, 0, this.endss_, this.stride)) {
+ this.orientedFlatCoordinates_ = flatCoordinates;
+ } else {
+ this.orientedFlatCoordinates_ = flatCoordinates.slice();
+ this.orientedFlatCoordinates_.length =
+ ol.geom.flat.orient.orientLinearRingss(
+ this.orientedFlatCoordinates_, 0, this.endss_, this.stride);
+ }
+ this.orientedRevision_ = this.getRevision();
+ }
+ return this.orientedFlatCoordinates_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.MultiPolygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
+ var simplifiedFlatCoordinates = [];
+ var simplifiedEndss = [];
+ simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizess(
+ this.flatCoordinates, 0, this.endss_, this.stride,
+ Math.sqrt(squaredTolerance),
+ simplifiedFlatCoordinates, 0, simplifiedEndss);
+ var simplifiedMultiPolygon = new ol.geom.MultiPolygon(null);
+ simplifiedMultiPolygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEndss);
+ return simplifiedMultiPolygon;
+};
+
+
+/**
+ * Return the polygon at the specified index.
+ * @param {number} index Index.
+ * @return {ol.geom.Polygon} Polygon.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.getPolygon = function(index) {
+ ol.DEBUG && console.assert(0 <= index && index < this.endss_.length,
+ 'index should be in between 0 and the length of this.endss_');
+ if (index < 0 || this.endss_.length <= index) {
+ return null;
+ }
+ var offset;
+ if (index === 0) {
+ offset = 0;
+ } else {
+ var prevEnds = this.endss_[index - 1];
+ offset = prevEnds[prevEnds.length - 1];
+ }
+ var ends = this.endss_[index].slice();
+ var end = ends[ends.length - 1];
+ if (offset !== 0) {
+ var i, ii;
+ for (i = 0, ii = ends.length; i < ii; ++i) {
+ ends[i] -= offset;
+ }
+ }
+ var polygon = new ol.geom.Polygon(null);
+ polygon.setFlatCoordinates(
+ this.layout, this.flatCoordinates.slice(offset, end), ends);
+ return polygon;
+};
+
+
+/**
+ * Return the polygons of this multipolygon.
+ * @return {Array.<ol.geom.Polygon>} Polygons.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.getPolygons = function() {
+ var layout = this.layout;
+ var flatCoordinates = this.flatCoordinates;
+ var endss = this.endss_;
+ var polygons = [];
+ var offset = 0;
+ var i, ii, j, jj;
+ for (i = 0, ii = endss.length; i < ii; ++i) {
+ var ends = endss[i].slice();
+ var end = ends[ends.length - 1];
+ if (offset !== 0) {
+ for (j = 0, jj = ends.length; j < jj; ++j) {
+ ends[j] -= offset;
+ }
+ }
+ var polygon = new ol.geom.Polygon(null);
+ polygon.setFlatCoordinates(
+ layout, flatCoordinates.slice(offset, end), ends);
+ polygons.push(polygon);
+ offset = end;
+ }
+ return polygons;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.getType = function() {
+ return ol.geom.GeometryType.MULTI_POLYGON;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.intersectsExtent = function(extent) {
+ return ol.geom.flat.intersectsextent.linearRingss(
+ this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent);
+};
+
+
+/**
+ * Set the coordinates of the multipolygon.
+ * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api stable
+ */
+ol.geom.MultiPolygon.prototype.setCoordinates = function(coordinates, opt_layout) {
+ if (!coordinates) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.endss_);
+ } else {
+ this.setLayout(opt_layout, coordinates, 3);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ var endss = ol.geom.flat.deflate.coordinatesss(
+ this.flatCoordinates, 0, coordinates, this.stride, this.endss_);
+ if (endss.length === 0) {
+ this.flatCoordinates.length = 0;
+ } else {
+ var lastEnds = endss[endss.length - 1];
+ this.flatCoordinates.length = lastEnds.length === 0 ?
+ 0 : lastEnds[lastEnds.length - 1];
+ }
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {Array.<Array.<number>>} endss Endss.
+ */
+ol.geom.MultiPolygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, endss) {
+ ol.DEBUG && console.assert(endss, 'endss must be truthy');
+ if (!flatCoordinates || flatCoordinates.length === 0) {
+ ol.DEBUG && console.assert(endss.length === 0, 'the length of endss should be 0');
+ } else {
+ ol.DEBUG && console.assert(endss.length > 0, 'endss cannot be an empty array');
+ var ends = endss[endss.length - 1];
+ ol.DEBUG && console.assert(flatCoordinates.length == ends[ends.length - 1],
+ 'the length of flatCoordinates should be the last value of ends');
+ }
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.endss_ = endss;
+ this.changed();
+};
+
+
+/**
+ * @param {Array.<ol.geom.Polygon>} polygons Polygons.
+ */
+ol.geom.MultiPolygon.prototype.setPolygons = function(polygons) {
+ var layout = this.getLayout();
+ var flatCoordinates = [];
+ var endss = [];
+ var i, ii, ends;
+ for (i = 0, ii = polygons.length; i < ii; ++i) {
+ var polygon = polygons[i];
+ if (i === 0) {
+ layout = polygon.getLayout();
+ } else {
+ // FIXME better handle the case of non-matching layouts
+ ol.DEBUG && console.assert(polygon.getLayout() == layout,
+ 'layout of polygon should be layout');
+ }
+ var offset = flatCoordinates.length;
+ ends = polygon.getEnds();
+ var j, jj;
+ for (j = 0, jj = ends.length; j < jj; ++j) {
+ ends[j] += offset;
+ }
+ ol.array.extend(flatCoordinates, polygon.getFlatCoordinates());
+ endss.push(ends);
+ }
+ this.setFlatCoordinates(layout, flatCoordinates, endss);
+};
+
+goog.provide('ol.format.EsriJSON');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.array');
+goog.require('ol.asserts');
+goog.require('ol.extent');
+goog.require('ol.format.Feature');
+goog.require('ol.format.JSONFeature');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.LinearRing');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.geom.flat.orient');
+goog.require('ol.obj');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the EsriJSON format.
+ *
+ * @constructor
+ * @extends {ol.format.JSONFeature}
+ * @param {olx.format.EsriJSONOptions=} opt_options Options.
+ * @api
+ */
+ol.format.EsriJSON = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.JSONFeature.call(this);
+
+ /**
+ * Name of the geometry attribute for features.
+ * @type {string|undefined}
+ * @private
+ */
+ this.geometryName_ = options.geometryName;
+
+};
+ol.inherits(ol.format.EsriJSON, ol.format.JSONFeature);
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @private
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.EsriJSON.readGeometry_ = function(object, opt_options) {
+ if (!object) {
+ return null;
+ }
+ /** @type {ol.geom.GeometryType} */
+ var type;
+ if (typeof object.x === 'number' && typeof object.y === 'number') {
+ type = ol.geom.GeometryType.POINT;
+ } else if (object.points) {
+ type = ol.geom.GeometryType.MULTI_POINT;
+ } else if (object.paths) {
+ if (object.paths.length === 1) {
+ type = ol.geom.GeometryType.LINE_STRING;
+ } else {
+ type = ol.geom.GeometryType.MULTI_LINE_STRING;
+ }
+ } else if (object.rings) {
+ var layout = ol.format.EsriJSON.getGeometryLayout_(object);
+ var rings = ol.format.EsriJSON.convertRings_(object.rings, layout);
+ object = /** @type {EsriJSONGeometry} */(ol.obj.assign({}, object));
+ if (rings.length === 1) {
+ type = ol.geom.GeometryType.POLYGON;
+ object.rings = rings[0];
+ } else {
+ type = ol.geom.GeometryType.MULTI_POLYGON;
+ object.rings = rings;
+ }
+ }
+ var geometryReader = ol.format.EsriJSON.GEOMETRY_READERS_[type];
+ return /** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(
+ geometryReader(object), false, opt_options));
+};
+
+
+/**
+ * Determines inner and outer rings.
+ * Checks if any polygons in this array contain any other polygons in this
+ * array. It is used for checking for holes.
+ * Logic inspired by: https://github.com/Esri/terraformer-arcgis-parser
+ * @param {Array.<!Array.<!Array.<number>>>} rings Rings.
+ * @param {ol.geom.GeometryLayout} layout Geometry layout.
+ * @private
+ * @return {Array.<!Array.<!Array.<number>>>} Transoformed rings.
+ */
+ol.format.EsriJSON.convertRings_ = function(rings, layout) {
+ var outerRings = [];
+ var holes = [];
+ var i, ii;
+ for (i = 0, ii = rings.length; i < ii; ++i) {
+ var flatRing = ol.array.flatten(rings[i]);
+ // is this ring an outer ring? is it clockwise?
+ var clockwise = ol.geom.flat.orient.linearRingIsClockwise(flatRing, 0,
+ flatRing.length, layout.length);
+ if (clockwise) {
+ outerRings.push([rings[i]]);
+ } else {
+ holes.push(rings[i]);
+ }
+ }
+ while (holes.length) {
+ var hole = holes.shift();
+ var matched = false;
+ // loop over all outer rings and see if they contain our hole.
+ for (i = outerRings.length - 1; i >= 0; i--) {
+ var outerRing = outerRings[i][0];
+ if (ol.extent.containsExtent(new ol.geom.LinearRing(
+ outerRing).getExtent(),
+ new ol.geom.LinearRing(hole).getExtent())) {
+ // the hole is contained push it into our polygon
+ outerRings[i].push(hole);
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ // no outer rings contain this hole turn it into and outer
+ // ring (reverse it)
+ outerRings.push([hole.reverse()]);
+ }
+ }
+ return outerRings;
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Geometry} Point.
+ */
+ol.format.EsriJSON.readPointGeometry_ = function(object) {
+ ol.DEBUG && console.assert(typeof object.x === 'number', 'object.x should be number');
+ ol.DEBUG && console.assert(typeof object.y === 'number', 'object.y should be number');
+ var point;
+ if (object.m !== undefined && object.z !== undefined) {
+ point = new ol.geom.Point([object.x, object.y, object.z, object.m],
+ ol.geom.GeometryLayout.XYZM);
+ } else if (object.z !== undefined) {
+ point = new ol.geom.Point([object.x, object.y, object.z],
+ ol.geom.GeometryLayout.XYZ);
+ } else if (object.m !== undefined) {
+ point = new ol.geom.Point([object.x, object.y, object.m],
+ ol.geom.GeometryLayout.XYM);
+ } else {
+ point = new ol.geom.Point([object.x, object.y]);
+ }
+ return point;
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Geometry} LineString.
+ */
+ol.format.EsriJSON.readLineStringGeometry_ = function(object) {
+ ol.DEBUG && console.assert(Array.isArray(object.paths),
+ 'object.paths should be an array');
+ ol.DEBUG && console.assert(object.paths.length === 1,
+ 'object.paths array length should be 1');
+ var layout = ol.format.EsriJSON.getGeometryLayout_(object);
+ return new ol.geom.LineString(object.paths[0], layout);
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Geometry} MultiLineString.
+ */
+ol.format.EsriJSON.readMultiLineStringGeometry_ = function(object) {
+ ol.DEBUG && console.assert(Array.isArray(object.paths),
+ 'object.paths should be an array');
+ ol.DEBUG && console.assert(object.paths.length > 1,
+ 'object.paths array length should be more than 1');
+ var layout = ol.format.EsriJSON.getGeometryLayout_(object);
+ return new ol.geom.MultiLineString(object.paths, layout);
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.GeometryLayout} The geometry layout to use.
+ */
+ol.format.EsriJSON.getGeometryLayout_ = function(object) {
+ var layout = ol.geom.GeometryLayout.XY;
+ if (object.hasZ === true && object.hasM === true) {
+ layout = ol.geom.GeometryLayout.XYZM;
+ } else if (object.hasZ === true) {
+ layout = ol.geom.GeometryLayout.XYZ;
+ } else if (object.hasM === true) {
+ layout = ol.geom.GeometryLayout.XYM;
+ }
+ return layout;
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Geometry} MultiPoint.
+ */
+ol.format.EsriJSON.readMultiPointGeometry_ = function(object) {
+ var layout = ol.format.EsriJSON.getGeometryLayout_(object);
+ return new ol.geom.MultiPoint(object.points, layout);
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Geometry} MultiPolygon.
+ */
+ol.format.EsriJSON.readMultiPolygonGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.rings.length > 1,
+ 'object.rings should have length larger than 1');
+ var layout = ol.format.EsriJSON.getGeometryLayout_(object);
+ return new ol.geom.MultiPolygon(
+ /** @type {Array.<Array.<Array.<Array.<number>>>>} */(object.rings),
+ layout);
+};
+
+
+/**
+ * @param {EsriJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Geometry} Polygon.
+ */
+ol.format.EsriJSON.readPolygonGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.rings);
+ var layout = ol.format.EsriJSON.getGeometryLayout_(object);
+ return new ol.geom.Polygon(object.rings, layout);
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONGeometry} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writePointGeometry_ = function(geometry, opt_options) {
+ var coordinates = /** @type {ol.geom.Point} */ (geometry).getCoordinates();
+ var esriJSON;
+ var layout = /** @type {ol.geom.Point} */ (geometry).getLayout();
+ if (layout === ol.geom.GeometryLayout.XYZ) {
+ esriJSON = /** @type {EsriJSONPoint} */ ({
+ x: coordinates[0],
+ y: coordinates[1],
+ z: coordinates[2]
+ });
+ } else if (layout === ol.geom.GeometryLayout.XYM) {
+ esriJSON = /** @type {EsriJSONPoint} */ ({
+ x: coordinates[0],
+ y: coordinates[1],
+ m: coordinates[2]
+ });
+ } else if (layout === ol.geom.GeometryLayout.XYZM) {
+ esriJSON = /** @type {EsriJSONPoint} */ ({
+ x: coordinates[0],
+ y: coordinates[1],
+ z: coordinates[2],
+ m: coordinates[3]
+ });
+ } else if (layout === ol.geom.GeometryLayout.XY) {
+ esriJSON = /** @type {EsriJSONPoint} */ ({
+ x: coordinates[0],
+ y: coordinates[1]
+ });
+ } else {
+ ol.asserts.assert(false, 34); // Invalid geometry layout
+ }
+ return /** @type {EsriJSONGeometry} */ (esriJSON);
+};
+
+
+/**
+ * @param {ol.geom.SimpleGeometry} geometry Geometry.
+ * @private
+ * @return {Object} Object with boolean hasZ and hasM keys.
+ */
+ol.format.EsriJSON.getHasZM_ = function(geometry) {
+ var layout = geometry.getLayout();
+ return {
+ hasZ: (layout === ol.geom.GeometryLayout.XYZ ||
+ layout === ol.geom.GeometryLayout.XYZM),
+ hasM: (layout === ol.geom.GeometryLayout.XYM ||
+ layout === ol.geom.GeometryLayout.XYZM)
+ };
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONPolyline} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writeLineStringGeometry_ = function(geometry, opt_options) {
+ var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.LineString} */ (geometry));
+ return /** @type {EsriJSONPolyline} */ ({
+ hasZ: hasZM.hasZ,
+ hasM: hasZM.hasM,
+ paths: [
+ /** @type {ol.geom.LineString} */ (geometry).getCoordinates()
+ ]
+ });
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONPolygon} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writePolygonGeometry_ = function(geometry, opt_options) {
+ // Esri geometries use the left-hand rule
+ var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.Polygon} */ (geometry));
+ return /** @type {EsriJSONPolygon} */ ({
+ hasZ: hasZM.hasZ,
+ hasM: hasZM.hasM,
+ rings: /** @type {ol.geom.Polygon} */ (geometry).getCoordinates(false)
+ });
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONPolyline} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writeMultiLineStringGeometry_ = function(geometry, opt_options) {
+ var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.MultiLineString} */ (geometry));
+ return /** @type {EsriJSONPolyline} */ ({
+ hasZ: hasZM.hasZ,
+ hasM: hasZM.hasM,
+ paths: /** @type {ol.geom.MultiLineString} */ (geometry).getCoordinates()
+ });
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONMultipoint} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writeMultiPointGeometry_ = function(geometry, opt_options) {
+ var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.MultiPoint} */ (geometry));
+ return /** @type {EsriJSONMultipoint} */ ({
+ hasZ: hasZM.hasZ,
+ hasM: hasZM.hasM,
+ points: /** @type {ol.geom.MultiPoint} */ (geometry).getCoordinates()
+ });
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONPolygon} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writeMultiPolygonGeometry_ = function(geometry,
+ opt_options) {
+ var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.MultiPolygon} */ (geometry));
+ var coordinates = /** @type {ol.geom.MultiPolygon} */ (geometry).getCoordinates(false);
+ var output = [];
+ for (var i = 0; i < coordinates.length; i++) {
+ for (var x = coordinates[i].length - 1; x >= 0; x--) {
+ output.push(coordinates[i][x]);
+ }
+ }
+ return /** @type {EsriJSONPolygon} */ ({
+ hasZ: hasZM.hasZ,
+ hasM: hasZM.hasM,
+ rings: output
+ });
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<ol.geom.GeometryType, function(EsriJSONGeometry): ol.geom.Geometry>}
+ */
+ol.format.EsriJSON.GEOMETRY_READERS_ = {};
+ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.POINT] =
+ ol.format.EsriJSON.readPointGeometry_;
+ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.LINE_STRING] =
+ ol.format.EsriJSON.readLineStringGeometry_;
+ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.POLYGON] =
+ ol.format.EsriJSON.readPolygonGeometry_;
+ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_POINT] =
+ ol.format.EsriJSON.readMultiPointGeometry_;
+ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_LINE_STRING] =
+ ol.format.EsriJSON.readMultiLineStringGeometry_;
+ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_POLYGON] =
+ ol.format.EsriJSON.readMultiPolygonGeometry_;
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (EsriJSONGeometry)>}
+ */
+ol.format.EsriJSON.GEOMETRY_WRITERS_ = {};
+ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.POINT] =
+ ol.format.EsriJSON.writePointGeometry_;
+ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.LINE_STRING] =
+ ol.format.EsriJSON.writeLineStringGeometry_;
+ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.POLYGON] =
+ ol.format.EsriJSON.writePolygonGeometry_;
+ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_POINT] =
+ ol.format.EsriJSON.writeMultiPointGeometry_;
+ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_LINE_STRING] =
+ ol.format.EsriJSON.writeMultiLineStringGeometry_;
+ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_POLYGON] =
+ ol.format.EsriJSON.writeMultiPolygonGeometry_;
+
+
+/**
+ * Read a feature from a EsriJSON Feature source. Only works for Feature,
+ * use `readFeatures` to read FeatureCollection source.
+ *
+ * @function
+ * @param {ArrayBuffer|Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api
+ */
+ol.format.EsriJSON.prototype.readFeature;
+
+
+/**
+ * Read all features from a EsriJSON source. Works with both Feature and
+ * FeatureCollection sources.
+ *
+ * @function
+ * @param {ArrayBuffer|Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api
+ */
+ol.format.EsriJSON.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.EsriJSON.prototype.readFeatureFromObject = function(
+ object, opt_options) {
+ var esriJSONFeature = /** @type {EsriJSONFeature} */ (object);
+ ol.DEBUG && console.assert(esriJSONFeature.geometry ||
+ esriJSONFeature.attributes,
+ 'geometry or attributes should be defined');
+ var geometry = ol.format.EsriJSON.readGeometry_(esriJSONFeature.geometry,
+ opt_options);
+ var feature = new ol.Feature();
+ if (this.geometryName_) {
+ feature.setGeometryName(this.geometryName_);
+ }
+ feature.setGeometry(geometry);
+ if (opt_options && opt_options.idField &&
+ esriJSONFeature.attributes[opt_options.idField]) {
+ ol.DEBUG && console.assert(
+ typeof esriJSONFeature.attributes[opt_options.idField] === 'number',
+ 'objectIdFieldName value should be a number');
+ feature.setId(/** @type {number} */(
+ esriJSONFeature.attributes[opt_options.idField]));
+ }
+ if (esriJSONFeature.attributes) {
+ feature.setProperties(esriJSONFeature.attributes);
+ }
+ return feature;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.EsriJSON.prototype.readFeaturesFromObject = function(
+ object, opt_options) {
+ var esriJSONObject = /** @type {EsriJSONObject} */ (object);
+ var options = opt_options ? opt_options : {};
+ if (esriJSONObject.features) {
+ var esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */
+ (object);
+ /** @type {Array.<ol.Feature>} */
+ var features = [];
+ var esriJSONFeatures = esriJSONFeatureCollection.features;
+ var i, ii;
+ options.idField = object.objectIdFieldName;
+ for (i = 0, ii = esriJSONFeatures.length; i < ii; ++i) {
+ features.push(this.readFeatureFromObject(esriJSONFeatures[i],
+ options));
+ }
+ return features;
+ } else {
+ return [this.readFeatureFromObject(object, options)];
+ }
+};
+
+
+/**
+ * Read a geometry from a EsriJSON source.
+ *
+ * @function
+ * @param {ArrayBuffer|Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.geom.Geometry} Geometry.
+ * @api
+ */
+ol.format.EsriJSON.prototype.readGeometry;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.EsriJSON.prototype.readGeometryFromObject = function(
+ object, opt_options) {
+ return ol.format.EsriJSON.readGeometry_(
+ /** @type {EsriJSONGeometry} */ (object), opt_options);
+};
+
+
+/**
+ * Read the projection from a EsriJSON source.
+ *
+ * @function
+ * @param {ArrayBuffer|Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api
+ */
+ol.format.EsriJSON.prototype.readProjection;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.EsriJSON.prototype.readProjectionFromObject = function(object) {
+ var esriJSONObject = /** @type {EsriJSONObject} */ (object);
+ if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) {
+ var crs = esriJSONObject.spatialReference.wkid;
+ return ol.proj.get('EPSG:' + crs);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {EsriJSONGeometry} EsriJSON geometry.
+ */
+ol.format.EsriJSON.writeGeometry_ = function(geometry, opt_options) {
+ var geometryWriter = ol.format.EsriJSON.GEOMETRY_WRITERS_[geometry.getType()];
+ return geometryWriter(/** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(geometry, true, opt_options)),
+ opt_options);
+};
+
+
+/**
+ * Encode a geometry as a EsriJSON string.
+ *
+ * @function
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} EsriJSON.
+ * @api
+ */
+ol.format.EsriJSON.prototype.writeGeometry;
+
+
+/**
+ * Encode a geometry as a EsriJSON object.
+ *
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {EsriJSONGeometry} Object.
+ * @api
+ */
+ol.format.EsriJSON.prototype.writeGeometryObject = function(geometry,
+ opt_options) {
+ return ol.format.EsriJSON.writeGeometry_(geometry,
+ this.adaptOptions(opt_options));
+};
+
+
+/**
+ * Encode a feature as a EsriJSON Feature string.
+ *
+ * @function
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} EsriJSON.
+ * @api
+ */
+ol.format.EsriJSON.prototype.writeFeature;
+
+
+/**
+ * Encode a feature as a esriJSON Feature object.
+ *
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {Object} Object.
+ * @api
+ */
+ol.format.EsriJSON.prototype.writeFeatureObject = function(
+ feature, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ var object = {};
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ object['geometry'] =
+ ol.format.EsriJSON.writeGeometry_(geometry, opt_options);
+ }
+ var properties = feature.getProperties();
+ delete properties[feature.getGeometryName()];
+ if (!ol.obj.isEmpty(properties)) {
+ object['attributes'] = properties;
+ } else {
+ object['attributes'] = {};
+ }
+ if (opt_options && opt_options.featureProjection) {
+ object['spatialReference'] = /** @type {EsriJSONCRS} */({
+ wkid: ol.proj.get(
+ opt_options.featureProjection).getCode().split(':').pop()
+ });
+ }
+ return object;
+};
+
+
+/**
+ * Encode an array of features as EsriJSON.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} EsriJSON.
+ * @api
+ */
+ol.format.EsriJSON.prototype.writeFeatures;
+
+
+/**
+ * Encode an array of features as a EsriJSON object.
+ *
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {Object} EsriJSON Object.
+ * @api
+ */
+ol.format.EsriJSON.prototype.writeFeaturesObject = function(features, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ var objects = [];
+ var i, ii;
+ for (i = 0, ii = features.length; i < ii; ++i) {
+ objects.push(this.writeFeatureObject(features[i], opt_options));
+ }
+ return /** @type {EsriJSONFeatureCollection} */ ({
+ 'features': objects
+ });
+};
+
+goog.provide('ol.format.filter.Filter');
+
+goog.require('ol');
+
+
+/**
+ * @classdesc
+ * Abstract class; normally only used for creating subclasses and not instantiated in apps.
+ * Base class for WFS GetFeature filters.
+ *
+ * @constructor
+ * @param {!string} tagName The XML tag name for this filter.
+ * @struct
+ * @api
+ */
+ol.format.filter.Filter = function(tagName) {
+
+ /**
+ * @private
+ * @type {!string}
+ */
+ this.tagName_ = tagName;
+};
+
+/**
+ * The XML tag name for a filter.
+ * @returns {!string} Name.
+ */
+ol.format.filter.Filter.prototype.getTagName = function() {
+ return this.tagName_;
+};
+
+goog.provide('ol.format.filter.Logical');
+
+goog.require('ol');
+goog.require('ol.format.filter.Filter');
+
+
+/**
+ * @classdesc
+ * Abstract class; normally only used for creating subclasses and not instantiated in apps.
+ * Base class for WFS GetFeature logical filters.
+ *
+ * @constructor
+ * @param {!string} tagName The XML tag name for this filter.
+ * @extends {ol.format.filter.Filter}
+ */
+ol.format.filter.Logical = function(tagName) {
+ ol.format.filter.Filter.call(this, tagName);
+};
+ol.inherits(ol.format.filter.Logical, ol.format.filter.Filter);
+
+goog.provide('ol.format.filter.LogicalBinary');
+
+goog.require('ol');
+goog.require('ol.format.filter.Logical');
+
+
+/**
+ * @classdesc
+ * Abstract class; normally only used for creating subclasses and not instantiated in apps.
+ * Base class for WFS GetFeature binary logical filters.
+ *
+ * @constructor
+ * @param {!string} tagName The XML tag name for this filter.
+ * @param {!ol.format.filter.Filter} conditionA First filter condition.
+ * @param {!ol.format.filter.Filter} conditionB Second filter condition.
+ * @extends {ol.format.filter.Logical}
+ */
+ol.format.filter.LogicalBinary = function(tagName, conditionA, conditionB) {
+
+ ol.format.filter.Logical.call(this, tagName);
+
+ /**
+ * @public
+ * @type {!ol.format.filter.Filter}
+ */
+ this.conditionA = conditionA;
+
+ /**
+ * @public
+ * @type {!ol.format.filter.Filter}
+ */
+ this.conditionB = conditionB;
+
+};
+ol.inherits(ol.format.filter.LogicalBinary, ol.format.filter.Logical);
+
+goog.provide('ol.format.filter.And');
+
+goog.require('ol');
+goog.require('ol.format.filter.LogicalBinary');
+
+/**
+ * @classdesc
+ * Represents a logical `<And>` operator between two filter conditions.
+ *
+ * @constructor
+ * @param {!ol.format.filter.Filter} conditionA First filter condition.
+ * @param {!ol.format.filter.Filter} conditionB Second filter condition.
+ * @extends {ol.format.filter.LogicalBinary}
+ * @api
+ */
+ol.format.filter.And = function(conditionA, conditionB) {
+ ol.format.filter.LogicalBinary.call(this, 'And', conditionA, conditionB);
+};
+ol.inherits(ol.format.filter.And, ol.format.filter.LogicalBinary);
+
+goog.provide('ol.format.filter.Bbox');
+
+goog.require('ol');
+goog.require('ol.format.filter.Filter');
+
+
+/**
+ * @classdesc
+ * Represents a `<BBOX>` operator to test whether a geometry-valued property
+ * intersects a fixed bounding box
+ *
+ * @constructor
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.Extent} extent Extent.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @extends {ol.format.filter.Filter}
+ * @api
+ */
+ol.format.filter.Bbox = function(geometryName, extent, opt_srsName) {
+
+ ol.format.filter.Filter.call(this, 'BBOX');
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.geometryName = geometryName;
+
+ /**
+ * @public
+ * @type {ol.Extent}
+ */
+ this.extent = extent;
+
+ /**
+ * @public
+ * @type {string|undefined}
+ */
+ this.srsName = opt_srsName;
+};
+ol.inherits(ol.format.filter.Bbox, ol.format.filter.Filter);
+
+goog.provide('ol.format.filter.Comparison');
+
+goog.require('ol');
+goog.require('ol.format.filter.Filter');
+
+
+/**
+ * @classdesc
+ * Abstract class; normally only used for creating subclasses and not instantiated in apps.
+ * Base class for WFS GetFeature property comparison filters.
+ *
+ * @constructor
+ * @param {!string} tagName The XML tag name for this filter.
+ * @param {!string} propertyName Name of the context property to compare.
+ * @extends {ol.format.filter.Filter}
+ * @api
+ */
+ol.format.filter.Comparison = function(tagName, propertyName) {
+
+ ol.format.filter.Filter.call(this, tagName);
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.propertyName = propertyName;
+};
+ol.inherits(ol.format.filter.Comparison, ol.format.filter.Filter);
+
+goog.provide('ol.format.filter.ComparisonBinary');
+
+goog.require('ol');
+goog.require('ol.format.filter.Comparison');
+
+
+/**
+ * @classdesc
+ * Abstract class; normally only used for creating subclasses and not instantiated in apps.
+ * Base class for WFS GetFeature property binary comparison filters.
+ *
+ * @constructor
+ * @param {!string} tagName The XML tag name for this filter.
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!(string|number)} expression The value to compare.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @extends {ol.format.filter.Comparison}
+ * @api
+ */
+ol.format.filter.ComparisonBinary = function(
+ tagName, propertyName, expression, opt_matchCase) {
+
+ ol.format.filter.Comparison.call(this, tagName, propertyName);
+
+ /**
+ * @public
+ * @type {!(string|number)}
+ */
+ this.expression = expression;
+
+ /**
+ * @public
+ * @type {boolean|undefined}
+ */
+ this.matchCase = opt_matchCase;
+};
+ol.inherits(ol.format.filter.ComparisonBinary, ol.format.filter.Comparison);
+
+goog.provide('ol.format.filter.EqualTo');
+
+goog.require('ol');
+goog.require('ol.format.filter.ComparisonBinary');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsEqualTo>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!(string|number)} expression The value to compare.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @extends {ol.format.filter.ComparisonBinary}
+ * @api
+ */
+ol.format.filter.EqualTo = function(propertyName, expression, opt_matchCase) {
+ ol.format.filter.ComparisonBinary.call(this, 'PropertyIsEqualTo', propertyName, expression, opt_matchCase);
+};
+ol.inherits(ol.format.filter.EqualTo, ol.format.filter.ComparisonBinary);
+
+goog.provide('ol.format.filter.GreaterThan');
+
+goog.require('ol');
+goog.require('ol.format.filter.ComparisonBinary');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsGreaterThan>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @extends {ol.format.filter.ComparisonBinary}
+ * @api
+ */
+ol.format.filter.GreaterThan = function(propertyName, expression) {
+ ol.format.filter.ComparisonBinary.call(this, 'PropertyIsGreaterThan', propertyName, expression);
+};
+ol.inherits(ol.format.filter.GreaterThan, ol.format.filter.ComparisonBinary);
+
+goog.provide('ol.format.filter.GreaterThanOrEqualTo');
+
+goog.require('ol');
+goog.require('ol.format.filter.ComparisonBinary');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsGreaterThanOrEqualTo>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @extends {ol.format.filter.ComparisonBinary}
+ * @api
+ */
+ol.format.filter.GreaterThanOrEqualTo = function(propertyName, expression) {
+ ol.format.filter.ComparisonBinary.call(this, 'PropertyIsGreaterThanOrEqualTo', propertyName, expression);
+};
+ol.inherits(ol.format.filter.GreaterThanOrEqualTo, ol.format.filter.ComparisonBinary);
+
+goog.provide('ol.format.filter.Spatial');
+
+goog.require('ol');
+goog.require('ol.format.filter.Filter');
+
+
+/**
+ * @classdesc
+ * Represents a spatial operator to test whether a geometry-valued property
+ * relates to a given geometry.
+ *
+ * @constructor
+ * @param {!string} tagName The XML tag name for this filter.
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.geom.Geometry} geometry Geometry.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @extends {ol.format.filter.Filter}
+ * @api
+ */
+ol.format.filter.Spatial = function(tagName, geometryName, geometry, opt_srsName) {
+
+ ol.format.filter.Filter.call(this, tagName);
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.geometryName = geometryName || 'the_geom';
+
+ /**
+ * @public
+ * @type {ol.geom.Geometry}
+ */
+ this.geometry = geometry;
+
+ /**
+ * @public
+ * @type {string|undefined}
+ */
+ this.srsName = opt_srsName;
+};
+ol.inherits(ol.format.filter.Spatial, ol.format.filter.Filter);
+
+goog.provide('ol.format.filter.Intersects');
+
+goog.require('ol');
+goog.require('ol.format.filter.Spatial');
+
+
+/**
+ * @classdesc
+ * Represents a `<Intersects>` operator to test whether a geometry-valued property
+ * intersects a given geometry.
+ *
+ * @constructor
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.geom.Geometry} geometry Geometry.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @extends {ol.format.filter.Spatial}
+ * @api
+ */
+ol.format.filter.Intersects = function(geometryName, geometry, opt_srsName) {
+
+ ol.format.filter.Spatial.call(this, 'Intersects', geometryName, geometry, opt_srsName);
+
+};
+ol.inherits(ol.format.filter.Intersects, ol.format.filter.Spatial);
+
+goog.provide('ol.format.filter.IsBetween');
+
+goog.require('ol');
+goog.require('ol.format.filter.Comparison');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsBetween>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} lowerBoundary The lower bound of the range.
+ * @param {!number} upperBoundary The upper bound of the range.
+ * @extends {ol.format.filter.Comparison}
+ * @api
+ */
+ol.format.filter.IsBetween = function(propertyName, lowerBoundary, upperBoundary) {
+ ol.format.filter.Comparison.call(this, 'PropertyIsBetween', propertyName);
+
+ /**
+ * @public
+ * @type {!number}
+ */
+ this.lowerBoundary = lowerBoundary;
+
+ /**
+ * @public
+ * @type {!number}
+ */
+ this.upperBoundary = upperBoundary;
+};
+ol.inherits(ol.format.filter.IsBetween, ol.format.filter.Comparison);
+
+goog.provide('ol.format.filter.IsLike');
+
+goog.require('ol');
+goog.require('ol.format.filter.Comparison');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsLike>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!string} pattern Text pattern.
+ * @param {string=} opt_wildCard Pattern character which matches any sequence of
+ * zero or more string characters. Default is '*'.
+ * @param {string=} opt_singleChar pattern character which matches any single
+ * string character. Default is '.'.
+ * @param {string=} opt_escapeChar Escape character which can be used to escape
+ * the pattern characters. Default is '!'.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @extends {ol.format.filter.Comparison}
+ * @api
+ */
+ol.format.filter.IsLike = function(propertyName, pattern,
+ opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) {
+ ol.format.filter.Comparison.call(this, 'PropertyIsLike', propertyName);
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.pattern = pattern;
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.wildCard = (opt_wildCard !== undefined) ? opt_wildCard : '*';
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.singleChar = (opt_singleChar !== undefined) ? opt_singleChar : '.';
+
+ /**
+ * @public
+ * @type {!string}
+ */
+ this.escapeChar = (opt_escapeChar !== undefined) ? opt_escapeChar : '!';
+
+ /**
+ * @public
+ * @type {boolean|undefined}
+ */
+ this.matchCase = opt_matchCase;
+};
+ol.inherits(ol.format.filter.IsLike, ol.format.filter.Comparison);
+
+goog.provide('ol.format.filter.IsNull');
+
+goog.require('ol');
+goog.require('ol.format.filter.Comparison');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsNull>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @extends {ol.format.filter.Comparison}
+ * @api
+ */
+ol.format.filter.IsNull = function(propertyName) {
+ ol.format.filter.Comparison.call(this, 'PropertyIsNull', propertyName);
+};
+ol.inherits(ol.format.filter.IsNull, ol.format.filter.Comparison);
+
+goog.provide('ol.format.filter.LessThan');
+
+goog.require('ol');
+goog.require('ol.format.filter.ComparisonBinary');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsLessThan>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @extends {ol.format.filter.ComparisonBinary}
+ * @api
+ */
+ol.format.filter.LessThan = function(propertyName, expression) {
+ ol.format.filter.ComparisonBinary.call(this, 'PropertyIsLessThan', propertyName, expression);
+};
+ol.inherits(ol.format.filter.LessThan, ol.format.filter.ComparisonBinary);
+
+goog.provide('ol.format.filter.LessThanOrEqualTo');
+
+goog.require('ol');
+goog.require('ol.format.filter.ComparisonBinary');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsLessThanOrEqualTo>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @extends {ol.format.filter.ComparisonBinary}
+ * @api
+ */
+ol.format.filter.LessThanOrEqualTo = function(propertyName, expression) {
+ ol.format.filter.ComparisonBinary.call(this, 'PropertyIsLessThanOrEqualTo', propertyName, expression);
+};
+ol.inherits(ol.format.filter.LessThanOrEqualTo, ol.format.filter.ComparisonBinary);
+
+goog.provide('ol.format.filter.Not');
+
+goog.require('ol');
+goog.require('ol.format.filter.Logical');
+
+
+/**
+ * @classdesc
+ * Represents a logical `<Not>` operator for a filter condition.
+ *
+ * @constructor
+ * @param {!ol.format.filter.Filter} condition Filter condition.
+ * @extends {ol.format.filter.Logical}
+ * @api
+ */
+ol.format.filter.Not = function(condition) {
+
+ ol.format.filter.Logical.call(this, 'Not');
+
+ /**
+ * @public
+ * @type {!ol.format.filter.Filter}
+ */
+ this.condition = condition;
+};
+ol.inherits(ol.format.filter.Not, ol.format.filter.Logical);
+
+goog.provide('ol.format.filter.NotEqualTo');
+
+goog.require('ol');
+goog.require('ol.format.filter.ComparisonBinary');
+
+
+/**
+ * @classdesc
+ * Represents a `<PropertyIsNotEqualTo>` comparison operator.
+ *
+ * @constructor
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!(string|number)} expression The value to compare.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @extends {ol.format.filter.ComparisonBinary}
+ * @api
+ */
+ol.format.filter.NotEqualTo = function(propertyName, expression, opt_matchCase) {
+ ol.format.filter.ComparisonBinary.call(this, 'PropertyIsNotEqualTo', propertyName, expression, opt_matchCase);
+};
+ol.inherits(ol.format.filter.NotEqualTo, ol.format.filter.ComparisonBinary);
+
+goog.provide('ol.format.filter.Or');
+
+goog.require('ol');
+goog.require('ol.format.filter.LogicalBinary');
+
+
+/**
+ * @classdesc
+ * Represents a logical `<Or>` operator between two filter conditions.
+ *
+ * @constructor
+ * @param {!ol.format.filter.Filter} conditionA First filter condition.
+ * @param {!ol.format.filter.Filter} conditionB Second filter condition.
+ * @extends {ol.format.filter.LogicalBinary}
+ * @api
+ */
+ol.format.filter.Or = function(conditionA, conditionB) {
+ ol.format.filter.LogicalBinary.call(this, 'Or', conditionA, conditionB);
+};
+ol.inherits(ol.format.filter.Or, ol.format.filter.LogicalBinary);
+
+goog.provide('ol.format.filter.Within');
+
+goog.require('ol');
+goog.require('ol.format.filter.Spatial');
+
+
+/**
+ * @classdesc
+ * Represents a `<Within>` operator to test whether a geometry-valued property
+ * is within a given geometry.
+ *
+ * @constructor
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.geom.Geometry} geometry Geometry.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @extends {ol.format.filter.Spatial}
+ * @api
+ */
+ol.format.filter.Within = function(geometryName, geometry, opt_srsName) {
+
+ ol.format.filter.Spatial.call(this, 'Within', geometryName, geometry, opt_srsName);
+
+};
+ol.inherits(ol.format.filter.Within, ol.format.filter.Spatial);
+
+goog.provide('ol.format.filter');
+
+goog.require('ol');
+goog.require('ol.format.filter.And');
+goog.require('ol.format.filter.Bbox');
+goog.require('ol.format.filter.EqualTo');
+goog.require('ol.format.filter.GreaterThan');
+goog.require('ol.format.filter.GreaterThanOrEqualTo');
+goog.require('ol.format.filter.Intersects');
+goog.require('ol.format.filter.IsBetween');
+goog.require('ol.format.filter.IsLike');
+goog.require('ol.format.filter.IsNull');
+goog.require('ol.format.filter.LessThan');
+goog.require('ol.format.filter.LessThanOrEqualTo');
+goog.require('ol.format.filter.Not');
+goog.require('ol.format.filter.NotEqualTo');
+goog.require('ol.format.filter.Or');
+goog.require('ol.format.filter.Within');
+
+
+/**
+ * Create a logical `<And>` operator between two filter conditions.
+ *
+ * @param {!ol.format.filter.Filter} conditionA First filter condition.
+ * @param {!ol.format.filter.Filter} conditionB Second filter condition.
+ * @returns {!ol.format.filter.And} `<And>` operator.
+ * @api
+ */
+ol.format.filter.and = function(conditionA, conditionB) {
+ return new ol.format.filter.And(conditionA, conditionB);
+};
+
+
+/**
+ * Create a logical `<Or>` operator between two filter conditions.
+ *
+ * @param {!ol.format.filter.Filter} conditionA First filter condition.
+ * @param {!ol.format.filter.Filter} conditionB Second filter condition.
+ * @returns {!ol.format.filter.Or} `<Or>` operator.
+ * @api
+ */
+ol.format.filter.or = function(conditionA, conditionB) {
+ return new ol.format.filter.Or(conditionA, conditionB);
+};
+
+
+/**
+ * Represents a logical `<Not>` operator for a filter condition.
+ *
+ * @param {!ol.format.filter.Filter} condition Filter condition.
+ * @returns {!ol.format.filter.Not} `<Not>` operator.
+ * @api
+ */
+ol.format.filter.not = function(condition) {
+ return new ol.format.filter.Not(condition);
+};
+
+
+/**
+ * Create a `<BBOX>` operator to test whether a geometry-valued property
+ * intersects a fixed bounding box
+ *
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.Extent} extent Extent.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @returns {!ol.format.filter.Bbox} `<BBOX>` operator.
+ * @api
+ */
+ol.format.filter.bbox = function(geometryName, extent, opt_srsName) {
+ return new ol.format.filter.Bbox(geometryName, extent, opt_srsName);
+};
+
+/**
+ * Create a `<Intersects>` operator to test whether a geometry-valued property
+ * intersects a given geometry.
+ *
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.geom.Geometry} geometry Geometry.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @returns {!ol.format.filter.Intersects} `<Intersects>` operator.
+ * @api
+ */
+ol.format.filter.intersects = function(geometryName, geometry, opt_srsName) {
+ return new ol.format.filter.Intersects(geometryName, geometry, opt_srsName);
+};
+
+/**
+ * Create a `<Within>` operator to test whether a geometry-valued property
+ * is within a given geometry.
+ *
+ * @param {!string} geometryName Geometry name to use.
+ * @param {!ol.geom.Geometry} geometry Geometry.
+ * @param {string=} opt_srsName SRS name. No srsName attribute will be
+ * set on geometries when this is not provided.
+ * @returns {!ol.format.filter.Within} `<Within>` operator.
+ * @api
+ */
+ol.format.filter.within = function(geometryName, geometry, opt_srsName) {
+ return new ol.format.filter.Within(geometryName, geometry, opt_srsName);
+};
+
+
+/**
+ * Creates a `<PropertyIsEqualTo>` comparison operator.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!(string|number)} expression The value to compare.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @returns {!ol.format.filter.EqualTo} `<PropertyIsEqualTo>` operator.
+ * @api
+ */
+ol.format.filter.equalTo = function(propertyName, expression, opt_matchCase) {
+ return new ol.format.filter.EqualTo(propertyName, expression, opt_matchCase);
+};
+
+
+/**
+ * Creates a `<PropertyIsNotEqualTo>` comparison operator.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!(string|number)} expression The value to compare.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @returns {!ol.format.filter.NotEqualTo} `<PropertyIsNotEqualTo>` operator.
+ * @api
+ */
+ol.format.filter.notEqualTo = function(propertyName, expression, opt_matchCase) {
+ return new ol.format.filter.NotEqualTo(propertyName, expression, opt_matchCase);
+};
+
+
+/**
+ * Creates a `<PropertyIsLessThan>` comparison operator.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @returns {!ol.format.filter.LessThan} `<PropertyIsLessThan>` operator.
+ * @api
+ */
+ol.format.filter.lessThan = function(propertyName, expression) {
+ return new ol.format.filter.LessThan(propertyName, expression);
+};
+
+
+/**
+ * Creates a `<PropertyIsLessThanOrEqualTo>` comparison operator.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @returns {!ol.format.filter.LessThanOrEqualTo} `<PropertyIsLessThanOrEqualTo>` operator.
+ * @api
+ */
+ol.format.filter.lessThanOrEqualTo = function(propertyName, expression) {
+ return new ol.format.filter.LessThanOrEqualTo(propertyName, expression);
+};
+
+
+/**
+ * Creates a `<PropertyIsGreaterThan>` comparison operator.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @returns {!ol.format.filter.GreaterThan} `<PropertyIsGreaterThan>` operator.
+ * @api
+ */
+ol.format.filter.greaterThan = function(propertyName, expression) {
+ return new ol.format.filter.GreaterThan(propertyName, expression);
+};
+
+
+/**
+ * Creates a `<PropertyIsGreaterThanOrEqualTo>` comparison operator.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} expression The value to compare.
+ * @returns {!ol.format.filter.GreaterThanOrEqualTo} `<PropertyIsGreaterThanOrEqualTo>` operator.
+ * @api
+ */
+ol.format.filter.greaterThanOrEqualTo = function(propertyName, expression) {
+ return new ol.format.filter.GreaterThanOrEqualTo(propertyName, expression);
+};
+
+
+/**
+ * Creates a `<PropertyIsNull>` comparison operator to test whether a property value
+ * is null.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @returns {!ol.format.filter.IsNull} `<PropertyIsNull>` operator.
+ * @api
+ */
+ol.format.filter.isNull = function(propertyName) {
+ return new ol.format.filter.IsNull(propertyName);
+};
+
+
+/**
+ * Creates a `<PropertyIsBetween>` comparison operator to test whether an expression
+ * value lies within a range given by a lower and upper bound (inclusive).
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!number} lowerBoundary The lower bound of the range.
+ * @param {!number} upperBoundary The upper bound of the range.
+ * @returns {!ol.format.filter.IsBetween} `<PropertyIsBetween>` operator.
+ * @api
+ */
+ol.format.filter.between = function(propertyName, lowerBoundary, upperBoundary) {
+ return new ol.format.filter.IsBetween(propertyName, lowerBoundary, upperBoundary);
+};
+
+
+/**
+ * Represents a `<PropertyIsLike>` comparison operator that matches a string property
+ * value against a text pattern.
+ *
+ * @param {!string} propertyName Name of the context property to compare.
+ * @param {!string} pattern Text pattern.
+ * @param {string=} opt_wildCard Pattern character which matches any sequence of
+ * zero or more string characters. Default is '*'.
+ * @param {string=} opt_singleChar pattern character which matches any single
+ * string character. Default is '.'.
+ * @param {string=} opt_escapeChar Escape character which can be used to escape
+ * the pattern characters. Default is '!'.
+ * @param {boolean=} opt_matchCase Case-sensitive?
+ * @returns {!ol.format.filter.IsLike} `<PropertyIsLike>` operator.
+ * @api
+ */
+ol.format.filter.like = function(propertyName, pattern,
+ opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) {
+ return new ol.format.filter.IsLike(propertyName, pattern,
+ opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase);
+};
+
+goog.provide('ol.geom.GeometryCollection');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.geom.Geometry');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.obj');
+
+
+/**
+ * @classdesc
+ * An array of {@link ol.geom.Geometry} objects.
+ *
+ * @constructor
+ * @extends {ol.geom.Geometry}
+ * @param {Array.<ol.geom.Geometry>=} opt_geometries Geometries.
+ * @api stable
+ */
+ol.geom.GeometryCollection = function(opt_geometries) {
+
+ ol.geom.Geometry.call(this);
+
+ /**
+ * @private
+ * @type {Array.<ol.geom.Geometry>}
+ */
+ this.geometries_ = opt_geometries ? opt_geometries : null;
+
+ this.listenGeometriesChange_();
+};
+ol.inherits(ol.geom.GeometryCollection, ol.geom.Geometry);
+
+
+/**
+ * @param {Array.<ol.geom.Geometry>} geometries Geometries.
+ * @private
+ * @return {Array.<ol.geom.Geometry>} Cloned geometries.
+ */
+ol.geom.GeometryCollection.cloneGeometries_ = function(geometries) {
+ var clonedGeometries = [];
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ clonedGeometries.push(geometries[i].clone());
+ }
+ return clonedGeometries;
+};
+
+
+/**
+ * @private
+ */
+ol.geom.GeometryCollection.prototype.unlistenGeometriesChange_ = function() {
+ var i, ii;
+ if (!this.geometries_) {
+ return;
+ }
+ for (i = 0, ii = this.geometries_.length; i < ii; ++i) {
+ ol.events.unlisten(
+ this.geometries_[i], ol.events.EventType.CHANGE,
+ this.changed, this);
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.geom.GeometryCollection.prototype.listenGeometriesChange_ = function() {
+ var i, ii;
+ if (!this.geometries_) {
+ return;
+ }
+ for (i = 0, ii = this.geometries_.length; i < ii; ++i) {
+ ol.events.listen(
+ this.geometries_[i], ol.events.EventType.CHANGE,
+ this.changed, this);
+ }
+};
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.GeometryCollection} Clone.
+ * @api stable
+ */
+ol.geom.GeometryCollection.prototype.clone = function() {
+ var geometryCollection = new ol.geom.GeometryCollection(null);
+ geometryCollection.setGeometries(this.geometries_);
+ return geometryCollection;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.GeometryCollection.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ if (minSquaredDistance <
+ ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
+ return minSquaredDistance;
+ }
+ var geometries = this.geometries_;
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ minSquaredDistance = geometries[i].closestPointXY(
+ x, y, closestPoint, minSquaredDistance);
+ }
+ return minSquaredDistance;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.GeometryCollection.prototype.containsXY = function(x, y) {
+ var geometries = this.geometries_;
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ if (geometries[i].containsXY(x, y)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.GeometryCollection.prototype.computeExtent = function(extent) {
+ ol.extent.createOrUpdateEmpty(extent);
+ var geometries = this.geometries_;
+ for (var i = 0, ii = geometries.length; i < ii; ++i) {
+ ol.extent.extend(extent, geometries[i].getExtent());
+ }
+ return extent;
+};
+
+
+/**
+ * Return the geometries that make up this geometry collection.
+ * @return {Array.<ol.geom.Geometry>} Geometries.
+ * @api stable
+ */
+ol.geom.GeometryCollection.prototype.getGeometries = function() {
+ return ol.geom.GeometryCollection.cloneGeometries_(this.geometries_);
+};
+
+
+/**
+ * @return {Array.<ol.geom.Geometry>} Geometries.
+ */
+ol.geom.GeometryCollection.prototype.getGeometriesArray = function() {
+ return this.geometries_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.GeometryCollection.prototype.getSimplifiedGeometry = function(squaredTolerance) {
+ if (this.simplifiedGeometryRevision != this.getRevision()) {
+ ol.obj.clear(this.simplifiedGeometryCache);
+ this.simplifiedGeometryMaxMinSquaredTolerance = 0;
+ this.simplifiedGeometryRevision = this.getRevision();
+ }
+ if (squaredTolerance < 0 ||
+ (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
+ squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) {
+ return this;
+ }
+ var key = squaredTolerance.toString();
+ if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
+ return this.simplifiedGeometryCache[key];
+ } else {
+ var simplifiedGeometries = [];
+ var geometries = this.geometries_;
+ var simplified = false;
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ var geometry = geometries[i];
+ var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
+ simplifiedGeometries.push(simplifiedGeometry);
+ if (simplifiedGeometry !== geometry) {
+ simplified = true;
+ }
+ }
+ if (simplified) {
+ var simplifiedGeometryCollection = new ol.geom.GeometryCollection(null);
+ simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries);
+ this.simplifiedGeometryCache[key] = simplifiedGeometryCollection;
+ return simplifiedGeometryCollection;
+ } else {
+ this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
+ return this;
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.GeometryCollection.prototype.getType = function() {
+ return ol.geom.GeometryType.GEOMETRY_COLLECTION;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.GeometryCollection.prototype.intersectsExtent = function(extent) {
+ var geometries = this.geometries_;
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ if (geometries[i].intersectsExtent(extent)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * @return {boolean} Is empty.
+ */
+ol.geom.GeometryCollection.prototype.isEmpty = function() {
+ return this.geometries_.length === 0;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.geom.GeometryCollection.prototype.rotate = function(angle, anchor) {
+ var geometries = this.geometries_;
+ for (var i = 0, ii = geometries.length; i < ii; ++i) {
+ geometries[i].rotate(angle, anchor);
+ }
+ this.changed();
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.geom.GeometryCollection.prototype.scale = function(sx, opt_sy, opt_anchor) {
+ var anchor = opt_anchor;
+ if (!anchor) {
+ anchor = ol.extent.getCenter(this.getExtent());
+ }
+ var geometries = this.geometries_;
+ for (var i = 0, ii = geometries.length; i < ii; ++i) {
+ geometries[i].scale(sx, opt_sy, anchor);
+ }
+ this.changed();
+};
+
+
+/**
+ * Set the geometries that make up this geometry collection.
+ * @param {Array.<ol.geom.Geometry>} geometries Geometries.
+ * @api stable
+ */
+ol.geom.GeometryCollection.prototype.setGeometries = function(geometries) {
+ this.setGeometriesArray(
+ ol.geom.GeometryCollection.cloneGeometries_(geometries));
+};
+
+
+/**
+ * @param {Array.<ol.geom.Geometry>} geometries Geometries.
+ */
+ol.geom.GeometryCollection.prototype.setGeometriesArray = function(geometries) {
+ this.unlistenGeometriesChange_();
+ this.geometries_ = geometries;
+ this.listenGeometriesChange_();
+ this.changed();
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.GeometryCollection.prototype.applyTransform = function(transformFn) {
+ var geometries = this.geometries_;
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ geometries[i].applyTransform(transformFn);
+ }
+ this.changed();
+};
+
+
+/**
+ * Translate the geometry.
+ * @param {number} deltaX Delta X.
+ * @param {number} deltaY Delta Y.
+ * @api
+ */
+ol.geom.GeometryCollection.prototype.translate = function(deltaX, deltaY) {
+ var geometries = this.geometries_;
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ geometries[i].translate(deltaX, deltaY);
+ }
+ this.changed();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.GeometryCollection.prototype.disposeInternal = function() {
+ this.unlistenGeometriesChange_();
+ ol.geom.Geometry.prototype.disposeInternal.call(this);
+};
+
+// TODO: serialize dataProjection as crs member when writing
+// see https://github.com/openlayers/ol3/issues/2078
+
+goog.provide('ol.format.GeoJSON');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.JSONFeature');
+goog.require('ol.geom.GeometryCollection');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.obj');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the GeoJSON format.
+ *
+ * @constructor
+ * @extends {ol.format.JSONFeature}
+ * @param {olx.format.GeoJSONOptions=} opt_options Options.
+ * @api stable
+ */
+ol.format.GeoJSON = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.JSONFeature.call(this);
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get(
+ options.defaultDataProjection ?
+ options.defaultDataProjection : 'EPSG:4326');
+
+
+ if (options.featureProjection) {
+ this.defaultFeatureProjection = ol.proj.get(options.featureProjection);
+ }
+
+ /**
+ * Name of the geometry attribute for features.
+ * @type {string|undefined}
+ * @private
+ */
+ this.geometryName_ = options.geometryName;
+
+};
+ol.inherits(ol.format.GeoJSON, ol.format.JSONFeature);
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.GeoJSON.EXTENSIONS_ = ['.geojson'];
+
+
+/**
+ * @param {GeoJSONGeometry|GeoJSONGeometryCollection} object Object.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @private
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.GeoJSON.readGeometry_ = function(object, opt_options) {
+ if (!object) {
+ return null;
+ }
+ var geometryReader = ol.format.GeoJSON.GEOMETRY_READERS_[object.type];
+ return /** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(
+ geometryReader(object), false, opt_options));
+};
+
+
+/**
+ * @param {GeoJSONGeometryCollection} object Object.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @private
+ * @return {ol.geom.GeometryCollection} Geometry collection.
+ */
+ol.format.GeoJSON.readGeometryCollectionGeometry_ = function(
+ object, opt_options) {
+ ol.DEBUG && console.assert(object.type == 'GeometryCollection',
+ 'object.type should be GeometryCollection');
+ var geometries = object.geometries.map(
+ /**
+ * @param {GeoJSONGeometry} geometry Geometry.
+ * @return {ol.geom.Geometry} geometry Geometry.
+ */
+ function(geometry) {
+ return ol.format.GeoJSON.readGeometry_(geometry, opt_options);
+ });
+ return new ol.geom.GeometryCollection(geometries);
+};
+
+
+/**
+ * @param {GeoJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Point} Point.
+ */
+ol.format.GeoJSON.readPointGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.type == 'Point',
+ 'object.type should be Point');
+ return new ol.geom.Point(object.coordinates);
+};
+
+
+/**
+ * @param {GeoJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.LineString} LineString.
+ */
+ol.format.GeoJSON.readLineStringGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.type == 'LineString',
+ 'object.type should be LineString');
+ return new ol.geom.LineString(object.coordinates);
+};
+
+
+/**
+ * @param {GeoJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.MultiLineString} MultiLineString.
+ */
+ol.format.GeoJSON.readMultiLineStringGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.type == 'MultiLineString',
+ 'object.type should be MultiLineString');
+ return new ol.geom.MultiLineString(object.coordinates);
+};
+
+
+/**
+ * @param {GeoJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.MultiPoint} MultiPoint.
+ */
+ol.format.GeoJSON.readMultiPointGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.type == 'MultiPoint',
+ 'object.type should be MultiPoint');
+ return new ol.geom.MultiPoint(object.coordinates);
+};
+
+
+/**
+ * @param {GeoJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.MultiPolygon} MultiPolygon.
+ */
+ol.format.GeoJSON.readMultiPolygonGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.type == 'MultiPolygon',
+ 'object.type should be MultiPolygon');
+ return new ol.geom.MultiPolygon(object.coordinates);
+};
+
+
+/**
+ * @param {GeoJSONGeometry} object Object.
+ * @private
+ * @return {ol.geom.Polygon} Polygon.
+ */
+ol.format.GeoJSON.readPolygonGeometry_ = function(object) {
+ ol.DEBUG && console.assert(object.type == 'Polygon',
+ 'object.type should be Polygon');
+ return new ol.geom.Polygon(object.coordinates);
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry|GeoJSONGeometryCollection} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writeGeometry_ = function(geometry, opt_options) {
+ var geometryWriter = ol.format.GeoJSON.GEOMETRY_WRITERS_[geometry.getType()];
+ return geometryWriter(/** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(geometry, true, opt_options)),
+ opt_options);
+};
+
+
+/**
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @private
+ * @return {GeoJSONGeometryCollection} Empty GeoJSON geometry collection.
+ */
+ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_ = function(geometry) {
+ return /** @type {GeoJSONGeometryCollection} */ ({
+ type: 'GeometryCollection',
+ geometries: []
+ });
+};
+
+
+/**
+ * @param {ol.geom.GeometryCollection} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometryCollection} GeoJSON geometry collection.
+ */
+ol.format.GeoJSON.writeGeometryCollectionGeometry_ = function(
+ geometry, opt_options) {
+ var geometries = geometry.getGeometriesArray().map(function(geometry) {
+ var options = ol.obj.assign({}, opt_options);
+ delete options.featureProjection;
+ return ol.format.GeoJSON.writeGeometry_(geometry, options);
+ });
+ return /** @type {GeoJSONGeometryCollection} */ ({
+ type: 'GeometryCollection',
+ geometries: geometries
+ });
+};
+
+
+/**
+ * @param {ol.geom.LineString} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry, opt_options) {
+ return /** @type {GeoJSONGeometry} */ ({
+ type: 'LineString',
+ coordinates: geometry.getCoordinates()
+ });
+};
+
+
+/**
+ * @param {ol.geom.MultiLineString} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writeMultiLineStringGeometry_ = function(geometry, opt_options) {
+ return /** @type {GeoJSONGeometry} */ ({
+ type: 'MultiLineString',
+ coordinates: geometry.getCoordinates()
+ });
+};
+
+
+/**
+ * @param {ol.geom.MultiPoint} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry, opt_options) {
+ return /** @type {GeoJSONGeometry} */ ({
+ type: 'MultiPoint',
+ coordinates: geometry.getCoordinates()
+ });
+};
+
+
+/**
+ * @param {ol.geom.MultiPolygon} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writeMultiPolygonGeometry_ = function(geometry, opt_options) {
+ var right;
+ if (opt_options) {
+ right = opt_options.rightHanded;
+ }
+ return /** @type {GeoJSONGeometry} */ ({
+ type: 'MultiPolygon',
+ coordinates: geometry.getCoordinates(right)
+ });
+};
+
+
+/**
+ * @param {ol.geom.Point} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writePointGeometry_ = function(geometry, opt_options) {
+ return /** @type {GeoJSONGeometry} */ ({
+ type: 'Point',
+ coordinates: geometry.getCoordinates()
+ });
+};
+
+
+/**
+ * @param {ol.geom.Polygon} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @private
+ * @return {GeoJSONGeometry} GeoJSON geometry.
+ */
+ol.format.GeoJSON.writePolygonGeometry_ = function(geometry, opt_options) {
+ var right;
+ if (opt_options) {
+ right = opt_options.rightHanded;
+ }
+ return /** @type {GeoJSONGeometry} */ ({
+ type: 'Polygon',
+ coordinates: geometry.getCoordinates(right)
+ });
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<string, function(GeoJSONObject): ol.geom.Geometry>}
+ */
+ol.format.GeoJSON.GEOMETRY_READERS_ = {
+ 'Point': ol.format.GeoJSON.readPointGeometry_,
+ 'LineString': ol.format.GeoJSON.readLineStringGeometry_,
+ 'Polygon': ol.format.GeoJSON.readPolygonGeometry_,
+ 'MultiPoint': ol.format.GeoJSON.readMultiPointGeometry_,
+ 'MultiLineString': ol.format.GeoJSON.readMultiLineStringGeometry_,
+ 'MultiPolygon': ol.format.GeoJSON.readMultiPolygonGeometry_,
+ 'GeometryCollection': ol.format.GeoJSON.readGeometryCollectionGeometry_
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (GeoJSONGeometry|GeoJSONGeometryCollection)>}
+ */
+ol.format.GeoJSON.GEOMETRY_WRITERS_ = {
+ 'Point': ol.format.GeoJSON.writePointGeometry_,
+ 'LineString': ol.format.GeoJSON.writeLineStringGeometry_,
+ 'Polygon': ol.format.GeoJSON.writePolygonGeometry_,
+ 'MultiPoint': ol.format.GeoJSON.writeMultiPointGeometry_,
+ 'MultiLineString': ol.format.GeoJSON.writeMultiLineStringGeometry_,
+ 'MultiPolygon': ol.format.GeoJSON.writeMultiPolygonGeometry_,
+ 'GeometryCollection': ol.format.GeoJSON.writeGeometryCollectionGeometry_,
+ 'Circle': ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GeoJSON.prototype.getExtensions = function() {
+ return ol.format.GeoJSON.EXTENSIONS_;
+};
+
+
+/**
+ * Read a feature from a GeoJSON Feature source. Only works for Feature or
+ * geometry types. Use {@link ol.format.GeoJSON#readFeatures} to read
+ * FeatureCollection source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.readFeature;
+
+
+/**
+ * Read all features from a GeoJSON source. Works for all GeoJSON types.
+ * If the source includes only geometries, features will be created with those
+ * geometries.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GeoJSON.prototype.readFeatureFromObject = function(
+ object, opt_options) {
+
+ ol.DEBUG && console.assert(object.type !== 'FeatureCollection', 'Expected a Feature or geometry');
+
+ /**
+ * @type {GeoJSONFeature}
+ */
+ var geoJSONFeature = null;
+ if (object.type === 'Feature') {
+ geoJSONFeature = /** @type {GeoJSONFeature} */ (object);
+ } else {
+ geoJSONFeature = /** @type {GeoJSONFeature} */ ({
+ type: 'Feature',
+ geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */ (object)
+ });
+ }
+
+ var geometry = ol.format.GeoJSON.readGeometry_(geoJSONFeature.geometry, opt_options);
+ var feature = new ol.Feature();
+ if (this.geometryName_) {
+ feature.setGeometryName(this.geometryName_);
+ }
+ feature.setGeometry(geometry);
+ if (geoJSONFeature.id !== undefined) {
+ feature.setId(geoJSONFeature.id);
+ }
+ if (geoJSONFeature.properties) {
+ feature.setProperties(geoJSONFeature.properties);
+ }
+ return feature;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GeoJSON.prototype.readFeaturesFromObject = function(
+ object, opt_options) {
+ var geoJSONObject = /** @type {GeoJSONObject} */ (object);
+ /** @type {Array.<ol.Feature>} */
+ var features = null;
+ if (geoJSONObject.type === 'FeatureCollection') {
+ var geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */
+ (object);
+ features = [];
+ var geoJSONFeatures = geoJSONFeatureCollection.features;
+ var i, ii;
+ for (i = 0, ii = geoJSONFeatures.length; i < ii; ++i) {
+ features.push(this.readFeatureFromObject(geoJSONFeatures[i],
+ opt_options));
+ }
+ } else {
+ features = [this.readFeatureFromObject(object, opt_options)];
+ }
+ return features;
+};
+
+
+/**
+ * Read a geometry from a GeoJSON source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.geom.Geometry} Geometry.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.readGeometry;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GeoJSON.prototype.readGeometryFromObject = function(
+ object, opt_options) {
+ return ol.format.GeoJSON.readGeometry_(
+ /** @type {GeoJSONGeometry} */ (object), opt_options);
+};
+
+
+/**
+ * Read the projection from a GeoJSON source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.readProjection;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GeoJSON.prototype.readProjectionFromObject = function(object) {
+ var geoJSONObject = /** @type {GeoJSONObject} */ (object);
+ var crs = geoJSONObject.crs;
+ var projection;
+ if (crs) {
+ if (crs.type == 'name') {
+ projection = ol.proj.get(crs.properties.name);
+ } else if (crs.type == 'EPSG') {
+ // 'EPSG' is not part of the GeoJSON specification, but is generated by
+ // GeoServer.
+ // TODO: remove this when http://jira.codehaus.org/browse/GEOS-5996
+ // is fixed and widely deployed.
+ projection = ol.proj.get('EPSG:' + crs.properties.code);
+ } else {
+ ol.asserts.assert(false, 36); // Unknown SRS type
+ }
+ } else {
+ projection = this.defaultDataProjection;
+ }
+ return /** @type {ol.proj.Projection} */ (projection);
+};
+
+
+/**
+ * Encode a feature as a GeoJSON Feature string.
+ *
+ * @function
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} GeoJSON.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.writeFeature;
+
+
+/**
+ * Encode a feature as a GeoJSON Feature object.
+ *
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {GeoJSONFeature} Object.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+
+ var object = /** @type {GeoJSONFeature} */ ({
+ 'type': 'Feature'
+ });
+ var id = feature.getId();
+ if (id !== undefined) {
+ object.id = id;
+ }
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ object.geometry =
+ ol.format.GeoJSON.writeGeometry_(geometry, opt_options);
+ } else {
+ object.geometry = null;
+ }
+ var properties = feature.getProperties();
+ delete properties[feature.getGeometryName()];
+ if (!ol.obj.isEmpty(properties)) {
+ object.properties = properties;
+ } else {
+ object.properties = null;
+ }
+ return object;
+};
+
+
+/**
+ * Encode an array of features as GeoJSON.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} GeoJSON.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.writeFeatures;
+
+
+/**
+ * Encode an array of features as a GeoJSON object.
+ *
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {GeoJSONFeatureCollection} GeoJSON Object.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ var objects = [];
+ var i, ii;
+ for (i = 0, ii = features.length; i < ii; ++i) {
+ objects.push(this.writeFeatureObject(features[i], opt_options));
+ }
+ return /** @type {GeoJSONFeatureCollection} */ ({
+ type: 'FeatureCollection',
+ features: objects
+ });
+};
+
+
+/**
+ * Encode a geometry as a GeoJSON string.
+ *
+ * @function
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} GeoJSON.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.writeGeometry;
+
+
+/**
+ * Encode a geometry as a GeoJSON object.
+ *
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object.
+ * @api stable
+ */
+ol.format.GeoJSON.prototype.writeGeometryObject = function(geometry,
+ opt_options) {
+ return ol.format.GeoJSON.writeGeometry_(geometry,
+ this.adaptOptions(opt_options));
+};
+
+goog.provide('ol.format.XMLFeature');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.format.Feature');
+goog.require('ol.format.FormatType');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for XML feature formats.
+ *
+ * @constructor
+ * @extends {ol.format.Feature}
+ */
+ol.format.XMLFeature = function() {
+
+ /**
+ * @type {XMLSerializer}
+ * @private
+ */
+ this.xmlSerializer_ = new XMLSerializer();
+
+ ol.format.Feature.call(this);
+};
+ol.inherits(ol.format.XMLFeature, ol.format.Feature);
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.getType = function() {
+ return ol.format.FormatType.XML;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.readFeature = function(source, opt_options) {
+ if (ol.xml.isDocument(source)) {
+ return this.readFeatureFromDocument(
+ /** @type {Document} */ (source), opt_options);
+ } else if (ol.xml.isNode(source)) {
+ return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options);
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readFeatureFromDocument(doc, opt_options);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @return {ol.Feature} Feature.
+ */
+ol.format.XMLFeature.prototype.readFeatureFromDocument = function(
+ doc, opt_options) {
+ var features = this.readFeaturesFromDocument(doc, opt_options);
+ if (features.length > 0) {
+ return features[0];
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @abstract
+ * @param {Node} node Node.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @return {ol.Feature} Feature.
+ */
+ol.format.XMLFeature.prototype.readFeatureFromNode = function(node, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.readFeatures = function(source, opt_options) {
+ if (ol.xml.isDocument(source)) {
+ return this.readFeaturesFromDocument(
+ /** @type {Document} */ (source), opt_options);
+ } else if (ol.xml.isNode(source)) {
+ return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options);
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readFeaturesFromDocument(doc, opt_options);
+ } else {
+ return [];
+ }
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @protected
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.format.XMLFeature.prototype.readFeaturesFromDocument = function(
+ doc, opt_options) {
+ /** @type {Array.<ol.Feature>} */
+ var features = [];
+ var n;
+ for (n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ ol.array.extend(features, this.readFeaturesFromNode(n, opt_options));
+ }
+ }
+ return features;
+};
+
+
+/**
+ * @abstract
+ * @param {Node} node Node.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @protected
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.format.XMLFeature.prototype.readFeaturesFromNode = function(node, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.readGeometry = function(source, opt_options) {
+ if (ol.xml.isDocument(source)) {
+ return this.readGeometryFromDocument(
+ /** @type {Document} */ (source), opt_options);
+ } else if (ol.xml.isNode(source)) {
+ return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options);
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readGeometryFromDocument(doc, opt_options);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @abstract
+ * @param {Document} doc Document.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @protected
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.XMLFeature.prototype.readGeometryFromDocument = function(doc, opt_options) {};
+
+
+/**
+ * @abstract
+ * @param {Node} node Node.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @protected
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.XMLFeature.prototype.readGeometryFromNode = function(node, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.readProjection = function(source) {
+ if (ol.xml.isDocument(source)) {
+ return this.readProjectionFromDocument(/** @type {Document} */ (source));
+ } else if (ol.xml.isNode(source)) {
+ return this.readProjectionFromNode(/** @type {Node} */ (source));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readProjectionFromDocument(doc);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @protected
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.format.XMLFeature.prototype.readProjectionFromDocument = function(doc) {
+ return this.defaultDataProjection;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @protected
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.format.XMLFeature.prototype.readProjectionFromNode = function(node) {
+ return this.defaultDataProjection;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.writeFeature = function(feature, opt_options) {
+ var node = this.writeFeatureNode(feature, opt_options);
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return this.xmlSerializer_.serializeToString(node);
+};
+
+
+/**
+ * @abstract
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @protected
+ * @return {Node} Node.
+ */
+ol.format.XMLFeature.prototype.writeFeatureNode = function(feature, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.writeFeatures = function(features, opt_options) {
+ var node = this.writeFeaturesNode(features, opt_options);
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return this.xmlSerializer_.serializeToString(node);
+};
+
+
+/**
+ * @abstract
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ */
+ol.format.XMLFeature.prototype.writeFeaturesNode = function(features, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.XMLFeature.prototype.writeGeometry = function(geometry, opt_options) {
+ var node = this.writeGeometryNode(geometry, opt_options);
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return this.xmlSerializer_.serializeToString(node);
+};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ */
+ol.format.XMLFeature.prototype.writeGeometryNode = function(geometry, opt_options) {};
+
+// FIXME Envelopes should not be treated as geometries! readEnvelope_ is part
+// of GEOMETRY_PARSERS_ and methods using GEOMETRY_PARSERS_ do not expect
+// envelopes/extents, only geometries!
+goog.provide('ol.format.GMLBase');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.XMLFeature');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.LinearRing');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Feature base format for reading and writing data in the GML format.
+ * This class cannot be instantiated, it contains only base content that
+ * is shared with versioned format classes ol.format.GML2 and
+ * ol.format.GML3.
+ *
+ * @constructor
+ * @param {olx.format.GMLOptions=} opt_options
+ * Optional configuration object.
+ * @extends {ol.format.XMLFeature}
+ */
+ol.format.GMLBase = function(opt_options) {
+ var options = /** @type {olx.format.GMLOptions} */
+ (opt_options ? opt_options : {});
+
+ /**
+ * @protected
+ * @type {Array.<string>|string|undefined}
+ */
+ this.featureType = options.featureType;
+
+ /**
+ * @protected
+ * @type {Object.<string, string>|string|undefined}
+ */
+ this.featureNS = options.featureNS;
+
+ /**
+ * @protected
+ * @type {string}
+ */
+ this.srsName = options.srsName;
+
+ /**
+ * @protected
+ * @type {string}
+ */
+ this.schemaLocation = '';
+
+ /**
+ * @type {Object.<string, Object.<string, Object>>}
+ */
+ this.FEATURE_COLLECTION_PARSERS = {};
+ this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS] = {
+ 'featureMember': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readFeaturesInternal),
+ 'featureMembers': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readFeaturesInternal)
+ };
+
+ ol.format.XMLFeature.call(this);
+};
+ol.inherits(ol.format.GMLBase, ol.format.XMLFeature);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.GMLBase.GMLNS = 'http://www.opengis.net/gml';
+
+
+/**
+ * A regular expression that matches if a string only contains whitespace
+ * characters. It will e.g. match `''`, `' '`, `'\n'` etc. The non-breaking
+ * space (0xa0) is explicitly included as IE doesn't include it in its
+ * definition of `\s`.
+ *
+ * Information from `goog.string.isEmptyOrWhitespace`: https://github.com/google/closure-library/blob/e877b1e/closure/goog/string/string.js#L156-L160
+ *
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.format.GMLBase.ONLY_WHITESPACE_RE_ = /^[\s\xa0]*$/;
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Array.<ol.Feature> | undefined} Features.
+ */
+ol.format.GMLBase.prototype.readFeaturesInternal = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ var localName = node.localName;
+ var features = null;
+ if (localName == 'FeatureCollection') {
+ if (node.namespaceURI === 'http://www.opengis.net/wfs') {
+ features = ol.xml.pushParseAndPop([],
+ this.FEATURE_COLLECTION_PARSERS, node,
+ objectStack, this);
+ } else {
+ features = ol.xml.pushParseAndPop(null,
+ this.FEATURE_COLLECTION_PARSERS, node,
+ objectStack, this);
+ }
+ } else if (localName == 'featureMembers' || localName == 'featureMember') {
+ var context = objectStack[0];
+ var featureType = context['featureType'];
+ var featureNS = context['featureNS'];
+ var i, ii, prefix = 'p', defaultPrefix = 'p0';
+ if (!featureType && node.childNodes) {
+ featureType = [], featureNS = {};
+ for (i = 0, ii = node.childNodes.length; i < ii; ++i) {
+ var child = node.childNodes[i];
+ if (child.nodeType === 1) {
+ var ft = child.nodeName.split(':').pop();
+ if (featureType.indexOf(ft) === -1) {
+ var key = '';
+ var count = 0;
+ var uri = child.namespaceURI;
+ for (var candidate in featureNS) {
+ if (featureNS[candidate] === uri) {
+ key = candidate;
+ break;
+ }
+ ++count;
+ }
+ if (!key) {
+ key = prefix + count;
+ featureNS[key] = uri;
+ }
+ featureType.push(key + ':' + ft);
+ }
+ }
+ }
+ if (localName != 'featureMember') {
+ // recheck featureType for each featureMember
+ context['featureType'] = featureType;
+ context['featureNS'] = featureNS;
+ }
+ }
+ if (typeof featureNS === 'string') {
+ var ns = featureNS;
+ featureNS = {};
+ featureNS[defaultPrefix] = ns;
+ }
+ var parsersNS = {};
+ var featureTypes = Array.isArray(featureType) ? featureType : [featureType];
+ for (var p in featureNS) {
+ var parsers = {};
+ for (i = 0, ii = featureTypes.length; i < ii; ++i) {
+ var featurePrefix = featureTypes[i].indexOf(':') === -1 ?
+ defaultPrefix : featureTypes[i].split(':')[0];
+ if (featurePrefix === p) {
+ parsers[featureTypes[i].split(':').pop()] =
+ (localName == 'featureMembers') ?
+ ol.xml.makeArrayPusher(this.readFeatureElement, this) :
+ ol.xml.makeReplacer(this.readFeatureElement, this);
+ }
+ }
+ parsersNS[featureNS[p]] = parsers;
+ }
+ if (localName == 'featureMember') {
+ features = ol.xml.pushParseAndPop(undefined, parsersNS, node, objectStack);
+ } else {
+ features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack);
+ }
+ }
+ if (features === null) {
+ features = [];
+ }
+ return features;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.Geometry|undefined} Geometry.
+ */
+ol.format.GMLBase.prototype.readGeometryElement = function(node, objectStack) {
+ var context = /** @type {Object} */ (objectStack[0]);
+ context['srsName'] = node.firstElementChild.getAttribute('srsName');
+ /** @type {ol.geom.Geometry} */
+ var geometry = ol.xml.pushParseAndPop(null,
+ this.GEOMETRY_PARSERS_, node, objectStack, this);
+ if (geometry) {
+ return /** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(geometry, false, context));
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.Feature} Feature.
+ */
+ol.format.GMLBase.prototype.readFeatureElement = function(node, objectStack) {
+ var n;
+ var fid = node.getAttribute('fid') ||
+ ol.xml.getAttributeNS(node, ol.format.GMLBase.GMLNS, 'id');
+ var values = {}, geometryName;
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ var localName = n.localName;
+ // Assume attribute elements have one child node and that the child
+ // is a text or CDATA node (to be treated as text).
+ // Otherwise assume it is a geometry node.
+ if (n.childNodes.length === 0 ||
+ (n.childNodes.length === 1 &&
+ (n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) {
+ var value = ol.xml.getAllTextContent(n, false);
+ if (ol.format.GMLBase.ONLY_WHITESPACE_RE_.test(value)) {
+ value = undefined;
+ }
+ values[localName] = value;
+ } else {
+ // boundedBy is an extent and must not be considered as a geometry
+ if (localName !== 'boundedBy') {
+ geometryName = localName;
+ }
+ values[localName] = this.readGeometryElement(n, objectStack);
+ }
+ }
+ var feature = new ol.Feature(values);
+ if (geometryName) {
+ feature.setGeometryName(geometryName);
+ }
+ if (fid) {
+ feature.setId(fid);
+ }
+ return feature;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.Point|undefined} Point.
+ */
+ol.format.GMLBase.prototype.readPoint = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Point', 'localName should be Point');
+ var flatCoordinates =
+ this.readFlatCoordinatesFromNode_(node, objectStack);
+ if (flatCoordinates) {
+ var point = new ol.geom.Point(null);
+ ol.DEBUG && console.assert(flatCoordinates.length == 3,
+ 'flatCoordinates should have a length of 3');
+ point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
+ return point;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.MultiPoint|undefined} MultiPoint.
+ */
+ol.format.GMLBase.prototype.readMultiPoint = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MultiPoint',
+ 'localName should be MultiPoint');
+ /** @type {Array.<Array.<number>>} */
+ var coordinates = ol.xml.pushParseAndPop([],
+ this.MULTIPOINT_PARSERS_, node, objectStack, this);
+ if (coordinates) {
+ return new ol.geom.MultiPoint(coordinates);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.MultiLineString|undefined} MultiLineString.
+ */
+ol.format.GMLBase.prototype.readMultiLineString = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MultiLineString',
+ 'localName should be MultiLineString');
+ /** @type {Array.<ol.geom.LineString>} */
+ var lineStrings = ol.xml.pushParseAndPop([],
+ this.MULTILINESTRING_PARSERS_, node, objectStack, this);
+ if (lineStrings) {
+ var multiLineString = new ol.geom.MultiLineString(null);
+ multiLineString.setLineStrings(lineStrings);
+ return multiLineString;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.MultiPolygon|undefined} MultiPolygon.
+ */
+ol.format.GMLBase.prototype.readMultiPolygon = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MultiPolygon',
+ 'localName should be MultiPolygon');
+ /** @type {Array.<ol.geom.Polygon>} */
+ var polygons = ol.xml.pushParseAndPop([],
+ this.MULTIPOLYGON_PARSERS_, node, objectStack, this);
+ if (polygons) {
+ var multiPolygon = new ol.geom.MultiPolygon(null);
+ multiPolygon.setPolygons(polygons);
+ return multiPolygon;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GMLBase.prototype.pointMemberParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'pointMember' ||
+ node.localName == 'pointMembers',
+ 'localName should be pointMember or pointMembers');
+ ol.xml.parseNode(this.POINTMEMBER_PARSERS_,
+ node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GMLBase.prototype.lineStringMemberParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'lineStringMember' ||
+ node.localName == 'lineStringMembers',
+ 'localName should be LineStringMember or LineStringMembers');
+ ol.xml.parseNode(this.LINESTRINGMEMBER_PARSERS_,
+ node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GMLBase.prototype.polygonMemberParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'polygonMember' ||
+ node.localName == 'polygonMembers',
+ 'localName should be polygonMember or polygonMembers');
+ ol.xml.parseNode(this.POLYGONMEMBER_PARSERS_, node,
+ objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.LineString|undefined} LineString.
+ */
+ol.format.GMLBase.prototype.readLineString = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LineString',
+ 'localName should be LineString');
+ var flatCoordinates =
+ this.readFlatCoordinatesFromNode_(node, objectStack);
+ if (flatCoordinates) {
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
+ return lineString;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>|undefined} LinearRing flat coordinates.
+ */
+ol.format.GMLBase.prototype.readFlatLinearRing_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LinearRing',
+ 'localName should be LinearRing');
+ var ring = ol.xml.pushParseAndPop(null,
+ this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
+ objectStack, this);
+ if (ring) {
+ return ring;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.LinearRing|undefined} LinearRing.
+ */
+ol.format.GMLBase.prototype.readLinearRing = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LinearRing',
+ 'localName should be LinearRing');
+ var flatCoordinates =
+ this.readFlatCoordinatesFromNode_(node, objectStack);
+ if (flatCoordinates) {
+ var ring = new ol.geom.LinearRing(null);
+ ring.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
+ return ring;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.geom.Polygon|undefined} Polygon.
+ */
+ol.format.GMLBase.prototype.readPolygon = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Polygon',
+ 'localName should be Polygon');
+ /** @type {Array.<Array.<number>>} */
+ var flatLinearRings = ol.xml.pushParseAndPop([null],
+ this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
+ if (flatLinearRings && flatLinearRings[0]) {
+ var polygon = new ol.geom.Polygon(null);
+ var flatCoordinates = flatLinearRings[0];
+ var ends = [flatCoordinates.length];
+ var i, ii;
+ for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
+ ol.array.extend(flatCoordinates, flatLinearRings[i]);
+ ends.push(flatCoordinates.length);
+ }
+ polygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XYZ, flatCoordinates, ends);
+ return polygon;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return ol.xml.pushParseAndPop(null,
+ this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
+ objectStack, this);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GMLBase.prototype.MULTIPOINT_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'pointMember': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.pointMemberParser_),
+ 'pointMembers': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.pointMemberParser_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GMLBase.prototype.MULTILINESTRING_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'lineStringMember': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.lineStringMemberParser_),
+ 'lineStringMembers': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.lineStringMemberParser_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GMLBase.prototype.MULTIPOLYGON_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'polygonMember': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.polygonMemberParser_),
+ 'polygonMembers': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.polygonMemberParser_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GMLBase.prototype.POINTMEMBER_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'Point': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GMLBase.prototype.LINESTRINGMEMBER_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'LineString': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.readLineString)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GMLBase.prototype.POLYGONMEMBER_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'Polygon': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.readPolygon)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @protected
+ */
+ol.format.GMLBase.prototype.RING_PARSERS = {
+ 'http://www.opengis.net/gml' : {
+ 'LinearRing': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readFlatLinearRing_)
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GMLBase.prototype.readGeometryFromNode = function(node, opt_options) {
+ var geometry = this.readGeometryElement(node,
+ [this.getReadOptions(node, opt_options ? opt_options : {})]);
+ return geometry ? geometry : null;
+};
+
+
+/**
+ * Read all features from a GML FeatureCollection.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.GMLBase.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GMLBase.prototype.readFeaturesFromNode = function(node, opt_options) {
+ var options = {
+ featureType: this.featureType,
+ featureNS: this.featureNS
+ };
+ if (opt_options) {
+ ol.obj.assign(options, this.getReadOptions(node, opt_options));
+ }
+ var features = this.readFeaturesInternal(node, [options]);
+ return features || [];
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GMLBase.prototype.readProjectionFromNode = function(node) {
+ return ol.proj.get(this.srsName ? this.srsName :
+ node.firstElementChild.getAttribute('srsName'));
+};
+
+goog.provide('ol.format.XSD');
+
+goog.require('ol');
+goog.require('ol.xml');
+goog.require('ol.string');
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.XSD.NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema';
+
+
+/**
+ * @param {Node} node Node.
+ * @return {boolean|undefined} Boolean.
+ */
+ol.format.XSD.readBoolean = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ return ol.format.XSD.readBooleanString(s);
+};
+
+
+/**
+ * @param {string} string String.
+ * @return {boolean|undefined} Boolean.
+ */
+ol.format.XSD.readBooleanString = function(string) {
+ var m = /^\s*(true|1)|(false|0)\s*$/.exec(string);
+ if (m) {
+ return m[1] !== undefined || false;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {number|undefined} DateTime in seconds.
+ */
+ol.format.XSD.readDateTime = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ var dateTime = Date.parse(s);
+ return isNaN(dateTime) ? undefined : dateTime / 1000;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {number|undefined} Decimal.
+ */
+ol.format.XSD.readDecimal = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ return ol.format.XSD.readDecimalString(s);
+};
+
+
+/**
+ * @param {string} string String.
+ * @return {number|undefined} Decimal.
+ */
+ol.format.XSD.readDecimalString = function(string) {
+ // FIXME check spec
+ var m = /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(string);
+ if (m) {
+ return parseFloat(m[1]);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {number|undefined} Non negative integer.
+ */
+ol.format.XSD.readNonNegativeInteger = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ return ol.format.XSD.readNonNegativeIntegerString(s);
+};
+
+
+/**
+ * @param {string} string String.
+ * @return {number|undefined} Non negative integer.
+ */
+ol.format.XSD.readNonNegativeIntegerString = function(string) {
+ var m = /^\s*(\d+)\s*$/.exec(string);
+ if (m) {
+ return parseInt(m[1], 10);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {string|undefined} String.
+ */
+ol.format.XSD.readString = function(node) {
+ return ol.xml.getAllTextContent(node, false).trim();
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the boolean to.
+ * @param {boolean} bool Boolean.
+ */
+ol.format.XSD.writeBooleanTextNode = function(node, bool) {
+ ol.format.XSD.writeStringTextNode(node, (bool) ? '1' : '0');
+};
+
+
+/**
+ * @param {Node} node Node to append a CDATA Section with the string to.
+ * @param {string} string String.
+ */
+ol.format.XSD.writeCDATASection = function(node, string) {
+ node.appendChild(ol.xml.DOCUMENT.createCDATASection(string));
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the dateTime to.
+ * @param {number} dateTime DateTime in seconds.
+ */
+ol.format.XSD.writeDateTimeTextNode = function(node, dateTime) {
+ var date = new Date(dateTime * 1000);
+ var string = date.getUTCFullYear() + '-' +
+ ol.string.padNumber(date.getUTCMonth() + 1, 2) + '-' +
+ ol.string.padNumber(date.getUTCDate(), 2) + 'T' +
+ ol.string.padNumber(date.getUTCHours(), 2) + ':' +
+ ol.string.padNumber(date.getUTCMinutes(), 2) + ':' +
+ ol.string.padNumber(date.getUTCSeconds(), 2) + 'Z';
+ node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the decimal to.
+ * @param {number} decimal Decimal.
+ */
+ol.format.XSD.writeDecimalTextNode = function(node, decimal) {
+ var string = decimal.toPrecision();
+ node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the decimal to.
+ * @param {number} nonNegativeInteger Non negative integer.
+ */
+ol.format.XSD.writeNonNegativeIntegerTextNode = function(node, nonNegativeInteger) {
+ ol.DEBUG && console.assert(nonNegativeInteger >= 0, 'value should be more than 0');
+ ol.DEBUG && console.assert(nonNegativeInteger == (nonNegativeInteger | 0),
+ 'value should be an integer value');
+ var string = nonNegativeInteger.toString();
+ node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the string to.
+ * @param {string} string String.
+ */
+ol.format.XSD.writeStringTextNode = function(node, string) {
+ node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
+};
+
+goog.provide('ol.format.GML3');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.format.Feature');
+goog.require('ol.format.GMLBase');
+goog.require('ol.format.XSD');
+goog.require('ol.geom.Geometry');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Polygon');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the GML format
+ * version 3.1.1.
+ * Currently only supports GML 3.1.1 Simple Features profile.
+ *
+ * @constructor
+ * @param {olx.format.GMLOptions=} opt_options
+ * Optional configuration object.
+ * @extends {ol.format.GMLBase}
+ * @api
+ */
+ol.format.GML3 = function(opt_options) {
+ var options = /** @type {olx.format.GMLOptions} */
+ (opt_options ? opt_options : {});
+
+ ol.format.GMLBase.call(this, options);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.surface_ = options.surface !== undefined ? options.surface : false;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.curve_ = options.curve !== undefined ? options.curve : false;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.multiCurve_ = options.multiCurve !== undefined ?
+ options.multiCurve : true;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.multiSurface_ = options.multiSurface !== undefined ?
+ options.multiSurface : true;
+
+ /**
+ * @inheritDoc
+ */
+ this.schemaLocation = options.schemaLocation ?
+ options.schemaLocation : ol.format.GML3.schemaLocation_;
+
+};
+ol.inherits(ol.format.GML3, ol.format.GMLBase);
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ol.format.GML3.schemaLocation_ = ol.format.GMLBase.GMLNS +
+ ' http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/' +
+ '1.0.0/gmlsf.xsd';
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.MultiLineString|undefined} MultiLineString.
+ */
+ol.format.GML3.prototype.readMultiCurve_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MultiCurve',
+ 'localName should be MultiCurve');
+ /** @type {Array.<ol.geom.LineString>} */
+ var lineStrings = ol.xml.pushParseAndPop([],
+ this.MULTICURVE_PARSERS_, node, objectStack, this);
+ if (lineStrings) {
+ var multiLineString = new ol.geom.MultiLineString(null);
+ multiLineString.setLineStrings(lineStrings);
+ return multiLineString;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.MultiPolygon|undefined} MultiPolygon.
+ */
+ol.format.GML3.prototype.readMultiSurface_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MultiSurface',
+ 'localName should be MultiSurface');
+ /** @type {Array.<ol.geom.Polygon>} */
+ var polygons = ol.xml.pushParseAndPop([],
+ this.MULTISURFACE_PARSERS_, node, objectStack, this);
+ if (polygons) {
+ var multiPolygon = new ol.geom.MultiPolygon(null);
+ multiPolygon.setPolygons(polygons);
+ return multiPolygon;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GML3.prototype.curveMemberParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'curveMember' ||
+ node.localName == 'curveMembers',
+ 'localName should be curveMember or curveMembers');
+ ol.xml.parseNode(this.CURVEMEMBER_PARSERS_, node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GML3.prototype.surfaceMemberParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'surfaceMember' ||
+ node.localName == 'surfaceMembers',
+ 'localName should be surfaceMember or surfaceMembers');
+ ol.xml.parseNode(this.SURFACEMEMBER_PARSERS_,
+ node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<(Array.<number>)>|undefined} flat coordinates.
+ */
+ol.format.GML3.prototype.readPatch_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'patches',
+ 'localName should be patches');
+ return ol.xml.pushParseAndPop([null],
+ this.PATCHES_PARSERS_, node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>|undefined} flat coordinates.
+ */
+ol.format.GML3.prototype.readSegment_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'segments',
+ 'localName should be segments');
+ return ol.xml.pushParseAndPop([null],
+ this.SEGMENTS_PARSERS_, node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<(Array.<number>)>|undefined} flat coordinates.
+ */
+ol.format.GML3.prototype.readPolygonPatch_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'npde.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'PolygonPatch',
+ 'localName should be PolygonPatch');
+ return ol.xml.pushParseAndPop([null],
+ this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>|undefined} flat coordinates.
+ */
+ol.format.GML3.prototype.readLineStringSegment_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LineStringSegment',
+ 'localName should be LineStringSegment');
+ return ol.xml.pushParseAndPop([null],
+ this.GEOMETRY_FLAT_COORDINATES_PARSERS_,
+ node, objectStack, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GML3.prototype.interiorParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'interior',
+ 'localName should be interior');
+ /** @type {Array.<number>|undefined} */
+ var flatLinearRing = ol.xml.pushParseAndPop(undefined,
+ this.RING_PARSERS, node, objectStack, this);
+ if (flatLinearRing) {
+ var flatLinearRings = /** @type {Array.<Array.<number>>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(flatLinearRings),
+ 'flatLinearRings should be an array');
+ ol.DEBUG && console.assert(flatLinearRings.length > 0,
+ 'flatLinearRings should have an array length of 1 or more');
+ flatLinearRings.push(flatLinearRing);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GML3.prototype.exteriorParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'exterior',
+ 'localName should be exterior');
+ /** @type {Array.<number>|undefined} */
+ var flatLinearRing = ol.xml.pushParseAndPop(undefined,
+ this.RING_PARSERS, node, objectStack, this);
+ if (flatLinearRing) {
+ var flatLinearRings = /** @type {Array.<Array.<number>>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(flatLinearRings),
+ 'flatLinearRings should be an array');
+ ol.DEBUG && console.assert(flatLinearRings.length > 0,
+ 'flatLinearRings should have an array length of 1 or more');
+ flatLinearRings[0] = flatLinearRing;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.Polygon|undefined} Polygon.
+ */
+ol.format.GML3.prototype.readSurface_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Surface',
+ 'localName should be Surface');
+ /** @type {Array.<Array.<number>>} */
+ var flatLinearRings = ol.xml.pushParseAndPop([null],
+ this.SURFACE_PARSERS_, node, objectStack, this);
+ if (flatLinearRings && flatLinearRings[0]) {
+ var polygon = new ol.geom.Polygon(null);
+ var flatCoordinates = flatLinearRings[0];
+ var ends = [flatCoordinates.length];
+ var i, ii;
+ for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
+ ol.array.extend(flatCoordinates, flatLinearRings[i]);
+ ends.push(flatCoordinates.length);
+ }
+ polygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XYZ, flatCoordinates, ends);
+ return polygon;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.LineString|undefined} LineString.
+ */
+ol.format.GML3.prototype.readCurve_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Curve', 'localName should be Curve');
+ /** @type {Array.<number>} */
+ var flatCoordinates = ol.xml.pushParseAndPop([null],
+ this.CURVE_PARSERS_, node, objectStack, this);
+ if (flatCoordinates) {
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
+ return lineString;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Extent|undefined} Envelope.
+ */
+ol.format.GML3.prototype.readEnvelope_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Envelope',
+ 'localName should be Envelope');
+ /** @type {Array.<number>} */
+ var flatCoordinates = ol.xml.pushParseAndPop([null],
+ this.ENVELOPE_PARSERS_, node, objectStack, this);
+ return ol.extent.createOrUpdate(flatCoordinates[1][0],
+ flatCoordinates[1][1], flatCoordinates[2][0],
+ flatCoordinates[2][1]);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>|undefined} Flat coordinates.
+ */
+ol.format.GML3.prototype.readFlatPos_ = function(node, objectStack) {
+ var s = ol.xml.getAllTextContent(node, false);
+ var re = /^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/;
+ /** @type {Array.<number>} */
+ var flatCoordinates = [];
+ var m;
+ while ((m = re.exec(s))) {
+ flatCoordinates.push(parseFloat(m[1]));
+ s = s.substr(m[0].length);
+ }
+ if (s !== '') {
+ return undefined;
+ }
+ var context = objectStack[0];
+ var containerSrs = context['srsName'];
+ var axisOrientation = 'enu';
+ if (containerSrs) {
+ var proj = ol.proj.get(containerSrs);
+ axisOrientation = proj.getAxisOrientation();
+ }
+ if (axisOrientation === 'neu') {
+ var i, ii;
+ for (i = 0, ii = flatCoordinates.length; i < ii; i += 3) {
+ var y = flatCoordinates[i];
+ var x = flatCoordinates[i + 1];
+ flatCoordinates[i] = x;
+ flatCoordinates[i + 1] = y;
+ }
+ }
+ var len = flatCoordinates.length;
+ if (len == 2) {
+ flatCoordinates.push(0);
+ }
+ if (len === 0) {
+ return undefined;
+ }
+ return flatCoordinates;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>|undefined} Flat coordinates.
+ */
+ol.format.GML3.prototype.readFlatPosList_ = function(node, objectStack) {
+ var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, '');
+ var context = objectStack[0];
+ var containerSrs = context['srsName'];
+ var containerDimension = node.parentNode.getAttribute('srsDimension');
+ var axisOrientation = 'enu';
+ if (containerSrs) {
+ var proj = ol.proj.get(containerSrs);
+ axisOrientation = proj.getAxisOrientation();
+ }
+ var coords = s.split(/\s+/);
+ // The "dimension" attribute is from the GML 3.0.1 spec.
+ var dim = 2;
+ if (node.getAttribute('srsDimension')) {
+ dim = ol.format.XSD.readNonNegativeIntegerString(
+ node.getAttribute('srsDimension'));
+ } else if (node.getAttribute('dimension')) {
+ dim = ol.format.XSD.readNonNegativeIntegerString(
+ node.getAttribute('dimension'));
+ } else if (containerDimension) {
+ dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension);
+ }
+ var x, y, z;
+ var flatCoordinates = [];
+ for (var i = 0, ii = coords.length; i < ii; i += dim) {
+ x = parseFloat(coords[i]);
+ y = parseFloat(coords[i + 1]);
+ z = (dim === 3) ? parseFloat(coords[i + 2]) : 0;
+ if (axisOrientation.substr(0, 2) === 'en') {
+ flatCoordinates.push(x, y, z);
+ } else {
+ flatCoordinates.push(y, x, z);
+ }
+ }
+ return flatCoordinates;
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'pos': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPos_),
+ 'posList': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPosList_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'interior': ol.format.GML3.prototype.interiorParser_,
+ 'exterior': ol.format.GML3.prototype.exteriorParser_
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.GEOMETRY_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint),
+ 'MultiPoint': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readMultiPoint),
+ 'LineString': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readLineString),
+ 'MultiLineString': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readMultiLineString),
+ 'LinearRing' : ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readLinearRing),
+ 'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon),
+ 'MultiPolygon': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readMultiPolygon),
+ 'Surface': ol.xml.makeReplacer(ol.format.GML3.prototype.readSurface_),
+ 'MultiSurface': ol.xml.makeReplacer(
+ ol.format.GML3.prototype.readMultiSurface_),
+ 'Curve': ol.xml.makeReplacer(ol.format.GML3.prototype.readCurve_),
+ 'MultiCurve': ol.xml.makeReplacer(
+ ol.format.GML3.prototype.readMultiCurve_),
+ 'Envelope': ol.xml.makeReplacer(ol.format.GML3.prototype.readEnvelope_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.MULTICURVE_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'curveMember': ol.xml.makeArrayPusher(
+ ol.format.GML3.prototype.curveMemberParser_),
+ 'curveMembers': ol.xml.makeArrayPusher(
+ ol.format.GML3.prototype.curveMemberParser_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.MULTISURFACE_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'surfaceMember': ol.xml.makeArrayPusher(
+ ol.format.GML3.prototype.surfaceMemberParser_),
+ 'surfaceMembers': ol.xml.makeArrayPusher(
+ ol.format.GML3.prototype.surfaceMemberParser_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.CURVEMEMBER_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'LineString': ol.xml.makeArrayPusher(
+ ol.format.GMLBase.prototype.readLineString),
+ 'Curve': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readCurve_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.SURFACEMEMBER_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'Polygon': ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readPolygon),
+ 'Surface': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readSurface_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.SURFACE_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'patches': ol.xml.makeReplacer(ol.format.GML3.prototype.readPatch_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.CURVE_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'segments': ol.xml.makeReplacer(ol.format.GML3.prototype.readSegment_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.ENVELOPE_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'lowerCorner': ol.xml.makeArrayPusher(
+ ol.format.GML3.prototype.readFlatPosList_),
+ 'upperCorner': ol.xml.makeArrayPusher(
+ ol.format.GML3.prototype.readFlatPosList_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.PATCHES_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'PolygonPatch': ol.xml.makeReplacer(
+ ol.format.GML3.prototype.readPolygonPatch_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML3.prototype.SEGMENTS_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'LineStringSegment': ol.xml.makeReplacer(
+ ol.format.GML3.prototype.readLineStringSegment_)
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Point} value Point geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ var axisOrientation = 'enu';
+ if (srsName) {
+ axisOrientation = ol.proj.get(srsName).getAxisOrientation();
+ }
+ var point = value.getCoordinates();
+ var coords;
+ // only 2d for simple features profile
+ if (axisOrientation.substr(0, 2) === 'en') {
+ coords = (point[0] + ' ' + point[1]);
+ } else {
+ coords = (point[1] + ' ' + point[0]);
+ }
+ ol.format.XSD.writeStringTextNode(node, coords);
+};
+
+
+/**
+ * @param {Array.<number>} point Point geometry.
+ * @param {string=} opt_srsName Optional srsName
+ * @return {string} The coords string.
+ * @private
+ */
+ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName) {
+ var axisOrientation = 'enu';
+ if (opt_srsName) {
+ axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation();
+ }
+ return ((axisOrientation.substr(0, 2) === 'en') ?
+ point[0] + ' ' + point[1] :
+ point[1] + ' ' + point[0]);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LineString|ol.geom.LinearRing} value Geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writePosList_ = function(node, value, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ // only 2d for simple features profile
+ var points = value.getCoordinates();
+ var len = points.length;
+ var parts = new Array(len);
+ var point;
+ for (var i = 0; i < len; ++i) {
+ point = points[i];
+ parts[i] = this.getCoords_(point, srsName);
+ }
+ ol.format.XSD.writeStringTextNode(node, parts.join(' '));
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Point} geometry Point geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writePoint_ = function(node, geometry, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ var pos = ol.xml.createElementNS(node.namespaceURI, 'pos');
+ node.appendChild(pos);
+ this.writePos_(pos, geometry, objectStack);
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GML3.ENVELOPE_SERIALIZERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'lowerCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'upperCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Extent} extent Extent.
+ * @param {Array.<*>} objectStack Node stack.
+ */
+ol.format.GML3.prototype.writeEnvelope = function(node, extent, objectStack) {
+ ol.DEBUG && console.assert(extent.length == 4, 'extent should have 4 items');
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ var keys = ['lowerCorner', 'upperCorner'];
+ var values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]];
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */
+ ({node: node}), ol.format.GML3.ENVELOPE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
+ values,
+ objectStack, keys, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LinearRing} geometry LinearRing geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeLinearRing_ = function(node, geometry, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ var posList = ol.xml.createElementNS(node.namespaceURI, 'posList');
+ node.appendChild(posList);
+ this.writePosList_(posList, geometry, objectStack);
+};
+
+
+/**
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node} Node.
+ * @private
+ */
+ol.format.GML3.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
+ var context = objectStack[objectStack.length - 1];
+ var parentNode = context.node;
+ var exteriorWritten = context['exteriorWritten'];
+ if (exteriorWritten === undefined) {
+ context['exteriorWritten'] = true;
+ }
+ return ol.xml.createElementNS(parentNode.namespaceURI,
+ exteriorWritten !== undefined ? 'interior' : 'exterior');
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Polygon} geometry Polygon geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ if (node.nodeName !== 'PolygonPatch' && srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') {
+ var rings = geometry.getLinearRings();
+ ol.xml.pushSerializeAndPop(
+ {node: node, srsName: srsName},
+ ol.format.GML3.RING_SERIALIZERS_,
+ this.RING_NODE_FACTORY_,
+ rings, objectStack, undefined, this);
+ } else if (node.nodeName === 'Surface') {
+ var patches = ol.xml.createElementNS(node.namespaceURI, 'patches');
+ node.appendChild(patches);
+ this.writeSurfacePatches_(
+ patches, geometry, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LineString} geometry LineString geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeCurveOrLineString_ = function(node, geometry, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ if (node.nodeName !== 'LineStringSegment' && srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ if (node.nodeName === 'LineString' ||
+ node.nodeName === 'LineStringSegment') {
+ var posList = ol.xml.createElementNS(node.namespaceURI, 'posList');
+ node.appendChild(posList);
+ this.writePosList_(posList, geometry, objectStack);
+ } else if (node.nodeName === 'Curve') {
+ var segments = ol.xml.createElementNS(node.namespaceURI, 'segments');
+ node.appendChild(segments);
+ this.writeCurveSegments_(segments,
+ geometry, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ var surface = context['surface'];
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ var polygons = geometry.getPolygons();
+ ol.xml.pushSerializeAndPop({node: node, srsName: srsName, surface: surface},
+ ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_,
+ this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons,
+ objectStack, undefined, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.MultiPoint} geometry MultiPoint geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeMultiPoint_ = function(node, geometry,
+ objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ var points = geometry.getPoints();
+ ol.xml.pushSerializeAndPop({node: node, srsName: srsName},
+ ol.format.GML3.POINTMEMBER_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('pointMember'), points,
+ objectStack, undefined, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.MultiLineString} geometry MultiLineString geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var srsName = context['srsName'];
+ var curve = context['curve'];
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ var lines = geometry.getLineStrings();
+ ol.xml.pushSerializeAndPop({node: node, srsName: srsName, curve: curve},
+ ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_,
+ this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines,
+ objectStack, undefined, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LinearRing} ring LinearRing geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeRing_ = function(node, ring, objectStack) {
+ var linearRing = ol.xml.createElementNS(node.namespaceURI, 'LinearRing');
+ node.appendChild(linearRing);
+ this.writeLinearRing_(linearRing, ring, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Polygon} polygon Polygon geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeSurfaceOrPolygonMember_ = function(node, polygon, objectStack) {
+ var child = this.GEOMETRY_NODE_FACTORY_(
+ polygon, objectStack);
+ if (child) {
+ node.appendChild(child);
+ this.writeSurfaceOrPolygon_(child, polygon, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Point} point Point geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writePointMember_ = function(node, point, objectStack) {
+ var child = ol.xml.createElementNS(node.namespaceURI, 'Point');
+ node.appendChild(child);
+ this.writePoint_(child, point, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LineString} line LineString geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeLineStringOrCurveMember_ = function(node, line, objectStack) {
+ var child = this.GEOMETRY_NODE_FACTORY_(line, objectStack);
+ if (child) {
+ node.appendChild(child);
+ this.writeCurveOrLineString_(child, line, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Polygon} polygon Polygon geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeSurfacePatches_ = function(node, polygon, objectStack) {
+ var child = ol.xml.createElementNS(node.namespaceURI, 'PolygonPatch');
+ node.appendChild(child);
+ this.writeSurfaceOrPolygon_(child, polygon, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LineString} line LineString geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeCurveSegments_ = function(node, line, objectStack) {
+ var child = ol.xml.createElementNS(node.namespaceURI,
+ 'LineStringSegment');
+ node.appendChild(child);
+ this.writeCurveOrLineString_(child, line, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Geometry|ol.Extent} geometry Geometry.
+ * @param {Array.<*>} objectStack Node stack.
+ */
+ol.format.GML3.prototype.writeGeometryElement = function(node, geometry, objectStack) {
+ var context = /** @type {olx.format.WriteOptions} */ (objectStack[objectStack.length - 1]);
+ var item = ol.obj.assign({}, context);
+ item.node = node;
+ var value;
+ if (Array.isArray(geometry)) {
+ if (context.dataProjection) {
+ value = ol.proj.transformExtent(
+ geometry, context.featureProjection, context.dataProjection);
+ } else {
+ value = geometry;
+ }
+ } else {
+ value =
+ ol.format.Feature.transformWithOptions(/** @type {ol.geom.Geometry} */ (geometry), true, context);
+ }
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */
+ (item), ol.format.GML3.GEOMETRY_SERIALIZERS_,
+ this.GEOMETRY_NODE_FACTORY_, [value],
+ objectStack, undefined, this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Node stack.
+ */
+ol.format.GML3.prototype.writeFeatureElement = function(node, feature, objectStack) {
+ var fid = feature.getId();
+ if (fid) {
+ node.setAttribute('fid', fid);
+ }
+ var context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var featureNS = context['featureNS'];
+ var geometryName = feature.getGeometryName();
+ if (!context.serializers) {
+ context.serializers = {};
+ context.serializers[featureNS] = {};
+ }
+ var properties = feature.getProperties();
+ var keys = [], values = [];
+ for (var key in properties) {
+ var value = properties[key];
+ if (value !== null) {
+ keys.push(key);
+ values.push(value);
+ if (key == geometryName || value instanceof ol.geom.Geometry) {
+ if (!(key in context.serializers[featureNS])) {
+ context.serializers[featureNS][key] = ol.xml.makeChildAppender(
+ this.writeGeometryElement, this);
+ }
+ } else {
+ if (!(key in context.serializers[featureNS])) {
+ context.serializers[featureNS][key] = ol.xml.makeChildAppender(
+ ol.format.XSD.writeStringTextNode);
+ }
+ }
+ }
+ }
+ var item = ol.obj.assign({}, context);
+ item.node = node;
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */
+ (item), context.serializers,
+ ol.xml.makeSimpleNodeFactory(undefined, featureNS),
+ values,
+ objectStack, keys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GML3.prototype.writeFeatureMembers_ = function(node, features, objectStack) {
+ var context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var featureType = context['featureType'];
+ var featureNS = context['featureNS'];
+ var serializers = {};
+ serializers[featureNS] = {};
+ serializers[featureNS][featureType] = ol.xml.makeChildAppender(
+ this.writeFeatureElement, this);
+ var item = ol.obj.assign({}, context);
+ item.node = node;
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */
+ (item),
+ serializers,
+ ol.xml.makeSimpleNodeFactory(featureType, featureNS), features,
+ objectStack);
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'surfaceMember': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeSurfaceOrPolygonMember_),
+ 'polygonMember': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeSurfaceOrPolygonMember_)
+ }
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GML3.POINTMEMBER_SERIALIZERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'pointMember': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writePointMember_)
+ }
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'lineStringMember': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeLineStringOrCurveMember_),
+ 'curveMember': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeLineStringOrCurveMember_)
+ }
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GML3.RING_SERIALIZERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'exterior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_),
+ 'interior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_)
+ }
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GML3.GEOMETRY_SERIALIZERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'Curve': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeCurveOrLineString_),
+ 'MultiCurve': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeMultiCurveOrLineString_),
+ 'Point': ol.xml.makeChildAppender(ol.format.GML3.prototype.writePoint_),
+ 'MultiPoint': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeMultiPoint_),
+ 'LineString': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeCurveOrLineString_),
+ 'MultiLineString': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeMultiCurveOrLineString_),
+ 'LinearRing': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeLinearRing_),
+ 'Polygon': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeSurfaceOrPolygon_),
+ 'MultiPolygon': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_),
+ 'Surface': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeSurfaceOrPolygon_),
+ 'MultiSurface': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_),
+ 'Envelope': ol.xml.makeChildAppender(
+ ol.format.GML3.prototype.writeEnvelope)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, string>}
+ * @private
+ */
+ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_ = {
+ 'MultiLineString': 'lineStringMember',
+ 'MultiCurve': 'curveMember',
+ 'MultiPolygon': 'polygonMember',
+ 'MultiSurface': 'surfaceMember'
+};
+
+
+/**
+ * @const
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node|undefined} Node.
+ * @private
+ */
+ol.format.GML3.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
+ var parentNode = objectStack[objectStack.length - 1].node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode),
+ 'parentNode should be a node');
+ return ol.xml.createElementNS('http://www.opengis.net/gml',
+ ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_[parentNode.nodeName]);
+};
+
+
+/**
+ * @const
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node|undefined} Node.
+ * @private
+ */
+ol.format.GML3.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
+ var context = objectStack[objectStack.length - 1];
+ var multiSurface = context['multiSurface'];
+ var surface = context['surface'];
+ var curve = context['curve'];
+ var multiCurve = context['multiCurve'];
+ var parentNode = objectStack[objectStack.length - 1].node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode),
+ 'parentNode should be a node');
+ var nodeName;
+ if (!Array.isArray(value)) {
+ nodeName = /** @type {ol.geom.Geometry} */ (value).getType();
+ if (nodeName === 'MultiPolygon' && multiSurface === true) {
+ nodeName = 'MultiSurface';
+ } else if (nodeName === 'Polygon' && surface === true) {
+ nodeName = 'Surface';
+ } else if (nodeName === 'LineString' && curve === true) {
+ nodeName = 'Curve';
+ } else if (nodeName === 'MultiLineString' && multiCurve === true) {
+ nodeName = 'MultiCurve';
+ }
+ } else {
+ nodeName = 'Envelope';
+ }
+ return ol.xml.createElementNS('http://www.opengis.net/gml',
+ nodeName);
+};
+
+
+/**
+ * Encode a geometry in GML 3.1.1 Simple Features.
+ *
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ * @api
+ */
+ol.format.GML3.prototype.writeGeometryNode = function(geometry, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ var geom = ol.xml.createElementNS('http://www.opengis.net/gml', 'geom');
+ var context = {node: geom, srsName: this.srsName,
+ curve: this.curve_, surface: this.surface_,
+ multiSurface: this.multiSurface_, multiCurve: this.multiCurve_};
+ if (opt_options) {
+ ol.obj.assign(context, opt_options);
+ }
+ this.writeGeometryElement(geom, geometry, [context]);
+ return geom;
+};
+
+
+/**
+ * Encode an array of features in GML 3.1.1 Simple Features.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {string} Result.
+ * @api stable
+ */
+ol.format.GML3.prototype.writeFeatures;
+
+
+/**
+ * Encode an array of features in the GML 3.1.1 format as an XML node.
+ *
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ * @api
+ */
+ol.format.GML3.prototype.writeFeaturesNode = function(features, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ var node = ol.xml.createElementNS('http://www.opengis.net/gml',
+ 'featureMembers');
+ ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance',
+ 'xsi:schemaLocation', this.schemaLocation);
+ var context = {
+ srsName: this.srsName,
+ curve: this.curve_,
+ surface: this.surface_,
+ multiSurface: this.multiSurface_,
+ multiCurve: this.multiCurve_,
+ featureNS: this.featureNS,
+ featureType: this.featureType
+ };
+ if (opt_options) {
+ ol.obj.assign(context, opt_options);
+ }
+ this.writeFeatureMembers_(node, features, [context]);
+ return node;
+};
+
+goog.provide('ol.format.GML');
+
+goog.require('ol.format.GML3');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the GML format
+ * version 3.1.1.
+ * Currently only supports GML 3.1.1 Simple Features profile.
+ *
+ * @constructor
+ * @param {olx.format.GMLOptions=} opt_options
+ * Optional configuration object.
+ * @extends {ol.format.GMLBase}
+ * @api stable
+ */
+ol.format.GML = ol.format.GML3;
+
+
+/**
+ * Encode an array of features in GML 3.1.1 Simple Features.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {string} Result.
+ * @api stable
+ */
+ol.format.GML.prototype.writeFeatures;
+
+
+/**
+ * Encode an array of features in the GML 3.1.1 format as an XML node.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ * @api
+ */
+ol.format.GML.prototype.writeFeaturesNode;
+
+goog.provide('ol.format.GML2');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.format.GMLBase');
+goog.require('ol.format.XSD');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the GML format,
+ * version 2.1.2.
+ *
+ * @constructor
+ * @param {olx.format.GMLOptions=} opt_options Optional configuration object.
+ * @extends {ol.format.GMLBase}
+ * @api
+ */
+ol.format.GML2 = function(opt_options) {
+ var options = /** @type {olx.format.GMLOptions} */
+ (opt_options ? opt_options : {});
+
+ ol.format.GMLBase.call(this, options);
+
+ this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][
+ 'featureMember'] =
+ ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal);
+
+ /**
+ * @inheritDoc
+ */
+ this.schemaLocation = options.schemaLocation ?
+ options.schemaLocation : ol.format.GML2.schemaLocation_;
+
+};
+ol.inherits(ol.format.GML2, ol.format.GMLBase);
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ol.format.GML2.schemaLocation_ = ol.format.GMLBase.GMLNS +
+ ' http://schemas.opengis.net/gml/2.1.2/feature.xsd';
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>|undefined} Flat coordinates.
+ */
+ol.format.GML2.prototype.readFlatCoordinates_ = function(node, objectStack) {
+ var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, '');
+ var context = /** @type {ol.XmlNodeStackItem} */ (objectStack[0]);
+ var containerSrs = context['srsName'];
+ var containerDimension = node.parentNode.getAttribute('srsDimension');
+ var axisOrientation = 'enu';
+ if (containerSrs) {
+ var proj = ol.proj.get(containerSrs);
+ if (proj) {
+ axisOrientation = proj.getAxisOrientation();
+ }
+ }
+ var coords = s.split(/[\s,]+/);
+ // The "dimension" attribute is from the GML 3.0.1 spec.
+ var dim = 2;
+ if (node.getAttribute('srsDimension')) {
+ dim = ol.format.XSD.readNonNegativeIntegerString(
+ node.getAttribute('srsDimension'));
+ } else if (node.getAttribute('dimension')) {
+ dim = ol.format.XSD.readNonNegativeIntegerString(
+ node.getAttribute('dimension'));
+ } else if (containerDimension) {
+ dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension);
+ }
+ var x, y, z;
+ var flatCoordinates = [];
+ for (var i = 0, ii = coords.length; i < ii; i += dim) {
+ x = parseFloat(coords[i]);
+ y = parseFloat(coords[i + 1]);
+ z = (dim === 3) ? parseFloat(coords[i + 2]) : 0;
+ if (axisOrientation.substr(0, 2) === 'en') {
+ flatCoordinates.push(x, y, z);
+ } else {
+ flatCoordinates.push(y, x, z);
+ }
+ }
+ return flatCoordinates;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Extent|undefined} Envelope.
+ */
+ol.format.GML2.prototype.readBox_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Box', 'localName should be Box');
+ /** @type {Array.<number>} */
+ var flatCoordinates = ol.xml.pushParseAndPop([null],
+ this.BOX_PARSERS_, node, objectStack, this);
+ return ol.extent.createOrUpdate(flatCoordinates[1][0],
+ flatCoordinates[1][1], flatCoordinates[1][3],
+ flatCoordinates[1][4]);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GML2.prototype.innerBoundaryIsParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'innerBoundaryIs',
+ 'localName should be innerBoundaryIs');
+ /** @type {Array.<number>|undefined} */
+ var flatLinearRing = ol.xml.pushParseAndPop(undefined,
+ this.RING_PARSERS, node, objectStack, this);
+ if (flatLinearRing) {
+ var flatLinearRings = /** @type {Array.<Array.<number>>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(flatLinearRings),
+ 'flatLinearRings should be an array');
+ ol.DEBUG && console.assert(flatLinearRings.length > 0,
+ 'flatLinearRings should have an array length larger than 0');
+ flatLinearRings.push(flatLinearRing);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GML2.prototype.outerBoundaryIsParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'outerBoundaryIs',
+ 'localName should be outerBoundaryIs');
+ /** @type {Array.<number>|undefined} */
+ var flatLinearRing = ol.xml.pushParseAndPop(undefined,
+ this.RING_PARSERS, node, objectStack, this);
+ if (flatLinearRing) {
+ var flatLinearRings = /** @type {Array.<Array.<number>>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(flatLinearRings),
+ 'flatLinearRings should be an array');
+ ol.DEBUG && console.assert(flatLinearRings.length > 0,
+ 'flatLinearRings should have an array length larger than 0');
+ flatLinearRings[0] = flatLinearRing;
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'coordinates': ol.xml.makeReplacer(
+ ol.format.GML2.prototype.readFlatCoordinates_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'innerBoundaryIs': ol.format.GML2.prototype.innerBoundaryIsParser_,
+ 'outerBoundaryIs': ol.format.GML2.prototype.outerBoundaryIsParser_
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML2.prototype.BOX_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'coordinates': ol.xml.makeArrayPusher(
+ ol.format.GML2.prototype.readFlatCoordinates_)
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GML2.prototype.GEOMETRY_PARSERS_ = {
+ 'http://www.opengis.net/gml' : {
+ 'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint),
+ 'MultiPoint': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readMultiPoint),
+ 'LineString': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readLineString),
+ 'MultiLineString': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readMultiLineString),
+ 'LinearRing' : ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readLinearRing),
+ 'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon),
+ 'MultiPolygon': ol.xml.makeReplacer(
+ ol.format.GMLBase.prototype.readMultiPolygon),
+ 'Box': ol.xml.makeReplacer(ol.format.GML2.prototype.readBox_)
+ }
+};
+
+goog.provide('ol.format.GPX');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.array');
+goog.require('ol.format.Feature');
+goog.require('ol.format.XMLFeature');
+goog.require('ol.format.XSD');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.Point');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the GPX format.
+ *
+ * @constructor
+ * @extends {ol.format.XMLFeature}
+ * @param {olx.format.GPXOptions=} opt_options Options.
+ * @api stable
+ */
+ol.format.GPX = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.XMLFeature.call(this);
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get('EPSG:4326');
+
+ /**
+ * @type {function(ol.Feature, Node)|undefined}
+ * @private
+ */
+ this.readExtensions_ = options.readExtensions;
+};
+ol.inherits(ol.format.GPX, ol.format.XMLFeature);
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.<string>}
+ */
+ol.format.GPX.NAMESPACE_URIS_ = [
+ null,
+ 'http://www.topografix.com/GPX/1/0',
+ 'http://www.topografix.com/GPX/1/1'
+];
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ol.format.GPX.SCHEMA_LOCATION_ = 'http://www.topografix.com/GPX/1/1 ' +
+ 'http://www.topografix.com/GPX/1/1/gpx.xsd';
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {ol.LayoutOptions} layoutOptions Layout options.
+ * @param {Node} node Node.
+ * @param {Object} values Values.
+ * @private
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.format.GPX.appendCoordinate_ = function(flatCoordinates, layoutOptions, node, values) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ flatCoordinates.push(
+ parseFloat(node.getAttribute('lon')),
+ parseFloat(node.getAttribute('lat')));
+ if ('ele' in values) {
+ flatCoordinates.push(/** @type {number} */ (values['ele']));
+ delete values['ele'];
+ layoutOptions.hasZ = true;
+ } else {
+ flatCoordinates.push(0);
+ }
+ if ('time' in values) {
+ flatCoordinates.push(/** @type {number} */ (values['time']));
+ delete values['time'];
+ layoutOptions.hasM = true;
+ } else {
+ flatCoordinates.push(0);
+ }
+ return flatCoordinates;
+};
+
+
+/**
+ * Choose GeometryLayout based on flags in layoutOptions and adjust flatCoordinates
+ * and ends arrays by shrinking them accordingly (removing unused zero entries).
+ *
+ * @param {ol.LayoutOptions} layoutOptions Layout options.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {Array.<number>=} ends Ends.
+ * @return {ol.geom.GeometryLayout} Layout.
+ */
+ol.format.GPX.applyLayoutOptions_ = function(layoutOptions, flatCoordinates, ends) {
+ var layout = ol.geom.GeometryLayout.XY;
+ var stride = 2;
+ if (layoutOptions.hasZ && layoutOptions.hasM) {
+ layout = ol.geom.GeometryLayout.XYZM;
+ stride = 4;
+ } else if (layoutOptions.hasZ) {
+ layout = ol.geom.GeometryLayout.XYZ;
+ stride = 3;
+ } else if (layoutOptions.hasM) {
+ layout = ol.geom.GeometryLayout.XYM;
+ stride = 3;
+ }
+ if (stride !== 4) {
+ var i, ii;
+ for (i = 0, ii = flatCoordinates.length / 4; i < ii; i++) {
+ flatCoordinates[i * stride] = flatCoordinates[i * 4];
+ flatCoordinates[i * stride + 1] = flatCoordinates[i * 4 + 1];
+ if (layoutOptions.hasZ) {
+ flatCoordinates[i * stride + 2] = flatCoordinates[i * 4 + 2];
+ }
+ if (layoutOptions.hasM) {
+ flatCoordinates[i * stride + 2] = flatCoordinates[i * 4 + 3];
+ }
+ }
+ flatCoordinates.length = flatCoordinates.length / 4 * stride;
+ if (ends) {
+ for (i = 0, ii = ends.length; i < ii; i++) {
+ ends[i] = ends[i] / 4 * stride;
+ }
+ }
+ }
+ return layout;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseLink_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'link', 'localName should be link');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var href = node.getAttribute('href');
+ if (href !== null) {
+ values['link'] = href;
+ }
+ ol.xml.parseNode(ol.format.GPX.LINK_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseExtensions_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'extensions',
+ 'localName should be extensions');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ values['extensionsNode_'] = node;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseRtePt_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'rtept', 'localName should be rtept');
+ var values = ol.xml.pushParseAndPop(
+ {}, ol.format.GPX.RTEPT_PARSERS_, node, objectStack);
+ if (values) {
+ var rteValues = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var flatCoordinates = /** @type {Array.<number>} */
+ (rteValues['flatCoordinates']);
+ var layoutOptions = /** @type {ol.LayoutOptions} */
+ (rteValues['layoutOptions']);
+ ol.format.GPX.appendCoordinate_(flatCoordinates, layoutOptions, node, values);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseTrkPt_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'trkpt', 'localName should be trkpt');
+ var values = ol.xml.pushParseAndPop(
+ {}, ol.format.GPX.TRKPT_PARSERS_, node, objectStack);
+ if (values) {
+ var trkValues = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var flatCoordinates = /** @type {Array.<number>} */
+ (trkValues['flatCoordinates']);
+ var layoutOptions = /** @type {ol.LayoutOptions} */
+ (trkValues['layoutOptions']);
+ ol.format.GPX.appendCoordinate_(flatCoordinates, layoutOptions, node, values);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseTrkSeg_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'trkseg',
+ 'localName should be trkseg');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ ol.xml.parseNode(ol.format.GPX.TRKSEG_PARSERS_, node, objectStack);
+ var flatCoordinates = /** @type {Array.<number>} */
+ (values['flatCoordinates']);
+ var ends = /** @type {Array.<number>} */ (values['ends']);
+ ends.push(flatCoordinates.length);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Track.
+ */
+ol.format.GPX.readRte_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'rte', 'localName should be rte');
+ var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
+ var values = ol.xml.pushParseAndPop({
+ 'flatCoordinates': [],
+ 'layoutOptions': {}
+ }, ol.format.GPX.RTE_PARSERS_, node, objectStack);
+ if (!values) {
+ return undefined;
+ }
+ var flatCoordinates = /** @type {Array.<number>} */
+ (values['flatCoordinates']);
+ delete values['flatCoordinates'];
+ var layoutOptions = /** @type {ol.LayoutOptions} */ (values['layoutOptions']);
+ delete values['layoutOptions'];
+ var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, flatCoordinates);
+ var geometry = new ol.geom.LineString(null);
+ geometry.setFlatCoordinates(layout, flatCoordinates);
+ ol.format.Feature.transformWithOptions(geometry, false, options);
+ var feature = new ol.Feature(geometry);
+ feature.setProperties(values);
+ return feature;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Track.
+ */
+ol.format.GPX.readTrk_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'trk', 'localName should be trk');
+ var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
+ var values = ol.xml.pushParseAndPop({
+ 'flatCoordinates': [],
+ 'ends': [],
+ 'layoutOptions': {}
+ }, ol.format.GPX.TRK_PARSERS_, node, objectStack);
+ if (!values) {
+ return undefined;
+ }
+ var flatCoordinates = /** @type {Array.<number>} */
+ (values['flatCoordinates']);
+ delete values['flatCoordinates'];
+ var ends = /** @type {Array.<number>} */ (values['ends']);
+ delete values['ends'];
+ var layoutOptions = /** @type {ol.LayoutOptions} */ (values['layoutOptions']);
+ delete values['layoutOptions'];
+ var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, flatCoordinates, ends);
+ var geometry = new ol.geom.MultiLineString(null);
+ geometry.setFlatCoordinates(layout, flatCoordinates, ends);
+ ol.format.Feature.transformWithOptions(geometry, false, options);
+ var feature = new ol.Feature(geometry);
+ feature.setProperties(values);
+ return feature;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Waypoint.
+ */
+ol.format.GPX.readWpt_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'wpt', 'localName should be wpt');
+ var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
+ var values = ol.xml.pushParseAndPop(
+ {}, ol.format.GPX.WPT_PARSERS_, node, objectStack);
+ if (!values) {
+ return undefined;
+ }
+ var layoutOptions = /** @type {ol.LayoutOptions} */ ({});
+ var coordinates = ol.format.GPX.appendCoordinate_([], layoutOptions, node, values);
+ var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, coordinates);
+ var geometry = new ol.geom.Point(coordinates, layout);
+ ol.format.Feature.transformWithOptions(geometry, false, options);
+ var feature = new ol.Feature(geometry);
+ feature.setProperties(values);
+ return feature;
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, function(Node, Array.<*>): (ol.Feature|undefined)>}
+ * @private
+ */
+ol.format.GPX.FEATURE_READER_ = {
+ 'rte': ol.format.GPX.readRte_,
+ 'trk': ol.format.GPX.readTrk_,
+ 'wpt': ol.format.GPX.readWpt_
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.GPX_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'rte': ol.xml.makeArrayPusher(ol.format.GPX.readRte_),
+ 'trk': ol.xml.makeArrayPusher(ol.format.GPX.readTrk_),
+ 'wpt': ol.xml.makeArrayPusher(ol.format.GPX.readWpt_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.LINK_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'text':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkText'),
+ 'type':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkType')
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.RTE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'link': ol.format.GPX.parseLink_,
+ 'number':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
+ 'extensions': ol.format.GPX.parseExtensions_,
+ 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'rtept': ol.format.GPX.parseRtePt_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.RTEPT_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.TRK_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'link': ol.format.GPX.parseLink_,
+ 'number':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
+ 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'extensions': ol.format.GPX.parseExtensions_,
+ 'trkseg': ol.format.GPX.parseTrkSeg_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.TRKSEG_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'trkpt': ol.format.GPX.parseTrkPt_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.TRKPT_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.GPX.WPT_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime),
+ 'magvar': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'geoidheight': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'link': ol.format.GPX.parseLink_,
+ 'sym': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'fix': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'sat': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'hdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'vdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'pdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'ageofdgpsdata':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'dgpsid':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
+ 'extensions': ol.format.GPX.parseExtensions_
+ });
+
+
+/**
+ * @param {Array.<ol.Feature>} features List of features.
+ * @private
+ */
+ol.format.GPX.prototype.handleReadExtensions_ = function(features) {
+ if (!features) {
+ features = [];
+ }
+ for (var i = 0, ii = features.length; i < ii; ++i) {
+ var feature = features[i];
+ if (this.readExtensions_) {
+ var extensionsNode = feature.get('extensionsNode_') || null;
+ this.readExtensions_(feature, extensionsNode);
+ }
+ feature.set('extensionsNode_', undefined);
+ }
+};
+
+
+/**
+ * Read the first feature from a GPX source.
+ * Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`)
+ * into MultiLineString. Any properties on route and track waypoints are ignored.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api stable
+ */
+ol.format.GPX.prototype.readFeature;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GPX.prototype.readFeatureFromNode = function(node, opt_options) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ if (!ol.array.includes(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) {
+ return null;
+ }
+ var featureReader = ol.format.GPX.FEATURE_READER_[node.localName];
+ if (!featureReader) {
+ return null;
+ }
+ var feature = featureReader(node, [this.getReadOptions(node, opt_options)]);
+ if (!feature) {
+ return null;
+ }
+ this.handleReadExtensions_([feature]);
+ return feature;
+};
+
+
+/**
+ * Read all features from a GPX source.
+ * Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`)
+ * into MultiLineString. Any properties on route and track waypoints are ignored.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.GPX.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GPX.prototype.readFeaturesFromNode = function(node, opt_options) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ if (!ol.array.includes(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) {
+ return [];
+ }
+ if (node.localName == 'gpx') {
+ /** @type {Array.<ol.Feature>} */
+ var features = ol.xml.pushParseAndPop([], ol.format.GPX.GPX_PARSERS_,
+ node, [this.getReadOptions(node, opt_options)]);
+ if (features) {
+ this.handleReadExtensions_(features);
+ return features;
+ } else {
+ return [];
+ }
+ }
+ return [];
+};
+
+
+/**
+ * Read the projection from a GPX source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.GPX.prototype.readProjection;
+
+
+/**
+ * @param {Node} node Node.
+ * @param {string} value Value for the link's `href` attribute.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.GPX.writeLink_ = function(node, value, objectStack) {
+ node.setAttribute('href', value);
+ var context = objectStack[objectStack.length - 1];
+ var properties = context['properties'];
+ var link = [
+ properties['linkText'],
+ properties['linkType']
+ ];
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ({node: node}),
+ ol.format.GPX.LINK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
+ link, objectStack, ol.format.GPX.LINK_SEQUENCE_);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.writeWptType_ = function(node, coordinate, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var parentNode = context.node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode),
+ 'parentNode should be an XML node');
+ var namespaceURI = parentNode.namespaceURI;
+ var properties = context['properties'];
+ //FIXME Projection handling
+ ol.xml.setAttributeNS(node, null, 'lat', coordinate[1]);
+ ol.xml.setAttributeNS(node, null, 'lon', coordinate[0]);
+ var geometryLayout = context['geometryLayout'];
+ switch (geometryLayout) {
+ case ol.geom.GeometryLayout.XYZM:
+ if (coordinate[3] !== 0) {
+ properties['time'] = coordinate[3];
+ }
+ // fall through
+ case ol.geom.GeometryLayout.XYZ:
+ if (coordinate[2] !== 0) {
+ properties['ele'] = coordinate[2];
+ }
+ break;
+ case ol.geom.GeometryLayout.XYM:
+ if (coordinate[2] !== 0) {
+ properties['time'] = coordinate[2];
+ }
+ break;
+ default:
+ // pass
+ }
+ var orderedKeys = (node.nodeName == 'rtept') ?
+ ol.format.GPX.RTEPT_TYPE_SEQUENCE_[namespaceURI] :
+ ol.format.GPX.WPT_TYPE_SEQUENCE_[namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */
+ ({node: node, 'properties': properties}),
+ ol.format.GPX.WPT_TYPE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
+ values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.writeRte_ = function(node, feature, objectStack) {
+ var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
+ var properties = feature.getProperties();
+ var context = {node: node, 'properties': properties};
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ geometry = /** @type {ol.geom.LineString} */
+ (ol.format.Feature.transformWithOptions(geometry, true, options));
+ context['geometryLayout'] = geometry.getLayout();
+ properties['rtept'] = geometry.getCoordinates();
+ }
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.GPX.RTE_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.GPX.RTE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
+ values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.writeTrk_ = function(node, feature, objectStack) {
+ var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
+ var properties = feature.getProperties();
+ /** @type {ol.XmlNodeStackItem} */
+ var context = {node: node, 'properties': properties};
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ geometry = /** @type {ol.geom.MultiLineString} */
+ (ol.format.Feature.transformWithOptions(geometry, true, options));
+ properties['trkseg'] = geometry.getLineStrings();
+ }
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.GPX.TRK_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.GPX.TRK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
+ values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LineString} lineString LineString.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.writeTrkSeg_ = function(node, lineString, objectStack) {
+ /** @type {ol.XmlNodeStackItem} */
+ var context = {node: node, 'geometryLayout': lineString.getLayout(),
+ 'properties': {}};
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.GPX.TRKSEG_SERIALIZERS_, ol.format.GPX.TRKSEG_NODE_FACTORY_,
+ lineString.getCoordinates(), objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.writeWpt_ = function(node, feature, objectStack) {
+ var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
+ var context = objectStack[objectStack.length - 1];
+ context['properties'] = feature.getProperties();
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ geometry = /** @type {ol.geom.Point} */
+ (ol.format.Feature.transformWithOptions(geometry, true, options));
+ context['geometryLayout'] = geometry.getLayout();
+ ol.format.GPX.writeWptType_(node, geometry.getCoordinates(), objectStack);
+ }
+};
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.GPX.LINK_SEQUENCE_ = ['text', 'type'];
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GPX.LINK_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'text': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.GPX.RTE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, [
+ 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'rtept'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GPX.RTE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
+ 'number': ol.xml.makeChildAppender(
+ ol.format.XSD.writeNonNegativeIntegerTextNode),
+ 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'rtept': ol.xml.makeArraySerializer(ol.xml.makeChildAppender(
+ ol.format.GPX.writeWptType_))
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.GPX.RTEPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, [
+ 'ele', 'time'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.GPX.TRK_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, [
+ 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'trkseg'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GPX.TRK_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
+ 'number': ol.xml.makeChildAppender(
+ ol.format.XSD.writeNonNegativeIntegerTextNode),
+ 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'trkseg': ol.xml.makeArraySerializer(ol.xml.makeChildAppender(
+ ol.format.GPX.writeTrkSeg_))
+ });
+
+
+/**
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.GPX.TRKSEG_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('trkpt');
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GPX.TRKSEG_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'trkpt': ol.xml.makeChildAppender(ol.format.GPX.writeWptType_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.GPX.WPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, [
+ 'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src',
+ 'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop',
+ 'ageofdgpsdata', 'dgpsid'
+ ]);
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GPX.WPT_TYPE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'time': ol.xml.makeChildAppender(ol.format.XSD.writeDateTimeTextNode),
+ 'magvar': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'geoidheight': ol.xml.makeChildAppender(
+ ol.format.XSD.writeDecimalTextNode),
+ 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
+ 'sym': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'fix': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'sat': ol.xml.makeChildAppender(
+ ol.format.XSD.writeNonNegativeIntegerTextNode),
+ 'hdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'vdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'pdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'ageofdgpsdata': ol.xml.makeChildAppender(
+ ol.format.XSD.writeDecimalTextNode),
+ 'dgpsid': ol.xml.makeChildAppender(
+ ol.format.XSD.writeNonNegativeIntegerTextNode)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, string>}
+ * @private
+ */
+ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_ = {
+ 'Point': 'wpt',
+ 'LineString': 'rte',
+ 'MultiLineString': 'trk'
+};
+
+
+/**
+ * @const
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node|undefined} Node.
+ * @private
+ */
+ol.format.GPX.GPX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
+ var geometry = /** @type {ol.Feature} */ (value).getGeometry();
+ if (geometry) {
+ var nodeName = ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_[geometry.getType()];
+ if (nodeName) {
+ var parentNode = objectStack[objectStack.length - 1].node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode),
+ 'parentNode should be an XML node');
+ return ol.xml.createElementNS(parentNode.namespaceURI, nodeName);
+ }
+ }
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.GPX.GPX_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'rte': ol.xml.makeChildAppender(ol.format.GPX.writeRte_),
+ 'trk': ol.xml.makeChildAppender(ol.format.GPX.writeTrk_),
+ 'wpt': ol.xml.makeChildAppender(ol.format.GPX.writeWpt_)
+ });
+
+
+/**
+ * Encode an array of features in the GPX format.
+ * LineString geometries are output as routes (`<rte>`), and MultiLineString
+ * as tracks (`<trk>`).
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} Result.
+ * @api stable
+ */
+ol.format.GPX.prototype.writeFeatures;
+
+
+/**
+ * Encode an array of features in the GPX format as an XML node.
+ * LineString geometries are output as routes (`<rte>`), and MultiLineString
+ * as tracks (`<trk>`).
+ *
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ * @api
+ */
+ol.format.GPX.prototype.writeFeaturesNode = function(features, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ //FIXME Serialize metadata
+ var gpx = ol.xml.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx');
+ var xmlnsUri = 'http://www.w3.org/2000/xmlns/';
+ var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance';
+ ol.xml.setAttributeNS(gpx, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri);
+ ol.xml.setAttributeNS(gpx, xmlSchemaInstanceUri, 'xsi:schemaLocation',
+ ol.format.GPX.SCHEMA_LOCATION_);
+ gpx.setAttribute('version', '1.1');
+ gpx.setAttribute('creator', 'OpenLayers 3');
+
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */
+ ({node: gpx}), ol.format.GPX.GPX_SERIALIZERS_,
+ ol.format.GPX.GPX_NODE_FACTORY_, features, [opt_options]);
+ return gpx;
+};
+
+goog.provide('ol.format.TextFeature');
+
+goog.require('ol');
+goog.require('ol.format.Feature');
+goog.require('ol.format.FormatType');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for text feature formats.
+ *
+ * @constructor
+ * @extends {ol.format.Feature}
+ */
+ol.format.TextFeature = function() {
+ ol.format.Feature.call(this);
+};
+ol.inherits(ol.format.TextFeature, ol.format.Feature);
+
+
+/**
+ * @param {Document|Node|Object|string} source Source.
+ * @private
+ * @return {string} Text.
+ */
+ol.format.TextFeature.prototype.getText_ = function(source) {
+ if (typeof source === 'string') {
+ return source;
+ } else {
+ return '';
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.getType = function() {
+ return ol.format.FormatType.TEXT;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.readFeature = function(source, opt_options) {
+ return this.readFeatureFromText(
+ this.getText_(source), this.adaptOptions(opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {string} text Text.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @protected
+ * @return {ol.Feature} Feature.
+ */
+ol.format.TextFeature.prototype.readFeatureFromText = function(text, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.readFeatures = function(source, opt_options) {
+ return this.readFeaturesFromText(
+ this.getText_(source), this.adaptOptions(opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {string} text Text.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @protected
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.format.TextFeature.prototype.readFeaturesFromText = function(text, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.readGeometry = function(source, opt_options) {
+ return this.readGeometryFromText(
+ this.getText_(source), this.adaptOptions(opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {string} text Text.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @protected
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.TextFeature.prototype.readGeometryFromText = function(text, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.readProjection = function(source) {
+ return this.readProjectionFromText(this.getText_(source));
+};
+
+
+/**
+ * @param {string} text Text.
+ * @protected
+ * @return {ol.proj.Projection} Projection.
+ */
+ol.format.TextFeature.prototype.readProjectionFromText = function(text) {
+ return this.defaultDataProjection;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.writeFeature = function(feature, opt_options) {
+ return this.writeFeatureText(feature, this.adaptOptions(opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {ol.Feature} feature Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @protected
+ * @return {string} Text.
+ */
+ol.format.TextFeature.prototype.writeFeatureText = function(feature, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.writeFeatures = function(
+ features, opt_options) {
+ return this.writeFeaturesText(features, this.adaptOptions(opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @protected
+ * @return {string} Text.
+ */
+ol.format.TextFeature.prototype.writeFeaturesText = function(features, opt_options) {};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TextFeature.prototype.writeGeometry = function(
+ geometry, opt_options) {
+ return this.writeGeometryText(geometry, this.adaptOptions(opt_options));
+};
+
+
+/**
+ * @abstract
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @protected
+ * @return {string} Text.
+ */
+ol.format.TextFeature.prototype.writeGeometryText = function(geometry, opt_options) {};
+
+goog.provide('ol.format.IGC');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.TextFeature');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Feature format for `*.igc` flight recording files.
+ *
+ * @constructor
+ * @extends {ol.format.TextFeature}
+ * @param {olx.format.IGCOptions=} opt_options Options.
+ * @api
+ */
+ol.format.IGC = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.TextFeature.call(this);
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get('EPSG:4326');
+
+ /**
+ * @private
+ * @type {ol.format.IGC.Z}
+ */
+ this.altitudeMode_ = options.altitudeMode ?
+ options.altitudeMode : ol.format.IGC.Z.NONE;
+
+};
+ol.inherits(ol.format.IGC, ol.format.TextFeature);
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.IGC.EXTENSIONS_ = ['.igc'];
+
+
+/**
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.format.IGC.B_RECORD_RE_ =
+ /^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/;
+
+
+/**
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.format.IGC.H_RECORD_RE_ = /^H.([A-Z]{3}).*?:(.*)/;
+
+
+/**
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.format.IGC.HFDTE_RECORD_RE_ = /^HFDTE(\d{2})(\d{2})(\d{2})/;
+
+
+/**
+ * A regular expression matching the newline characters `\r\n`, `\r` and `\n`.
+ *
+ * @const
+ * @type {RegExp}
+ * @private
+ */
+ol.format.IGC.NEWLINE_RE_ = /\r\n|\r|\n/;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.IGC.prototype.getExtensions = function() {
+ return ol.format.IGC.EXTENSIONS_;
+};
+
+
+/**
+ * Read the feature from the IGC source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api
+ */
+ol.format.IGC.prototype.readFeature;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.IGC.prototype.readFeatureFromText = function(text, opt_options) {
+ var altitudeMode = this.altitudeMode_;
+ var lines = text.split(ol.format.IGC.NEWLINE_RE_);
+ /** @type {Object.<string, string>} */
+ var properties = {};
+ var flatCoordinates = [];
+ var year = 2000;
+ var month = 0;
+ var day = 1;
+ var lastDateTime = -1;
+ var i, ii;
+ for (i = 0, ii = lines.length; i < ii; ++i) {
+ var line = lines[i];
+ var m;
+ if (line.charAt(0) == 'B') {
+ m = ol.format.IGC.B_RECORD_RE_.exec(line);
+ if (m) {
+ var hour = parseInt(m[1], 10);
+ var minute = parseInt(m[2], 10);
+ var second = parseInt(m[3], 10);
+ var y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000;
+ if (m[6] == 'S') {
+ y = -y;
+ }
+ var x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000;
+ if (m[9] == 'W') {
+ x = -x;
+ }
+ flatCoordinates.push(x, y);
+ if (altitudeMode != ol.format.IGC.Z.NONE) {
+ var z;
+ if (altitudeMode == ol.format.IGC.Z.GPS) {
+ z = parseInt(m[11], 10);
+ } else if (altitudeMode == ol.format.IGC.Z.BAROMETRIC) {
+ z = parseInt(m[12], 10);
+ } else {
+ ol.DEBUG && console.assert(false, 'Unknown altitude mode.');
+ z = 0;
+ }
+ flatCoordinates.push(z);
+ }
+ var dateTime = Date.UTC(year, month, day, hour, minute, second);
+ // Detect UTC midnight wrap around.
+ if (dateTime < lastDateTime) {
+ dateTime = Date.UTC(year, month, day + 1, hour, minute, second);
+ }
+ flatCoordinates.push(dateTime / 1000);
+ lastDateTime = dateTime;
+ }
+ } else if (line.charAt(0) == 'H') {
+ m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line);
+ if (m) {
+ day = parseInt(m[1], 10);
+ month = parseInt(m[2], 10) - 1;
+ year = 2000 + parseInt(m[3], 10);
+ } else {
+ m = ol.format.IGC.H_RECORD_RE_.exec(line);
+ if (m) {
+ properties[m[1]] = m[2].trim();
+ }
+ }
+ }
+ }
+ if (flatCoordinates.length === 0) {
+ return null;
+ }
+ var lineString = new ol.geom.LineString(null);
+ var layout = altitudeMode == ol.format.IGC.Z.NONE ?
+ ol.geom.GeometryLayout.XYM : ol.geom.GeometryLayout.XYZM;
+ lineString.setFlatCoordinates(layout, flatCoordinates);
+ var feature = new ol.Feature(ol.format.Feature.transformWithOptions(
+ lineString, false, opt_options));
+ feature.setProperties(properties);
+ return feature;
+};
+
+
+/**
+ * Read the feature from the source. As IGC sources contain a single
+ * feature, this will return the feature in an array.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api
+ */
+ol.format.IGC.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.IGC.prototype.readFeaturesFromText = function(text, opt_options) {
+ var feature = this.readFeatureFromText(text, opt_options);
+ if (feature) {
+ return [feature];
+ } else {
+ return [];
+ }
+};
+
+
+/**
+ * Read the projection from the IGC source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api
+ */
+ol.format.IGC.prototype.readProjection;
+
+
+/**
+ * IGC altitude/z. One of 'barometric', 'gps', 'none'.
+ * @enum {string}
+ */
+ol.format.IGC.Z = {
+ BAROMETRIC: 'barometric',
+ GPS: 'gps',
+ NONE: 'none'
+};
+
+goog.provide('ol.style.IconImage');
+
+goog.require('ol');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.EventTarget');
+goog.require('ol.events.EventType');
+goog.require('ol.Image');
+goog.require('ol.style');
+
+
+/**
+ * @constructor
+ * @param {Image|HTMLCanvasElement} image Image.
+ * @param {string|undefined} src Src.
+ * @param {ol.Size} size Size.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.Image.State} imageState Image state.
+ * @param {ol.Color} color Color.
+ * @extends {ol.events.EventTarget}
+ */
+ol.style.IconImage = function(image, src, size, crossOrigin, imageState,
+ color) {
+
+ ol.events.EventTarget.call(this);
+
+ /**
+ * @private
+ * @type {Image|HTMLCanvasElement}
+ */
+ this.hitDetectionImage_ = null;
+
+ /**
+ * @private
+ * @type {Image|HTMLCanvasElement}
+ */
+ this.image_ = !image ? new Image() : image;
+
+ if (crossOrigin !== null) {
+ this.image_.crossOrigin = crossOrigin;
+ }
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = color ?
+ /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) :
+ null;
+
+ /**
+ * @private
+ * @type {ol.Color}
+ */
+ this.color_ = color;
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.imageListenerKeys_ = null;
+
+ /**
+ * @private
+ * @type {ol.Image.State}
+ */
+ this.imageState_ = imageState;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.size_ = size;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.tainting_ = false;
+ if (this.imageState_ == ol.Image.State.LOADED) {
+ this.determineTainting_();
+ }
+
+};
+ol.inherits(ol.style.IconImage, ol.events.EventTarget);
+
+
+/**
+ * @param {Image|HTMLCanvasElement} image Image.
+ * @param {string} src Src.
+ * @param {ol.Size} size Size.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.Image.State} imageState Image state.
+ * @param {ol.Color} color Color.
+ * @return {ol.style.IconImage} Icon image.
+ */
+ol.style.IconImage.get = function(image, src, size, crossOrigin, imageState,
+ color) {
+ var iconImageCache = ol.style.iconImageCache;
+ var iconImage = iconImageCache.get(src, crossOrigin, color);
+ if (!iconImage) {
+ iconImage = new ol.style.IconImage(
+ image, src, size, crossOrigin, imageState, color);
+ iconImageCache.set(src, crossOrigin, color, iconImage);
+ }
+ return iconImage;
+};
+
+
+/**
+ * @private
+ */
+ol.style.IconImage.prototype.determineTainting_ = function() {
+ var context = ol.dom.createCanvasContext2D(1, 1);
+ try {
+ context.drawImage(this.image_, 0, 0);
+ context.getImageData(0, 0, 1, 1);
+ } catch (e) {
+ this.tainting_ = true;
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.style.IconImage.prototype.dispatchChangeEvent_ = function() {
+ this.dispatchEvent(ol.events.EventType.CHANGE);
+};
+
+
+/**
+ * @private
+ */
+ol.style.IconImage.prototype.handleImageError_ = function() {
+ this.imageState_ = ol.Image.State.ERROR;
+ this.unlistenImage_();
+ this.dispatchChangeEvent_();
+};
+
+
+/**
+ * @private
+ */
+ol.style.IconImage.prototype.handleImageLoad_ = function() {
+ this.imageState_ = ol.Image.State.LOADED;
+ if (this.size_) {
+ this.image_.width = this.size_[0];
+ this.image_.height = this.size_[1];
+ }
+ this.size_ = [this.image_.width, this.image_.height];
+ this.unlistenImage_();
+ this.determineTainting_();
+ this.replaceColor_();
+ this.dispatchChangeEvent_();
+};
+
+
+/**
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {Image|HTMLCanvasElement} Image or Canvas element.
+ */
+ol.style.IconImage.prototype.getImage = function(pixelRatio) {
+ return this.canvas_ ? this.canvas_ : this.image_;
+};
+
+
+/**
+ * @return {ol.Image.State} Image state.
+ */
+ol.style.IconImage.prototype.getImageState = function() {
+ return this.imageState_;
+};
+
+
+/**
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {Image|HTMLCanvasElement} Image element.
+ */
+ol.style.IconImage.prototype.getHitDetectionImage = function(pixelRatio) {
+ if (!this.hitDetectionImage_) {
+ if (this.tainting_) {
+ var width = this.size_[0];
+ var height = this.size_[1];
+ var context = ol.dom.createCanvasContext2D(width, height);
+ context.fillRect(0, 0, width, height);
+ this.hitDetectionImage_ = context.canvas;
+ } else {
+ this.hitDetectionImage_ = this.image_;
+ }
+ }
+ return this.hitDetectionImage_;
+};
+
+
+/**
+ * @return {ol.Size} Image size.
+ */
+ol.style.IconImage.prototype.getSize = function() {
+ return this.size_;
+};
+
+
+/**
+ * @return {string|undefined} Image src.
+ */
+ol.style.IconImage.prototype.getSrc = function() {
+ return this.src_;
+};
+
+
+/**
+ * Load not yet loaded URI.
+ */
+ol.style.IconImage.prototype.load = function() {
+ if (this.imageState_ == ol.Image.State.IDLE) {
+ ol.DEBUG && console.assert(this.src_ !== undefined,
+ 'this.src_ must not be undefined');
+ ol.DEBUG && console.assert(!this.imageListenerKeys_,
+ 'no listener keys existing');
+ this.imageState_ = ol.Image.State.LOADING;
+ this.imageListenerKeys_ = [
+ ol.events.listenOnce(this.image_, ol.events.EventType.ERROR,
+ this.handleImageError_, this),
+ ol.events.listenOnce(this.image_, ol.events.EventType.LOAD,
+ this.handleImageLoad_, this)
+ ];
+ try {
+ this.image_.src = this.src_;
+ } catch (e) {
+ this.handleImageError_();
+ }
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.style.IconImage.prototype.replaceColor_ = function() {
+ if (this.tainting_ || this.color_ === null) {
+ return;
+ }
+
+ this.canvas_.width = this.image_.width;
+ this.canvas_.height = this.image_.height;
+
+ var ctx = this.canvas_.getContext('2d');
+ ctx.drawImage(this.image_, 0, 0);
+
+ var imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height);
+ var data = imgData.data;
+ var r = this.color_[0] / 255.0;
+ var g = this.color_[1] / 255.0;
+ var b = this.color_[2] / 255.0;
+
+ for (var i = 0, ii = data.length; i < ii; i += 4) {
+ data[i] *= r;
+ data[i + 1] *= g;
+ data[i + 2] *= b;
+ }
+ ctx.putImageData(imgData, 0, 0);
+};
+
+
+/**
+ * Discards event handlers which listen for load completion or errors.
+ *
+ * @private
+ */
+ol.style.IconImage.prototype.unlistenImage_ = function() {
+ this.imageListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.imageListenerKeys_ = null;
+};
+
+goog.provide('ol.style.Icon');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.color');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.Image');
+goog.require('ol.style.IconImage');
+goog.require('ol.style.Image');
+
+
+/**
+ * @classdesc
+ * Set icon style for vector features.
+ *
+ * @constructor
+ * @param {olx.style.IconOptions=} opt_options Options.
+ * @extends {ol.style.Image}
+ * @api
+ */
+ol.style.Icon = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.anchor_ = options.anchor !== undefined ? options.anchor : [0.5, 0.5];
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.normalizedAnchor_ = null;
+
+ /**
+ * @private
+ * @type {ol.style.Icon.Origin}
+ */
+ this.anchorOrigin_ = options.anchorOrigin !== undefined ?
+ options.anchorOrigin : ol.style.Icon.Origin.TOP_LEFT;
+
+ /**
+ * @private
+ * @type {ol.style.Icon.AnchorUnits}
+ */
+ this.anchorXUnits_ = options.anchorXUnits !== undefined ?
+ options.anchorXUnits : ol.style.Icon.AnchorUnits.FRACTION;
+
+ /**
+ * @private
+ * @type {ol.style.Icon.AnchorUnits}
+ */
+ this.anchorYUnits_ = options.anchorYUnits !== undefined ?
+ options.anchorYUnits : ol.style.Icon.AnchorUnits.FRACTION;
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ =
+ options.crossOrigin !== undefined ? options.crossOrigin : null;
+
+ /**
+ * @type {Image|HTMLCanvasElement}
+ */
+ var image = options.img !== undefined ? options.img : null;
+
+ /**
+ * @type {ol.Size}
+ */
+ var imgSize = options.imgSize !== undefined ? options.imgSize : null;
+
+ /**
+ * @type {string|undefined}
+ */
+ var src = options.src;
+
+ ol.asserts.assert(!(src !== undefined && image),
+ 4); // `image` and `src` cannot be provided at the same time
+ ol.asserts.assert(!image || (image && imgSize),
+ 5); // `imgSize` must be set when `image` is provided
+
+ if ((src === undefined || src.length === 0) && image) {
+ src = image.src || ol.getUid(image).toString();
+ }
+ ol.asserts.assert(src !== undefined && src.length > 0,
+ 6); // A defined and non-empty `src` or `image` must be provided
+
+ /**
+ * @type {ol.Image.State}
+ */
+ var imageState = options.src !== undefined ?
+ ol.Image.State.IDLE : ol.Image.State.LOADED;
+
+ /**
+ * @private
+ * @type {ol.Color}
+ */
+ this.color_ = options.color !== undefined ? ol.color.asArray(options.color) :
+ null;
+
+ /**
+ * @private
+ * @type {ol.style.IconImage}
+ */
+ this.iconImage_ = ol.style.IconImage.get(
+ image, /** @type {string} */ (src), imgSize, this.crossOrigin_, imageState, this.color_);
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.offset_ = options.offset !== undefined ? options.offset : [0, 0];
+
+ /**
+ * @private
+ * @type {ol.style.Icon.Origin}
+ */
+ this.offsetOrigin_ = options.offsetOrigin !== undefined ?
+ options.offsetOrigin : ol.style.Icon.Origin.TOP_LEFT;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.origin_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.size_ = options.size !== undefined ? options.size : null;
+
+ /**
+ * @type {number}
+ */
+ var opacity = options.opacity !== undefined ? options.opacity : 1;
+
+ /**
+ * @type {boolean}
+ */
+ var rotateWithView = options.rotateWithView !== undefined ?
+ options.rotateWithView : false;
+
+ /**
+ * @type {number}
+ */
+ var rotation = options.rotation !== undefined ? options.rotation : 0;
+
+ /**
+ * @type {number}
+ */
+ var scale = options.scale !== undefined ? options.scale : 1;
+
+ /**
+ * @type {boolean}
+ */
+ var snapToPixel = options.snapToPixel !== undefined ?
+ options.snapToPixel : true;
+
+ ol.style.Image.call(this, {
+ opacity: opacity,
+ rotation: rotation,
+ scale: scale,
+ snapToPixel: snapToPixel,
+ rotateWithView: rotateWithView
+ });
+
+};
+ol.inherits(ol.style.Icon, ol.style.Image);
+
+
+/**
+ * Clones the style.
+ * @return {ol.style.Icon} The cloned style.
+ * @api
+ */
+ol.style.Icon.prototype.clone = function() {
+ var oldImage = this.getImage(1);
+ var newImage;
+ if (this.iconImage_.getImageState() === ol.Image.State.LOADED) {
+ if (oldImage.tagName.toUpperCase() === 'IMG') {
+ newImage = /** @type {Image} */ (oldImage.cloneNode(true));
+ } else {
+ newImage = /** @type {HTMLCanvasElement} */ (document.createElement('canvas'));
+ var context = newImage.getContext('2d');
+ newImage.width = oldImage.width;
+ newImage.height = oldImage.height;
+ context.drawImage(oldImage, 0, 0);
+ }
+ }
+ return new ol.style.Icon({
+ anchor: this.anchor_.slice(),
+ anchorOrigin: this.anchorOrigin_,
+ anchorXUnits: this.anchorXUnits_,
+ anchorYUnits: this.anchorYUnits_,
+ crossOrigin: this.crossOrigin_,
+ color: (this.color_ && this.color_.slice) ? this.color_.slice() : this.color_ || undefined,
+ img: newImage ? newImage : undefined,
+ imgSize: newImage ? this.iconImage_.getSize().slice() : undefined,
+ src: newImage ? undefined : this.getSrc(),
+ offset: this.offset_.slice(),
+ offsetOrigin: this.offsetOrigin_,
+ size: this.size_ !== null ? this.size_.slice() : undefined,
+ opacity: this.getOpacity(),
+ scale: this.getScale(),
+ snapToPixel: this.getSnapToPixel(),
+ rotation: this.getRotation(),
+ rotateWithView: this.getRotateWithView()
+ });
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.Icon.prototype.getAnchor = function() {
+ if (this.normalizedAnchor_) {
+ return this.normalizedAnchor_;
+ }
+ var anchor = this.anchor_;
+ var size = this.getSize();
+ if (this.anchorXUnits_ == ol.style.Icon.AnchorUnits.FRACTION ||
+ this.anchorYUnits_ == ol.style.Icon.AnchorUnits.FRACTION) {
+ if (!size) {
+ return null;
+ }
+ anchor = this.anchor_.slice();
+ if (this.anchorXUnits_ == ol.style.Icon.AnchorUnits.FRACTION) {
+ anchor[0] *= size[0];
+ }
+ if (this.anchorYUnits_ == ol.style.Icon.AnchorUnits.FRACTION) {
+ anchor[1] *= size[1];
+ }
+ }
+
+ if (this.anchorOrigin_ != ol.style.Icon.Origin.TOP_LEFT) {
+ if (!size) {
+ return null;
+ }
+ if (anchor === this.anchor_) {
+ anchor = this.anchor_.slice();
+ }
+ if (this.anchorOrigin_ == ol.style.Icon.Origin.TOP_RIGHT ||
+ this.anchorOrigin_ == ol.style.Icon.Origin.BOTTOM_RIGHT) {
+ anchor[0] = -anchor[0] + size[0];
+ }
+ if (this.anchorOrigin_ == ol.style.Icon.Origin.BOTTOM_LEFT ||
+ this.anchorOrigin_ == ol.style.Icon.Origin.BOTTOM_RIGHT) {
+ anchor[1] = -anchor[1] + size[1];
+ }
+ }
+ this.normalizedAnchor_ = anchor;
+ return this.normalizedAnchor_;
+};
+
+
+/**
+ * Get the icon color.
+ * @return {ol.Color} Color.
+ * @api
+ */
+ol.style.Icon.prototype.getColor = function() {
+ return this.color_;
+};
+
+
+/**
+ * Get the image icon.
+ * @param {number} pixelRatio Pixel ratio.
+ * @return {Image|HTMLCanvasElement} Image or Canvas element.
+ * @api
+ */
+ol.style.Icon.prototype.getImage = function(pixelRatio) {
+ return this.iconImage_.getImage(pixelRatio);
+};
+
+
+/**
+ * Real Image size used.
+ * @return {ol.Size} Size.
+ */
+ol.style.Icon.prototype.getImageSize = function() {
+ return this.iconImage_.getSize();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.Icon.prototype.getHitDetectionImageSize = function() {
+ return this.getImageSize();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.Icon.prototype.getImageState = function() {
+ return this.iconImage_.getImageState();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) {
+ return this.iconImage_.getHitDetectionImage(pixelRatio);
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.Icon.prototype.getOrigin = function() {
+ if (this.origin_) {
+ return this.origin_;
+ }
+ var offset = this.offset_;
+
+ if (this.offsetOrigin_ != ol.style.Icon.Origin.TOP_LEFT) {
+ var size = this.getSize();
+ var iconImageSize = this.iconImage_.getSize();
+ if (!size || !iconImageSize) {
+ return null;
+ }
+ offset = offset.slice();
+ if (this.offsetOrigin_ == ol.style.Icon.Origin.TOP_RIGHT ||
+ this.offsetOrigin_ == ol.style.Icon.Origin.BOTTOM_RIGHT) {
+ offset[0] = iconImageSize[0] - size[0] - offset[0];
+ }
+ if (this.offsetOrigin_ == ol.style.Icon.Origin.BOTTOM_LEFT ||
+ this.offsetOrigin_ == ol.style.Icon.Origin.BOTTOM_RIGHT) {
+ offset[1] = iconImageSize[1] - size[1] - offset[1];
+ }
+ }
+ this.origin_ = offset;
+ return this.origin_;
+};
+
+
+/**
+ * Get the image URL.
+ * @return {string|undefined} Image src.
+ * @api
+ */
+ol.style.Icon.prototype.getSrc = function() {
+ return this.iconImage_.getSrc();
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.style.Icon.prototype.getSize = function() {
+ return !this.size_ ? this.iconImage_.getSize() : this.size_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.Icon.prototype.listenImageChange = function(listener, thisArg) {
+ return ol.events.listen(this.iconImage_, ol.events.EventType.CHANGE,
+ listener, thisArg);
+};
+
+
+/**
+ * Load not yet loaded URI.
+ * When rendering a feature with an icon style, the vector renderer will
+ * automatically call this method. However, you might want to call this
+ * method yourself for preloading or other purposes.
+ * @api
+ */
+ol.style.Icon.prototype.load = function() {
+ this.iconImage_.load();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) {
+ ol.events.unlisten(this.iconImage_, ol.events.EventType.CHANGE,
+ listener, thisArg);
+};
+
+
+/**
+ * Icon anchor units. One of 'fraction', 'pixels'.
+ * @enum {string}
+ */
+ol.style.Icon.AnchorUnits = {
+ FRACTION: 'fraction',
+ PIXELS: 'pixels'
+};
+
+
+/**
+ * Icon origin. One of 'bottom-left', 'bottom-right', 'top-left', 'top-right'.
+ * @enum {string}
+ */
+ol.style.Icon.Origin = {
+ BOTTOM_LEFT: 'bottom-left',
+ BOTTOM_RIGHT: 'bottom-right',
+ TOP_LEFT: 'top-left',
+ TOP_RIGHT: 'top-right'
+};
+
+goog.provide('ol.style.Text');
+
+
+goog.require('ol.style.Fill');
+
+
+/**
+ * @classdesc
+ * Set text style for vector features.
+ *
+ * @constructor
+ * @param {olx.style.TextOptions=} opt_options Options.
+ * @api
+ */
+ol.style.Text = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.font_ = options.font;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.rotation_ = options.rotation;
+
+ /**
+ * @private
+ * @type {boolean|undefined}
+ */
+ this.rotateWithView_ = options.rotateWithView;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.scale_ = options.scale;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.text_ = options.text;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.textAlign_ = options.textAlign;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.textBaseline_ = options.textBaseline;
+
+ /**
+ * @private
+ * @type {ol.style.Fill}
+ */
+ this.fill_ = options.fill !== undefined ? options.fill :
+ new ol.style.Fill({color: ol.style.Text.DEFAULT_FILL_COLOR_});
+
+ /**
+ * @private
+ * @type {ol.style.Stroke}
+ */
+ this.stroke_ = options.stroke !== undefined ? options.stroke : null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0;
+};
+
+
+/**
+ * The default fill color to use if no fill was set at construction time; a
+ * blackish `#333`.
+ *
+ * @const {string}
+ * @private
+ */
+ol.style.Text.DEFAULT_FILL_COLOR_ = '#333';
+
+
+/**
+ * Clones the style.
+ * @return {ol.style.Text} The cloned style.
+ * @api
+ */
+ol.style.Text.prototype.clone = function() {
+ return new ol.style.Text({
+ font: this.getFont(),
+ rotation: this.getRotation(),
+ rotateWithView: this.getRotateWithView(),
+ scale: this.getScale(),
+ text: this.getText(),
+ textAlign: this.getTextAlign(),
+ textBaseline: this.getTextBaseline(),
+ fill: this.getFill() ? this.getFill().clone() : undefined,
+ stroke: this.getStroke() ? this.getStroke().clone() : undefined,
+ offsetX: this.getOffsetX(),
+ offsetY: this.getOffsetY()
+ });
+};
+
+
+/**
+ * Get the font name.
+ * @return {string|undefined} Font.
+ * @api
+ */
+ol.style.Text.prototype.getFont = function() {
+ return this.font_;
+};
+
+
+/**
+ * Get the x-offset for the text.
+ * @return {number} Horizontal text offset.
+ * @api
+ */
+ol.style.Text.prototype.getOffsetX = function() {
+ return this.offsetX_;
+};
+
+
+/**
+ * Get the y-offset for the text.
+ * @return {number} Vertical text offset.
+ * @api
+ */
+ol.style.Text.prototype.getOffsetY = function() {
+ return this.offsetY_;
+};
+
+
+/**
+ * Get the fill style for the text.
+ * @return {ol.style.Fill} Fill style.
+ * @api
+ */
+ol.style.Text.prototype.getFill = function() {
+ return this.fill_;
+};
+
+
+/**
+ * Determine whether the text rotates with the map.
+ * @return {boolean|undefined} Rotate with map.
+ * @api
+ */
+ol.style.Text.prototype.getRotateWithView = function() {
+ return this.rotateWithView_;
+};
+
+
+/**
+ * Get the text rotation.
+ * @return {number|undefined} Rotation.
+ * @api
+ */
+ol.style.Text.prototype.getRotation = function() {
+ return this.rotation_;
+};
+
+
+/**
+ * Get the text scale.
+ * @return {number|undefined} Scale.
+ * @api
+ */
+ol.style.Text.prototype.getScale = function() {
+ return this.scale_;
+};
+
+
+/**
+ * Get the stroke style for the text.
+ * @return {ol.style.Stroke} Stroke style.
+ * @api
+ */
+ol.style.Text.prototype.getStroke = function() {
+ return this.stroke_;
+};
+
+
+/**
+ * Get the text to be rendered.
+ * @return {string|undefined} Text.
+ * @api
+ */
+ol.style.Text.prototype.getText = function() {
+ return this.text_;
+};
+
+
+/**
+ * Get the text alignment.
+ * @return {string|undefined} Text align.
+ * @api
+ */
+ol.style.Text.prototype.getTextAlign = function() {
+ return this.textAlign_;
+};
+
+
+/**
+ * Get the text baseline.
+ * @return {string|undefined} Text baseline.
+ * @api
+ */
+ol.style.Text.prototype.getTextBaseline = function() {
+ return this.textBaseline_;
+};
+
+
+/**
+ * Set the font.
+ *
+ * @param {string|undefined} font Font.
+ * @api
+ */
+ol.style.Text.prototype.setFont = function(font) {
+ this.font_ = font;
+};
+
+
+/**
+ * Set the x offset.
+ *
+ * @param {number} offsetX Horizontal text offset.
+ * @api
+ */
+ol.style.Text.prototype.setOffsetX = function(offsetX) {
+ this.offsetX_ = offsetX;
+};
+
+
+/**
+ * Set the y offset.
+ *
+ * @param {number} offsetY Vertical text offset.
+ * @api
+ */
+ol.style.Text.prototype.setOffsetY = function(offsetY) {
+ this.offsetY_ = offsetY;
+};
+
+
+/**
+ * Set the fill.
+ *
+ * @param {ol.style.Fill} fill Fill style.
+ * @api
+ */
+ol.style.Text.prototype.setFill = function(fill) {
+ this.fill_ = fill;
+};
+
+
+/**
+ * Set the rotation.
+ *
+ * @param {number|undefined} rotation Rotation.
+ * @api
+ */
+ol.style.Text.prototype.setRotation = function(rotation) {
+ this.rotation_ = rotation;
+};
+
+
+/**
+ * Set the scale.
+ *
+ * @param {number|undefined} scale Scale.
+ * @api
+ */
+ol.style.Text.prototype.setScale = function(scale) {
+ this.scale_ = scale;
+};
+
+
+/**
+ * Set the stroke.
+ *
+ * @param {ol.style.Stroke} stroke Stroke style.
+ * @api
+ */
+ol.style.Text.prototype.setStroke = function(stroke) {
+ this.stroke_ = stroke;
+};
+
+
+/**
+ * Set the text.
+ *
+ * @param {string|undefined} text Text.
+ * @api
+ */
+ol.style.Text.prototype.setText = function(text) {
+ this.text_ = text;
+};
+
+
+/**
+ * Set the text alignment.
+ *
+ * @param {string|undefined} textAlign Text align.
+ * @api
+ */
+ol.style.Text.prototype.setTextAlign = function(textAlign) {
+ this.textAlign_ = textAlign;
+};
+
+
+/**
+ * Set the text baseline.
+ *
+ * @param {string|undefined} textBaseline Text baseline.
+ * @api
+ */
+ol.style.Text.prototype.setTextBaseline = function(textBaseline) {
+ this.textBaseline_ = textBaseline;
+};
+
+// FIXME http://earth.google.com/kml/1.0 namespace?
+// FIXME why does node.getAttribute return an unknown type?
+// FIXME serialize arbitrary feature properties
+// FIXME don't parse style if extractStyles is false
+
+goog.provide('ol.format.KML');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.array');
+goog.require('ol.asserts');
+goog.require('ol.color');
+goog.require('ol.format.Feature');
+goog.require('ol.format.XMLFeature');
+goog.require('ol.format.XSD');
+goog.require('ol.geom.GeometryCollection');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.LinearRing');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.math');
+goog.require('ol.proj');
+goog.require('ol.style.Fill');
+goog.require('ol.style.Icon');
+goog.require('ol.style.Stroke');
+goog.require('ol.style.Style');
+goog.require('ol.style.Text');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the KML format.
+ *
+ * Note that the KML format uses the URL() constructor. Older browsers such as IE
+ * which do not support this will need a URL polyfill to be loaded before use.
+ *
+ * @constructor
+ * @extends {ol.format.XMLFeature}
+ * @param {olx.format.KMLOptions=} opt_options Options.
+ * @api stable
+ */
+ol.format.KML = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.XMLFeature.call(this);
+
+ if (!ol.format.KML.DEFAULT_STYLE_ARRAY_) {
+ ol.format.KML.createStyleDefaults_();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get('EPSG:4326');
+
+ /**
+ * @private
+ * @type {Array.<ol.style.Style>}
+ */
+ this.defaultStyle_ = options.defaultStyle ?
+ options.defaultStyle : ol.format.KML.DEFAULT_STYLE_ARRAY_;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.extractStyles_ = options.extractStyles !== undefined ?
+ options.extractStyles : true;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.writeStyles_ = options.writeStyles !== undefined ?
+ options.writeStyles : true;
+
+ /**
+ * @private
+ * @type {Object.<string, (Array.<ol.style.Style>|string)>}
+ */
+ this.sharedStyles_ = {};
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.showPointNames_ = options.showPointNames !== undefined ?
+ options.showPointNames : true;
+
+};
+ol.inherits(ol.format.KML, ol.format.XMLFeature);
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.KML.EXTENSIONS_ = ['.kml'];
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.KML.GX_NAMESPACE_URIS_ = [
+ 'http://www.google.com/kml/ext/2.2'
+];
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.KML.NAMESPACE_URIS_ = [
+ null,
+ 'http://earth.google.com/kml/2.0',
+ 'http://earth.google.com/kml/2.1',
+ 'http://earth.google.com/kml/2.2',
+ 'http://www.opengis.net/kml/2.2'
+];
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ol.format.KML.SCHEMA_LOCATION_ = 'http://www.opengis.net/kml/2.2 ' +
+ 'https://developers.google.com/kml/schema/kml22gx.xsd';
+
+
+/**
+ * @return {Array.<ol.style.Style>} Default style.
+ * @private
+ */
+ol.format.KML.createStyleDefaults_ = function() {
+ /**
+ * @const
+ * @type {ol.Color}
+ * @private
+ */
+ ol.format.KML.DEFAULT_COLOR_ = [255, 255, 255, 1];
+
+ /**
+ * @const
+ * @type {ol.style.Fill}
+ * @private
+ */
+ ol.format.KML.DEFAULT_FILL_STYLE_ = new ol.style.Fill({
+ color: ol.format.KML.DEFAULT_COLOR_
+ });
+
+ /**
+ * @const
+ * @type {ol.Size}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_ = [20, 2]; // FIXME maybe [8, 32] ?
+
+ /**
+ * @const
+ * @type {ol.style.Icon.AnchorUnits}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_ =
+ ol.style.Icon.AnchorUnits.PIXELS;
+
+ /**
+ * @const
+ * @type {ol.style.Icon.AnchorUnits}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_ =
+ ol.style.Icon.AnchorUnits.PIXELS;
+
+ /**
+ * @const
+ * @type {ol.Size}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_ = [64, 64];
+
+ /**
+ * @const
+ * @type {string}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_ =
+ 'https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png';
+
+ /**
+ * @const
+ * @type {number}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_ = 0.5;
+
+ /**
+ * @const
+ * @type {ol.style.Image}
+ * @private
+ */
+ ol.format.KML.DEFAULT_IMAGE_STYLE_ = new ol.style.Icon({
+ anchor: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_,
+ anchorOrigin: ol.style.Icon.Origin.BOTTOM_LEFT,
+ anchorXUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_,
+ anchorYUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_,
+ crossOrigin: 'anonymous',
+ rotation: 0,
+ scale: ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_,
+ size: ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_,
+ src: ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_
+ });
+
+ /**
+ * @const
+ * @type {string}
+ * @private
+ */
+ ol.format.KML.DEFAULT_NO_IMAGE_STYLE_ = 'NO_IMAGE';
+
+ /**
+ * @const
+ * @type {ol.style.Stroke}
+ * @private
+ */
+ ol.format.KML.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({
+ color: ol.format.KML.DEFAULT_COLOR_,
+ width: 1
+ });
+
+ /**
+ * @const
+ * @type {ol.style.Stroke}
+ * @private
+ */
+ ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_ = new ol.style.Stroke({
+ color: [51, 51, 51, 1],
+ width: 2
+ });
+
+ /**
+ * @const
+ * @type {ol.style.Text}
+ * @private
+ */
+ ol.format.KML.DEFAULT_TEXT_STYLE_ = new ol.style.Text({
+ font: 'bold 16px Helvetica',
+ fill: ol.format.KML.DEFAULT_FILL_STYLE_,
+ stroke: ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_,
+ scale: 0.8
+ });
+
+ /**
+ * @const
+ * @type {ol.style.Style}
+ * @private
+ */
+ ol.format.KML.DEFAULT_STYLE_ = new ol.style.Style({
+ fill: ol.format.KML.DEFAULT_FILL_STYLE_,
+ image: ol.format.KML.DEFAULT_IMAGE_STYLE_,
+ text: ol.format.KML.DEFAULT_TEXT_STYLE_,
+ stroke: ol.format.KML.DEFAULT_STROKE_STYLE_,
+ zIndex: 0
+ });
+
+ /**
+ * @const
+ * @type {Array.<ol.style.Style>}
+ * @private
+ */
+ ol.format.KML.DEFAULT_STYLE_ARRAY_ = [ol.format.KML.DEFAULT_STYLE_];
+
+ return ol.format.KML.DEFAULT_STYLE_ARRAY_;
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, ol.style.Icon.AnchorUnits>}
+ * @private
+ */
+ol.format.KML.ICON_ANCHOR_UNITS_MAP_ = {
+ 'fraction': ol.style.Icon.AnchorUnits.FRACTION,
+ 'pixels': ol.style.Icon.AnchorUnits.PIXELS
+};
+
+
+/**
+ * @param {ol.style.Style|undefined} foundStyle Style.
+ * @param {string} name Name.
+ * @return {ol.style.Style} style Style.
+ * @private
+ */
+ol.format.KML.createNameStyleFunction_ = function(foundStyle, name) {
+ var textStyle = null;
+ var textOffset = [0, 0];
+ var textAlign = 'start';
+ if (foundStyle.getImage()) {
+ var imageSize = foundStyle.getImage().getImageSize();
+ if (imageSize === null) {
+ imageSize = ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_;
+ }
+ if (imageSize.length == 2) {
+ var imageScale = foundStyle.getImage().getScale();
+ // Offset the label to be centered to the right of the icon, if there is
+ // one.
+ textOffset[0] = imageScale * imageSize[0] / 2;
+ textOffset[1] = -imageScale * imageSize[1] / 2;
+ textAlign = 'left';
+ }
+ }
+ if (foundStyle.getText() !== null) {
+ // clone the text style, customizing it with name, alignments and offset.
+ // Note that kml does not support many text options that OpenLayers does (rotation, textBaseline).
+ var foundText = foundStyle.getText();
+ textStyle = foundText.clone();
+ textStyle.setFont(foundText.getFont() || ol.format.KML.DEFAULT_TEXT_STYLE_.getFont());
+ textStyle.setScale(foundText.getScale() || ol.format.KML.DEFAULT_TEXT_STYLE_.getScale());
+ textStyle.setFill(foundText.getFill() || ol.format.KML.DEFAULT_TEXT_STYLE_.getFill());
+ textStyle.setStroke(foundText.getStroke() || ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_);
+ } else {
+ textStyle = ol.format.KML.DEFAULT_TEXT_STYLE_.clone();
+ }
+ textStyle.setText(name);
+ textStyle.setOffsetX(textOffset[0]);
+ textStyle.setOffsetY(textOffset[1]);
+ textStyle.setTextAlign(textAlign);
+
+ var nameStyle = new ol.style.Style({
+ text: textStyle
+ });
+ return nameStyle;
+};
+
+
+/**
+ * @param {Array.<ol.style.Style>|undefined} style Style.
+ * @param {string} styleUrl Style URL.
+ * @param {Array.<ol.style.Style>} defaultStyle Default style.
+ * @param {Object.<string, (Array.<ol.style.Style>|string)>} sharedStyles Shared
+ * styles.
+ * @param {boolean|undefined} showPointNames true to show names for point
+ * placemarks.
+ * @return {ol.FeatureStyleFunction} Feature style function.
+ * @private
+ */
+ol.format.KML.createFeatureStyleFunction_ = function(style, styleUrl,
+ defaultStyle, sharedStyles, showPointNames) {
+
+ return (
+ /**
+ * @param {number} resolution Resolution.
+ * @return {Array.<ol.style.Style>} Style.
+ * @this {ol.Feature}
+ */
+ function(resolution) {
+ var drawName = showPointNames;
+ /** @type {ol.style.Style|undefined} */
+ var nameStyle;
+ var name = '';
+ if (drawName) {
+ if (this.getGeometry()) {
+ drawName = (this.getGeometry().getType() ===
+ ol.geom.GeometryType.POINT);
+ }
+ }
+
+ if (drawName) {
+ name = /** @type {string} */ (this.get('name'));
+ drawName = drawName && name;
+ }
+
+ if (style) {
+ if (drawName) {
+ nameStyle = ol.format.KML.createNameStyleFunction_(style[0],
+ name);
+ return style.concat(nameStyle);
+ }
+ return style;
+ }
+ if (styleUrl) {
+ var foundStyle = ol.format.KML.findStyle_(styleUrl, defaultStyle,
+ sharedStyles);
+ if (drawName) {
+ nameStyle = ol.format.KML.createNameStyleFunction_(foundStyle[0],
+ name);
+ return foundStyle.concat(nameStyle);
+ }
+ return foundStyle;
+ }
+ if (drawName) {
+ nameStyle = ol.format.KML.createNameStyleFunction_(defaultStyle[0],
+ name);
+ return defaultStyle.concat(nameStyle);
+ }
+ return defaultStyle;
+ });
+};
+
+
+/**
+ * @param {Array.<ol.style.Style>|string|undefined} styleValue Style value.
+ * @param {Array.<ol.style.Style>} defaultStyle Default style.
+ * @param {Object.<string, (Array.<ol.style.Style>|string)>} sharedStyles
+ * Shared styles.
+ * @return {Array.<ol.style.Style>} Style.
+ * @private
+ */
+ol.format.KML.findStyle_ = function(styleValue, defaultStyle, sharedStyles) {
+ if (Array.isArray(styleValue)) {
+ return styleValue;
+ } else if (typeof styleValue === 'string') {
+ // KML files in the wild occasionally forget the leading `#` on styleUrls
+ // defined in the same document. Add a leading `#` if it enables to find
+ // a style.
+ if (!(styleValue in sharedStyles) && ('#' + styleValue in sharedStyles)) {
+ styleValue = '#' + styleValue;
+ }
+ return ol.format.KML.findStyle_(
+ sharedStyles[styleValue], defaultStyle, sharedStyles);
+ } else {
+ return defaultStyle;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @private
+ * @return {ol.Color|undefined} Color.
+ */
+ol.format.KML.readColor_ = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ // The KML specification states that colors should not include a leading `#`
+ // but we tolerate them.
+ var m = /^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(s);
+ if (m) {
+ var hexColor = m[1];
+ return [
+ parseInt(hexColor.substr(6, 2), 16),
+ parseInt(hexColor.substr(4, 2), 16),
+ parseInt(hexColor.substr(2, 2), 16),
+ parseInt(hexColor.substr(0, 2), 16) / 255
+ ];
+
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @private
+ * @return {Array.<number>|undefined} Flat coordinates.
+ */
+ol.format.KML.readFlatCoordinates_ = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ var flatCoordinates = [];
+ // The KML specification states that coordinate tuples should not include
+ // spaces, but we tolerate them.
+ var re =
+ /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i;
+ var m;
+ while ((m = re.exec(s))) {
+ var x = parseFloat(m[1]);
+ var y = parseFloat(m[2]);
+ var z = m[3] ? parseFloat(m[3]) : 0;
+ flatCoordinates.push(x, y, z);
+ s = s.substr(m[0].length);
+ }
+ if (s !== '') {
+ return undefined;
+ }
+ return flatCoordinates;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @private
+ * @return {string} URI.
+ */
+ol.format.KML.readURI_ = function(node) {
+ var s = ol.xml.getAllTextContent(node, false).trim();
+ if (node.baseURI) {
+ var url = new URL(s, node.baseURI);
+ return url.href;
+ } else {
+ return s;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @private
+ * @return {ol.KMLVec2_} Vec2.
+ */
+ol.format.KML.readVec2_ = function(node) {
+ var xunits = node.getAttribute('xunits');
+ var yunits = node.getAttribute('yunits');
+ return {
+ x: parseFloat(node.getAttribute('x')),
+ xunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[xunits],
+ y: parseFloat(node.getAttribute('y')),
+ yunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[yunits]
+ };
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @private
+ * @return {number|undefined} Scale.
+ */
+ol.format.KML.readScale_ = function(node) {
+ return ol.format.XSD.readDecimal(node);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<ol.style.Style>|string|undefined} StyleMap.
+ */
+ol.format.KML.readStyleMapValue_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop(undefined,
+ ol.format.KML.STYLE_MAP_PARSERS_, node, objectStack);
+};
+ /**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.IconStyleParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be an ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'IconStyle',
+ 'localName should be IconStyle');
+ // FIXME refreshMode
+ // FIXME refreshInterval
+ // FIXME viewRefreshTime
+ // FIXME viewBoundScale
+ // FIXME viewFormat
+ // FIXME httpQuery
+ var object = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.ICON_STYLE_PARSERS_, node, objectStack);
+ if (!object) {
+ return;
+ }
+ var styleObject = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var IconObject = 'Icon' in object ? object['Icon'] : {};
+ var drawIcon = (!('Icon' in object) || Object.keys(IconObject).length > 0);
+ var src;
+ var href = /** @type {string|undefined} */
+ (IconObject['href']);
+ if (href) {
+ src = href;
+ } else if (drawIcon) {
+ src = ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_;
+ }
+ var anchor, anchorXUnits, anchorYUnits;
+ var hotSpot = /** @type {ol.KMLVec2_|undefined} */
+ (object['hotSpot']);
+ if (hotSpot) {
+ anchor = [hotSpot.x, hotSpot.y];
+ anchorXUnits = hotSpot.xunits;
+ anchorYUnits = hotSpot.yunits;
+ } else if (src === ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) {
+ anchor = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_;
+ anchorXUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_;
+ anchorYUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_;
+ } else if (/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(src)) {
+ anchor = [0.5, 0];
+ anchorXUnits = ol.style.Icon.AnchorUnits.FRACTION;
+ anchorYUnits = ol.style.Icon.AnchorUnits.FRACTION;
+ }
+
+ var offset;
+ var x = /** @type {number|undefined} */
+ (IconObject['x']);
+ var y = /** @type {number|undefined} */
+ (IconObject['y']);
+ if (x !== undefined && y !== undefined) {
+ offset = [x, y];
+ }
+
+ var size;
+ var w = /** @type {number|undefined} */
+ (IconObject['w']);
+ var h = /** @type {number|undefined} */
+ (IconObject['h']);
+ if (w !== undefined && h !== undefined) {
+ size = [w, h];
+ }
+
+ var rotation;
+ var heading = /** @type {number} */
+ (object['heading']);
+ if (heading !== undefined) {
+ rotation = ol.math.toRadians(heading);
+ }
+
+ var scale = /** @type {number|undefined} */
+ (object['scale']);
+
+ if (drawIcon) {
+ if (src == ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) {
+ size = ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_;
+ if (scale === undefined) {
+ scale = ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_;
+ }
+ }
+
+ var imageStyle = new ol.style.Icon({
+ anchor: anchor,
+ anchorOrigin: ol.style.Icon.Origin.BOTTOM_LEFT,
+ anchorXUnits: anchorXUnits,
+ anchorYUnits: anchorYUnits,
+ crossOrigin: 'anonymous', // FIXME should this be configurable?
+ offset: offset,
+ offsetOrigin: ol.style.Icon.Origin.BOTTOM_LEFT,
+ rotation: rotation,
+ scale: scale,
+ size: size,
+ src: src
+ });
+ styleObject['imageStyle'] = imageStyle;
+ } else {
+ // handle the case when we explicitly want to draw no icon.
+ styleObject['imageStyle'] = ol.format.KML.DEFAULT_NO_IMAGE_STYLE_;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.LabelStyleParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LabelStyle',
+ 'localName should be LabelStyle');
+ // FIXME colorMode
+ var object = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.LABEL_STYLE_PARSERS_, node, objectStack);
+ if (!object) {
+ return;
+ }
+ var styleObject = objectStack[objectStack.length - 1];
+ var textStyle = new ol.style.Text({
+ fill: new ol.style.Fill({
+ color: /** @type {ol.Color} */
+ ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_)
+ }),
+ scale: /** @type {number|undefined} */
+ (object['scale'])
+ });
+ styleObject['textStyle'] = textStyle;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.LineStyleParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LineStyle',
+ 'localName should be LineStyle');
+ // FIXME colorMode
+ // FIXME gx:outerColor
+ // FIXME gx:outerWidth
+ // FIXME gx:physicalWidth
+ // FIXME gx:labelVisibility
+ var object = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.LINE_STYLE_PARSERS_, node, objectStack);
+ if (!object) {
+ return;
+ }
+ var styleObject = objectStack[objectStack.length - 1];
+ var strokeStyle = new ol.style.Stroke({
+ color: /** @type {ol.Color} */
+ ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_),
+ width: /** @type {number} */ ('width' in object ? object['width'] : 1)
+ });
+ styleObject['strokeStyle'] = strokeStyle;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.PolyStyleParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'PolyStyle',
+ 'localName should be PolyStyle');
+ // FIXME colorMode
+ var object = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.POLY_STYLE_PARSERS_, node, objectStack);
+ if (!object) {
+ return;
+ }
+ var styleObject = objectStack[objectStack.length - 1];
+ var fillStyle = new ol.style.Fill({
+ color: /** @type {ol.Color} */
+ ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_)
+ });
+ styleObject['fillStyle'] = fillStyle;
+ var fill = /** @type {boolean|undefined} */ (object['fill']);
+ if (fill !== undefined) {
+ styleObject['fill'] = fill;
+ }
+ var outline =
+ /** @type {boolean|undefined} */ (object['outline']);
+ if (outline !== undefined) {
+ styleObject['outline'] = outline;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>} LinearRing flat coordinates.
+ */
+ol.format.KML.readFlatLinearRing_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LinearRing',
+ 'localName should be LinearRing');
+ return ol.xml.pushParseAndPop(null,
+ ol.format.KML.FLAT_LINEAR_RING_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.gxCoordParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(ol.array.includes(
+ ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI),
+ 'namespaceURI of the node should be known to the KML parser');
+ ol.DEBUG && console.assert(node.localName == 'coord', 'localName should be coord');
+ var gxTrackObject = /** @type {ol.KMLGxTrackObject_} */
+ (objectStack[objectStack.length - 1]);
+ var flatCoordinates = gxTrackObject.flatCoordinates;
+ var s = ol.xml.getAllTextContent(node, false);
+ var re =
+ /^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i;
+ var m = re.exec(s);
+ if (m) {
+ var x = parseFloat(m[1]);
+ var y = parseFloat(m[2]);
+ var z = parseFloat(m[3]);
+ flatCoordinates.push(x, y, z, 0);
+ } else {
+ flatCoordinates.push(0, 0, 0, 0);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.MultiLineString|undefined} MultiLineString.
+ */
+ol.format.KML.readGxMultiTrack_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(ol.array.includes(
+ ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI),
+ 'namespaceURI of the node should be known to the KML parser');
+ ol.DEBUG && console.assert(node.localName == 'MultiTrack',
+ 'localName should be MultiTrack');
+ var lineStrings = ol.xml.pushParseAndPop([],
+ ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_, node, objectStack);
+ if (!lineStrings) {
+ return undefined;
+ }
+ var multiLineString = new ol.geom.MultiLineString(null);
+ multiLineString.setLineStrings(lineStrings);
+ return multiLineString;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.LineString|undefined} LineString.
+ */
+ol.format.KML.readGxTrack_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(ol.array.includes(
+ ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI),
+ 'namespaceURI of the node should be known to the KML parser');
+ ol.DEBUG && console.assert(node.localName == 'Track', 'localName should be Track');
+ var gxTrackObject = ol.xml.pushParseAndPop(
+ /** @type {ol.KMLGxTrackObject_} */ ({
+ flatCoordinates: [],
+ whens: []
+ }), ol.format.KML.GX_TRACK_PARSERS_, node, objectStack);
+ if (!gxTrackObject) {
+ return undefined;
+ }
+ var flatCoordinates = gxTrackObject.flatCoordinates;
+ var whens = gxTrackObject.whens;
+ ol.DEBUG && console.assert(flatCoordinates.length / 4 == whens.length,
+ 'the length of the flatCoordinates array divided by 4 should be the ' +
+ 'length of the whens array');
+ var i, ii;
+ for (i = 0, ii = Math.min(flatCoordinates.length, whens.length); i < ii;
+ ++i) {
+ flatCoordinates[4 * i + 3] = whens[i];
+ }
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates);
+ return lineString;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object} Icon object.
+ */
+ol.format.KML.readIcon_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Icon', 'localName should be Icon');
+ var iconObject = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.ICON_PARSERS_, node, objectStack);
+ if (iconObject) {
+ return iconObject;
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.format.KML.readFlatCoordinatesFromNode_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return ol.xml.pushParseAndPop(null,
+ ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.LineString|undefined} LineString.
+ */
+ol.format.KML.readLineString_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LineString',
+ 'localName should be LineString');
+ var properties = ol.xml.pushParseAndPop({},
+ ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node,
+ objectStack);
+ var flatCoordinates =
+ ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack);
+ if (flatCoordinates) {
+ var lineString = new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
+ lineString.setProperties(properties);
+ return lineString;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.Polygon|undefined} Polygon.
+ */
+ol.format.KML.readLinearRing_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LinearRing',
+ 'localName should be LinearRing');
+ var properties = ol.xml.pushParseAndPop({},
+ ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node,
+ objectStack);
+ var flatCoordinates =
+ ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack);
+ if (flatCoordinates) {
+ var polygon = new ol.geom.Polygon(null);
+ polygon.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates,
+ [flatCoordinates.length]);
+ polygon.setProperties(properties);
+ return polygon;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.KML.readMultiGeometry_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MultiGeometry',
+ 'localName should be MultiGeometry');
+ var geometries = ol.xml.pushParseAndPop([],
+ ol.format.KML.MULTI_GEOMETRY_PARSERS_, node, objectStack);
+ if (!geometries) {
+ return null;
+ }
+ if (geometries.length === 0) {
+ return new ol.geom.GeometryCollection(geometries);
+ }
+ /** @type {ol.geom.Geometry} */
+ var multiGeometry;
+ var homogeneous = true;
+ var type = geometries[0].getType();
+ var geometry, i, ii;
+ for (i = 1, ii = geometries.length; i < ii; ++i) {
+ geometry = geometries[i];
+ if (geometry.getType() != type) {
+ homogeneous = false;
+ break;
+ }
+ }
+ if (homogeneous) {
+ var layout;
+ var flatCoordinates;
+ if (type == ol.geom.GeometryType.POINT) {
+ var point = geometries[0];
+ layout = point.getLayout();
+ flatCoordinates = point.getFlatCoordinates();
+ for (i = 1, ii = geometries.length; i < ii; ++i) {
+ geometry = geometries[i];
+ ol.DEBUG && console.assert(geometry.getLayout() == layout,
+ 'geometry layout should be consistent');
+ ol.array.extend(flatCoordinates, geometry.getFlatCoordinates());
+ }
+ multiGeometry = new ol.geom.MultiPoint(null);
+ multiGeometry.setFlatCoordinates(layout, flatCoordinates);
+ ol.format.KML.setCommonGeometryProperties_(multiGeometry, geometries);
+ } else if (type == ol.geom.GeometryType.LINE_STRING) {
+ multiGeometry = new ol.geom.MultiLineString(null);
+ multiGeometry.setLineStrings(geometries);
+ ol.format.KML.setCommonGeometryProperties_(multiGeometry, geometries);
+ } else if (type == ol.geom.GeometryType.POLYGON) {
+ multiGeometry = new ol.geom.MultiPolygon(null);
+ multiGeometry.setPolygons(geometries);
+ ol.format.KML.setCommonGeometryProperties_(multiGeometry, geometries);
+ } else if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) {
+ multiGeometry = new ol.geom.GeometryCollection(geometries);
+ } else {
+ ol.asserts.assert(false, 37); // Unknown geometry type found
+ }
+ } else {
+ multiGeometry = new ol.geom.GeometryCollection(geometries);
+ }
+ return /** @type {ol.geom.Geometry} */ (multiGeometry);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.Point|undefined} Point.
+ */
+ol.format.KML.readPoint_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Point', 'localName should be Point');
+ var properties = ol.xml.pushParseAndPop({},
+ ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node,
+ objectStack);
+ var flatCoordinates =
+ ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack);
+ if (flatCoordinates) {
+ var point = new ol.geom.Point(null);
+ ol.DEBUG && console.assert(flatCoordinates.length == 3,
+ 'flatCoordinates should have a length of 3');
+ point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
+ point.setProperties(properties);
+ return point;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.geom.Polygon|undefined} Polygon.
+ */
+ol.format.KML.readPolygon_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Polygon',
+ 'localName should be Polygon');
+ var properties = ol.xml.pushParseAndPop(/** @type {Object<string,*>} */ ({}),
+ ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node,
+ objectStack);
+ var flatLinearRings = ol.xml.pushParseAndPop([null],
+ ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack);
+ if (flatLinearRings && flatLinearRings[0]) {
+ var polygon = new ol.geom.Polygon(null);
+ var flatCoordinates = flatLinearRings[0];
+ var ends = [flatCoordinates.length];
+ var i, ii;
+ for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
+ ol.array.extend(flatCoordinates, flatLinearRings[i]);
+ ends.push(flatCoordinates.length);
+ }
+ polygon.setFlatCoordinates(
+ ol.geom.GeometryLayout.XYZ, flatCoordinates, ends);
+ polygon.setProperties(properties);
+ return polygon;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<ol.style.Style>} Style.
+ */
+ol.format.KML.readStyle_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Style', 'localName should be Style');
+ var styleObject = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.STYLE_PARSERS_, node, objectStack);
+ if (!styleObject) {
+ return null;
+ }
+ var fillStyle = /** @type {ol.style.Fill} */
+ ('fillStyle' in styleObject ?
+ styleObject['fillStyle'] : ol.format.KML.DEFAULT_FILL_STYLE_);
+ var fill = /** @type {boolean|undefined} */ (styleObject['fill']);
+ if (fill !== undefined && !fill) {
+ fillStyle = null;
+ }
+ var imageStyle = /** @type {ol.style.Image} */
+ ('imageStyle' in styleObject ?
+ styleObject['imageStyle'] : ol.format.KML.DEFAULT_IMAGE_STYLE_);
+ if (imageStyle == ol.format.KML.DEFAULT_NO_IMAGE_STYLE_) {
+ imageStyle = undefined;
+ }
+ var textStyle = /** @type {ol.style.Text} */
+ ('textStyle' in styleObject ?
+ styleObject['textStyle'] : ol.format.KML.DEFAULT_TEXT_STYLE_);
+ var strokeStyle = /** @type {ol.style.Stroke} */
+ ('strokeStyle' in styleObject ?
+ styleObject['strokeStyle'] : ol.format.KML.DEFAULT_STROKE_STYLE_);
+ var outline = /** @type {boolean|undefined} */
+ (styleObject['outline']);
+ if (outline !== undefined && !outline) {
+ strokeStyle = null;
+ }
+ return [new ol.style.Style({
+ fill: fillStyle,
+ image: imageStyle,
+ stroke: strokeStyle,
+ text: textStyle,
+ zIndex: undefined // FIXME
+ })];
+};
+
+
+/**
+ * Reads an array of geometries and creates arrays for common geometry
+ * properties. Then sets them to the multi geometry.
+ * @param {ol.geom.MultiPoint|ol.geom.MultiLineString|ol.geom.MultiPolygon}
+ * multiGeometry A multi-geometry.
+ * @param {Array.<ol.geom.Geometry>} geometries List of geometries.
+ * @private
+ */
+ol.format.KML.setCommonGeometryProperties_ = function(multiGeometry,
+ geometries) {
+ var ii = geometries.length;
+ var extrudes = new Array(geometries.length);
+ var altitudeModes = new Array(geometries.length);
+ var geometry, i, hasExtrude, hasAltitudeMode;
+ hasExtrude = hasAltitudeMode = false;
+ for (i = 0; i < ii; ++i) {
+ geometry = geometries[i];
+ extrudes[i] = geometry.get('extrude');
+ altitudeModes[i] = geometry.get('altitudeMode');
+ hasExtrude = hasExtrude || extrudes[i] !== undefined;
+ hasAltitudeMode = hasAltitudeMode || altitudeModes[i];
+ }
+ if (hasExtrude) {
+ multiGeometry.set('extrude', extrudes);
+ }
+ if (hasAltitudeMode) {
+ multiGeometry.set('altitudeMode', altitudeModes);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.DataParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Data', 'localName should be Data');
+ var name = node.getAttribute('name');
+ ol.xml.parseNode(ol.format.KML.DATA_PARSERS_, node, objectStack);
+ var featureObject =
+ /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ if (name !== null) {
+ featureObject[name] = featureObject.value;
+ } else if (featureObject.displayName !== null) {
+ featureObject[featureObject.displayName] = featureObject.value;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.ExtendedDataParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ExtendedData',
+ 'localName should be ExtendedData');
+ ol.xml.parseNode(ol.format.KML.EXTENDED_DATA_PARSERS_, node, objectStack);
+};
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.RegionParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Region',
+ 'localName should be Region');
+ ol.xml.parseNode(ol.format.KML.REGION_PARSERS_, node, objectStack);
+};
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.PairDataParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Pair', 'localName should be Pair');
+ var pairObject = ol.xml.pushParseAndPop(
+ {}, ol.format.KML.PAIR_PARSERS_, node, objectStack);
+ if (!pairObject) {
+ return;
+ }
+ var key = /** @type {string|undefined} */
+ (pairObject['key']);
+ if (key && key == 'normal') {
+ var styleUrl = /** @type {string|undefined} */
+ (pairObject['styleUrl']);
+ if (styleUrl) {
+ objectStack[objectStack.length - 1] = styleUrl;
+ }
+ var Style = /** @type {ol.style.Style} */
+ (pairObject['Style']);
+ if (Style) {
+ objectStack[objectStack.length - 1] = Style;
+ }
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.PlacemarkStyleMapParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'StyleMap',
+ 'localName should be StyleMap');
+ var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack);
+ if (!styleMapValue) {
+ return;
+ }
+ var placemarkObject = objectStack[objectStack.length - 1];
+ if (Array.isArray(styleMapValue)) {
+ placemarkObject['Style'] = styleMapValue;
+ } else if (typeof styleMapValue === 'string') {
+ placemarkObject['styleUrl'] = styleMapValue;
+ } else {
+ ol.asserts.assert(false, 38); // `styleMapValue` has an unknown type
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.SchemaDataParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'SchemaData',
+ 'localName should be SchemaData');
+ ol.xml.parseNode(ol.format.KML.SCHEMA_DATA_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.SimpleDataParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'SimpleData',
+ 'localName should be SimpleData');
+ var name = node.getAttribute('name');
+ if (name !== null) {
+ var data = ol.format.XSD.readString(node);
+ var featureObject =
+ /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ featureObject[name] = data;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.LatLonAltBoxParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'LatLonAltBox',
+ 'localName should be LatLonAltBox');
+ var object = ol.xml.pushParseAndPop({}, ol.format.KML.LAT_LON_ALT_BOX_PARSERS_, node, objectStack);
+ if (!object) {
+ return;
+ }
+ var regionObject = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var extent = [
+ parseFloat(object['west']),
+ parseFloat(object['south']),
+ parseFloat(object['east']),
+ parseFloat(object['north'])
+ ];
+ regionObject['extent'] = extent;
+ regionObject['altitudeMode'] = object['altitudeMode'];
+ regionObject['minAltitude'] = parseFloat(object['minAltitude']);
+ regionObject['maxAltitude'] = parseFloat(object['maxAltitude']);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.LodParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Lod',
+ 'localName should be Lod');
+ var object = ol.xml.pushParseAndPop({}, ol.format.KML.LOD_PARSERS_, node, objectStack);
+ if (!object) {
+ return;
+ }
+ var lodObject = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ lodObject['minLodPixels'] = parseFloat(object['minLodPixels']);
+ lodObject['maxLodPixels'] = parseFloat(object['maxLodPixels']);
+ lodObject['minFadeExtent'] = parseFloat(object['minFadeExtent']);
+ lodObject['maxFadeExtent'] = parseFloat(object['maxFadeExtent']);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.innerBoundaryIsParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'innerBoundaryIs',
+ 'localName should be innerBoundaryIs');
+ /** @type {Array.<number>|undefined} */
+ var flatLinearRing = ol.xml.pushParseAndPop(undefined,
+ ol.format.KML.INNER_BOUNDARY_IS_PARSERS_, node, objectStack);
+ if (flatLinearRing) {
+ var flatLinearRings = /** @type {Array.<Array.<number>>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(flatLinearRings),
+ 'flatLinearRings should be an array');
+ ol.DEBUG && console.assert(flatLinearRings.length > 0,
+ 'flatLinearRings array should not be empty');
+ flatLinearRings.push(flatLinearRing);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.outerBoundaryIsParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'outerBoundaryIs',
+ 'localName should be outerBoundaryIs');
+ /** @type {Array.<number>|undefined} */
+ var flatLinearRing = ol.xml.pushParseAndPop(undefined,
+ ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_, node, objectStack);
+ if (flatLinearRing) {
+ var flatLinearRings = /** @type {Array.<Array.<number>>} */
+ (objectStack[objectStack.length - 1]);
+ ol.DEBUG && console.assert(Array.isArray(flatLinearRings),
+ 'flatLinearRings should be an array');
+ ol.DEBUG && console.assert(flatLinearRings.length > 0,
+ 'flatLinearRings array should not be empty');
+ flatLinearRings[0] = flatLinearRing;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.LinkParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Link', 'localName should be Link');
+ ol.xml.parseNode(ol.format.KML.LINK_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.whenParser_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'when', 'localName should be when');
+ var gxTrackObject = /** @type {ol.KMLGxTrackObject_} */
+ (objectStack[objectStack.length - 1]);
+ var whens = gxTrackObject.whens;
+ var s = ol.xml.getAllTextContent(node, false);
+ var when = Date.parse(s);
+ whens.push(isNaN(when) ? 0 : when);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.DATA_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'displayName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'value': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.EXTENDED_DATA_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Data': ol.format.KML.DataParser_,
+ 'SchemaData': ol.format.KML.SchemaDataParser_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.REGION_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'LatLonAltBox': ol.format.KML.LatLonAltBoxParser_,
+ 'Lod': ol.format.KML.LodParser_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.LAT_LON_ALT_BOX_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'altitudeMode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'minAltitude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'maxAltitude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'north': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'south': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'east': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'west': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.LOD_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'minLodPixels': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'maxLodPixels': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'minFadeExtent': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'maxFadeExtent': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'extrude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
+ 'altitudeMode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.FLAT_LINEAR_RING_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'innerBoundaryIs': ol.format.KML.innerBoundaryIsParser_,
+ 'outerBoundaryIs': ol.format.KML.outerBoundaryIsParser_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.GX_TRACK_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'when': ol.format.KML.whenParser_
+ }, ol.xml.makeStructureNS(
+ ol.format.KML.GX_NAMESPACE_URIS_, {
+ 'coord': ol.format.KML.gxCoordParser_
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.ICON_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_)
+ }, ol.xml.makeStructureNS(
+ ol.format.KML.GX_NAMESPACE_URIS_, {
+ 'x': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'y': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'w': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'h': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal)
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.ICON_STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Icon': ol.xml.makeObjectPropertySetter(ol.format.KML.readIcon_),
+ 'heading': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'hotSpot': ol.xml.makeObjectPropertySetter(ol.format.KML.readVec2_),
+ 'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.INNER_BOUNDARY_IS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.LABEL_STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_),
+ 'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.LINE_STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_),
+ 'width': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.MULTI_GEOMETRY_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'LineString': ol.xml.makeArrayPusher(ol.format.KML.readLineString_),
+ 'LinearRing': ol.xml.makeArrayPusher(ol.format.KML.readLinearRing_),
+ 'MultiGeometry': ol.xml.makeArrayPusher(ol.format.KML.readMultiGeometry_),
+ 'Point': ol.xml.makeArrayPusher(ol.format.KML.readPoint_),
+ 'Polygon': ol.xml.makeArrayPusher(ol.format.KML.readPolygon_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.GX_NAMESPACE_URIS_, {
+ 'Track': ol.xml.makeArrayPusher(ol.format.KML.readGxTrack_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.NETWORK_LINK_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'ExtendedData': ol.format.KML.ExtendedDataParser_,
+ 'Region': ol.format.KML.RegionParser_,
+ 'Link': ol.format.KML.LinkParser_,
+ 'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
+ 'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.LINK_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.PAIR_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_),
+ 'key': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.PLACEMARK_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'ExtendedData': ol.format.KML.ExtendedDataParser_,
+ 'Region': ol.format.KML.RegionParser_,
+ 'MultiGeometry': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readMultiGeometry_, 'geometry'),
+ 'LineString': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readLineString_, 'geometry'),
+ 'LinearRing': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readLinearRing_, 'geometry'),
+ 'Point': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readPoint_, 'geometry'),
+ 'Polygon': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readPolygon_, 'geometry'),
+ 'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_),
+ 'StyleMap': ol.format.KML.PlacemarkStyleMapParser_,
+ 'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
+ 'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_),
+ 'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean)
+ }, ol.xml.makeStructureNS(
+ ol.format.KML.GX_NAMESPACE_URIS_, {
+ 'MultiTrack': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readGxMultiTrack_, 'geometry'),
+ 'Track': ol.xml.makeObjectPropertySetter(
+ ol.format.KML.readGxTrack_, 'geometry')
+ }
+ ));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.POLY_STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_),
+ 'fill': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
+ 'outline': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.SCHEMA_DATA_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'SimpleData': ol.format.KML.SimpleDataParser_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'IconStyle': ol.format.KML.IconStyleParser_,
+ 'LabelStyle': ol.format.KML.LabelStyleParser_,
+ 'LineStyle': ol.format.KML.LineStyleParser_,
+ 'PolyStyle': ol.format.KML.PolyStyleParser_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.KML.STYLE_MAP_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Pair': ol.format.KML.PairDataParser_
+ });
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.KML.prototype.getExtensions = function() {
+ return ol.format.KML.EXTENSIONS_;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<ol.Feature>|undefined} Features.
+ */
+ol.format.KML.prototype.readDocumentOrFolder_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ var localName = node.localName;
+ ol.DEBUG && console.assert(localName == 'Document' || localName == 'Folder',
+ 'localName should be Document or Folder');
+ // FIXME use scope somehow
+ var parsersNS = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Document': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this),
+ 'Folder': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this),
+ 'Placemark': ol.xml.makeArrayPusher(this.readPlacemark_, this),
+ 'Style': this.readSharedStyle_.bind(this),
+ 'StyleMap': this.readSharedStyleMap_.bind(this)
+ });
+ /** @type {Array.<ol.Feature>} */
+ var features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack, this);
+ if (features) {
+ return features;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Feature.
+ */
+ol.format.KML.prototype.readPlacemark_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Placemark',
+ 'localName should be Placemark');
+ var object = ol.xml.pushParseAndPop({'geometry': null},
+ ol.format.KML.PLACEMARK_PARSERS_, node, objectStack);
+ if (!object) {
+ return undefined;
+ }
+ var feature = new ol.Feature();
+ var id = node.getAttribute('id');
+ if (id !== null) {
+ feature.setId(id);
+ }
+ var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
+
+ var geometry = object['geometry'];
+ if (geometry) {
+ ol.format.Feature.transformWithOptions(geometry, false, options);
+ }
+ feature.setGeometry(geometry);
+ delete object['geometry'];
+
+ if (this.extractStyles_) {
+ var style = object['Style'];
+ var styleUrl = object['styleUrl'];
+ var styleFunction = ol.format.KML.createFeatureStyleFunction_(
+ style, styleUrl, this.defaultStyle_, this.sharedStyles_,
+ this.showPointNames_);
+ feature.setStyle(styleFunction);
+ }
+ delete object['Style'];
+ // we do not remove the styleUrl property from the object, so it
+ // gets stored on feature when setProperties is called
+
+ feature.setProperties(object);
+
+ return feature;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.prototype.readSharedStyle_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Style', 'localName should be Style');
+ var id = node.getAttribute('id');
+ if (id !== null) {
+ var style = ol.format.KML.readStyle_(node, objectStack);
+ if (style) {
+ var styleUri;
+ if (node.baseURI) {
+ var url = new URL('#' + id, node.baseURI);
+ styleUri = url.href;
+ } else {
+ styleUri = '#' + id;
+ }
+ this.sharedStyles_[styleUri] = style;
+ }
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.prototype.readSharedStyleMap_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'StyleMap',
+ 'localName should be StyleMap');
+ var id = node.getAttribute('id');
+ if (id === null) {
+ return;
+ }
+ var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack);
+ if (!styleMapValue) {
+ return;
+ }
+ var styleUri;
+ if (node.baseURI) {
+ var url = new URL('#' + id, node.baseURI);
+ styleUri = url.href;
+ } else {
+ styleUri = '#' + id;
+ }
+ this.sharedStyles_[styleUri] = styleMapValue;
+};
+
+
+/**
+ * Read the first feature from a KML source. MultiGeometries are converted into
+ * GeometryCollections if they are a mix of geometry types, and into MultiPoint/
+ * MultiLineString/MultiPolygon if they are all of the same type.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api stable
+ */
+ol.format.KML.prototype.readFeature;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.KML.prototype.readFeatureFromNode = function(node, opt_options) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) {
+ return null;
+ }
+ ol.DEBUG && console.assert(node.localName == 'Placemark',
+ 'localName should be Placemark');
+ var feature = this.readPlacemark_(
+ node, [this.getReadOptions(node, opt_options)]);
+ if (feature) {
+ return feature;
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * Read all features from a KML source. MultiGeometries are converted into
+ * GeometryCollections if they are a mix of geometry types, and into MultiPoint/
+ * MultiLineString/MultiPolygon if they are all of the same type.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.KML.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.KML.prototype.readFeaturesFromNode = function(node, opt_options) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) {
+ return [];
+ }
+ var features;
+ var localName = node.localName;
+ if (localName == 'Document' || localName == 'Folder') {
+ features = this.readDocumentOrFolder_(
+ node, [this.getReadOptions(node, opt_options)]);
+ if (features) {
+ return features;
+ } else {
+ return [];
+ }
+ } else if (localName == 'Placemark') {
+ var feature = this.readPlacemark_(
+ node, [this.getReadOptions(node, opt_options)]);
+ if (feature) {
+ return [feature];
+ } else {
+ return [];
+ }
+ } else if (localName == 'kml') {
+ features = [];
+ var n;
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ var fs = this.readFeaturesFromNode(n, opt_options);
+ if (fs) {
+ ol.array.extend(features, fs);
+ }
+ }
+ return features;
+ } else {
+ return [];
+ }
+};
+
+
+/**
+ * Read the name of the KML.
+ *
+ * @param {Document|Node|string} source Souce.
+ * @return {string|undefined} Name.
+ * @api stable
+ */
+ol.format.KML.prototype.readName = function(source) {
+ if (ol.xml.isDocument(source)) {
+ return this.readNameFromDocument(/** @type {Document} */ (source));
+ } else if (ol.xml.isNode(source)) {
+ return this.readNameFromNode(/** @type {Node} */ (source));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readNameFromDocument(doc);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {string|undefined} Name.
+ */
+ol.format.KML.prototype.readNameFromDocument = function(doc) {
+ var n;
+ for (n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ var name = this.readNameFromNode(n);
+ if (name) {
+ return name;
+ }
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {string|undefined} Name.
+ */
+ol.format.KML.prototype.readNameFromNode = function(node) {
+ var n;
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
+ n.localName == 'name') {
+ return ol.format.XSD.readString(n);
+ }
+ }
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ var localName = n.localName;
+ if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
+ (localName == 'Document' ||
+ localName == 'Folder' ||
+ localName == 'Placemark' ||
+ localName == 'kml')) {
+ var name = this.readNameFromNode(n);
+ if (name) {
+ return name;
+ }
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * Read the network links of the KML.
+ *
+ * @param {Document|Node|string} source Source.
+ * @return {Array.<Object>} Network links.
+ * @api
+ */
+ol.format.KML.prototype.readNetworkLinks = function(source) {
+ var networkLinks = [];
+ if (ol.xml.isDocument(source)) {
+ ol.array.extend(networkLinks, this.readNetworkLinksFromDocument(
+ /** @type {Document} */ (source)));
+ } else if (ol.xml.isNode(source)) {
+ ol.array.extend(networkLinks, this.readNetworkLinksFromNode(
+ /** @type {Node} */ (source)));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ ol.array.extend(networkLinks, this.readNetworkLinksFromDocument(doc));
+ }
+ return networkLinks;
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {Array.<Object>} Network links.
+ */
+ol.format.KML.prototype.readNetworkLinksFromDocument = function(doc) {
+ var n, networkLinks = [];
+ for (n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ ol.array.extend(networkLinks, this.readNetworkLinksFromNode(n));
+ }
+ }
+ return networkLinks;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {Array.<Object>} Network links.
+ */
+ol.format.KML.prototype.readNetworkLinksFromNode = function(node) {
+ var n, networkLinks = [];
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
+ n.localName == 'NetworkLink') {
+ var obj = ol.xml.pushParseAndPop({}, ol.format.KML.NETWORK_LINK_PARSERS_,
+ n, []);
+ networkLinks.push(obj);
+ }
+ }
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ var localName = n.localName;
+ if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
+ (localName == 'Document' ||
+ localName == 'Folder' ||
+ localName == 'kml')) {
+ ol.array.extend(networkLinks, this.readNetworkLinksFromNode(n));
+ }
+ }
+ return networkLinks;
+};
+
+
+/**
+ * Read the regions of the KML.
+ *
+ * @param {Document|Node|string} source Source.
+ * @return {Array.<Object>} Regions.
+ * @api
+ */
+ol.format.KML.prototype.readRegion = function(source) {
+ var regions = [];
+ if (ol.xml.isDocument(source)) {
+ ol.array.extend(regions, this.readRegionFromDocument(
+ /** @type {Document} */ (source)));
+ } else if (ol.xml.isNode(source)) {
+ ol.array.extend(regions, this.readRegionFromNode(
+ /** @type {Node} */ (source)));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ ol.array.extend(regions, this.readRegionFromDocument(doc));
+ }
+ return regions;
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {Array.<Object>} Region.
+ */
+ol.format.KML.prototype.readRegionFromDocument = function(doc) {
+ var n, regions = [];
+ for (n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ ol.array.extend(regions, this.readRegionFromNode(n));
+ }
+ }
+ return regions;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {Array.<Object>} Region.
+ * @api
+ */
+ol.format.KML.prototype.readRegionFromNode = function(node) {
+ var n, regions = [];
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
+ n.localName == 'Region') {
+ var obj = ol.xml.pushParseAndPop({}, ol.format.KML.REGION_PARSERS_,
+ n, []);
+ regions.push(obj);
+ }
+ }
+ for (n = node.firstElementChild; n; n = n.nextElementSibling) {
+ var localName = n.localName;
+ if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
+ (localName == 'Document' ||
+ localName == 'Folder' ||
+ localName == 'kml')) {
+ ol.array.extend(regions, this.readRegionFromNode(n));
+ }
+ }
+ return regions;
+};
+
+
+/**
+ * Read the projection from a KML source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.KML.prototype.readProjection;
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the color to.
+ * @param {ol.Color|string} color Color.
+ * @private
+ */
+ol.format.KML.writeColorTextNode_ = function(node, color) {
+ var rgba = ol.color.asArray(color);
+ var opacity = (rgba.length == 4) ? rgba[3] : 1;
+ var abgr = [opacity * 255, rgba[2], rgba[1], rgba[0]];
+ var i;
+ for (i = 0; i < 4; ++i) {
+ var hex = parseInt(abgr[i], 10).toString(16);
+ abgr[i] = (hex.length == 1) ? '0' + hex : hex;
+ }
+ ol.format.XSD.writeStringTextNode(node, abgr.join(''));
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the coordinates to.
+ * @param {Array.<number>} coordinates Coordinates.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeCoordinatesTextNode_ = function(node, coordinates, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+
+ var layout = context['layout'];
+ var stride = context['stride'];
+
+ var dimension;
+ if (layout == ol.geom.GeometryLayout.XY ||
+ layout == ol.geom.GeometryLayout.XYM) {
+ dimension = 2;
+ } else if (layout == ol.geom.GeometryLayout.XYZ ||
+ layout == ol.geom.GeometryLayout.XYZM) {
+ dimension = 3;
+ } else {
+ ol.asserts.assert(false, 34); // Invalid geometry layout
+ }
+
+ var d, i;
+ var ii = coordinates.length;
+ var text = '';
+ if (ii > 0) {
+ text += coordinates[0];
+ for (d = 1; d < dimension; ++d) {
+ text += ',' + coordinates[d];
+ }
+ for (i = stride; i < ii; i += stride) {
+ text += ' ' + coordinates[i];
+ for (d = 1; d < dimension; ++d) {
+ text += ',' + coordinates[i + d];
+ }
+ }
+ }
+ ol.format.XSD.writeStringTextNode(node, text);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {{name: *, value: *}} pair Name value pair.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeDataNode_ = function(node, pair, objectStack) {
+ node.setAttribute('name', pair.name);
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var value = pair.value;
+
+ if (typeof value == 'object') {
+ if (value !== null && value.displayName) {
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, [value.displayName], objectStack, ['displayName']);
+ }
+
+ if (value !== null && value.value) {
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, [value.value], objectStack, ['value']);
+ }
+ } else {
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, [value], objectStack, ['value']);
+ }
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the name to.
+ * @param {string} name DisplayName.
+ * @private
+ */
+ol.format.KML.writeDataNodeName_ = function(node, name) {
+ ol.format.XSD.writeCDATASection(node, name);
+};
+
+
+/**
+ * @param {Node} node Node to append a CDATA Section with the value to.
+ * @param {string} value Value.
+ * @private
+ */
+ol.format.KML.writeDataNodeValue_ = function(node, value) {
+ ol.format.XSD.writeStringTextNode(node, value);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {Array.<*>} objectStack Object stack.
+ * @this {ol.format.KML}
+ * @private
+ */
+ol.format.KML.writeDocument_ = function(node, features, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.DOCUMENT_SERIALIZERS_,
+ ol.format.KML.DOCUMENT_NODE_FACTORY_, features, objectStack, undefined,
+ this);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {{names: Array<string>, values: (Array<*>)}} namesAndValues Names and values.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeExtendedData_ = function(node, namesAndValues, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var names = namesAndValues.names, values = namesAndValues.values;
+ var length = names.length;
+
+ for (var i = 0; i < length; i++) {
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_,
+ ol.format.KML.DATA_NODE_FACTORY_, [{name: names[i], value: values[i]}], objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Object} icon Icon object.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeIcon_ = function(node, icon, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.KML.ICON_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(icon, orderedKeys);
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.KML.ICON_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
+ values, objectStack, orderedKeys);
+ orderedKeys =
+ ol.format.KML.ICON_SEQUENCE_[ol.format.KML.GX_NAMESPACE_URIS_[0]];
+ values = ol.xml.makeSequence(icon, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_SERIALIZERS_,
+ ol.format.KML.GX_NODE_FACTORY_, values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.style.Icon} style Icon style.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeIconStyle_ = function(node, style, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var properties = {};
+ var src = style.getSrc();
+ var size = style.getSize();
+ var iconImageSize = style.getImageSize();
+ var iconProperties = {
+ 'href': src
+ };
+
+ if (size) {
+ iconProperties['w'] = size[0];
+ iconProperties['h'] = size[1];
+ var anchor = style.getAnchor(); // top-left
+ var origin = style.getOrigin(); // top-left
+
+ if (origin && iconImageSize && origin[0] !== 0 && origin[1] !== size[1]) {
+ iconProperties['x'] = origin[0];
+ iconProperties['y'] = iconImageSize[1] - (origin[1] + size[1]);
+ }
+
+ if (anchor && anchor[0] !== 0 && anchor[1] !== size[1]) {
+ var /** @type {ol.KMLVec2_} */ hotSpot = {
+ x: anchor[0],
+ xunits: ol.style.Icon.AnchorUnits.PIXELS,
+ y: size[1] - anchor[1],
+ yunits: ol.style.Icon.AnchorUnits.PIXELS
+ };
+ properties['hotSpot'] = hotSpot;
+ }
+ }
+
+ properties['Icon'] = iconProperties;
+
+ var scale = style.getScale();
+ if (scale !== 1) {
+ properties['scale'] = scale;
+ }
+
+ var rotation = style.getRotation();
+ if (rotation !== 0) {
+ properties['heading'] = rotation; // 0-360
+ }
+
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.KML.ICON_STYLE_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_STYLE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.style.Text} style style.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeLabelStyle_ = function(node, style, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var properties = {};
+ var fill = style.getFill();
+ if (fill) {
+ properties['color'] = fill.getColor();
+ }
+ var scale = style.getScale();
+ if (scale && scale !== 1) {
+ properties['scale'] = scale;
+ }
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys =
+ ol.format.KML.LABEL_STYLE_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.LABEL_STYLE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.style.Stroke} style style.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeLineStyle_ = function(node, style, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var properties = {
+ 'color': style.getColor(),
+ 'width': style.getWidth()
+ };
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.KML.LINE_STYLE_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.LINE_STYLE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeMultiGeometry_ = function(node, geometry, objectStack) {
+ /** @type {ol.XmlNodeStackItem} */
+ var context = {node: node};
+ var type = geometry.getType();
+ /** @type {Array.<ol.geom.Geometry>} */
+ var geometries;
+ /** @type {function(*, Array.<*>, string=): (Node|undefined)} */
+ var factory;
+ if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) {
+ geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries();
+ factory = ol.format.KML.GEOMETRY_NODE_FACTORY_;
+ } else if (type == ol.geom.GeometryType.MULTI_POINT) {
+ geometries = /** @type {ol.geom.MultiPoint} */ (geometry).getPoints();
+ factory = ol.format.KML.POINT_NODE_FACTORY_;
+ } else if (type == ol.geom.GeometryType.MULTI_LINE_STRING) {
+ geometries =
+ (/** @type {ol.geom.MultiLineString} */ (geometry)).getLineStrings();
+ factory = ol.format.KML.LINE_STRING_NODE_FACTORY_;
+ } else if (type == ol.geom.GeometryType.MULTI_POLYGON) {
+ geometries =
+ (/** @type {ol.geom.MultiPolygon} */ (geometry)).getPolygons();
+ factory = ol.format.KML.POLYGON_NODE_FACTORY_;
+ } else {
+ ol.asserts.assert(false, 39); // Unknown geometry type
+ }
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_, factory,
+ geometries, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.LinearRing} linearRing Linear ring.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeBoundaryIs_ = function(node, linearRing, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.KML.BOUNDARY_IS_SERIALIZERS_,
+ ol.format.KML.LINEAR_RING_NODE_FACTORY_, [linearRing], objectStack);
+};
+
+
+/**
+ * FIXME currently we do serialize arbitrary/custom feature properties
+ * (ExtendedData).
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Object stack.
+ * @this {ol.format.KML}
+ * @private
+ */
+ol.format.KML.writePlacemark_ = function(node, feature, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+
+ // set id
+ if (feature.getId()) {
+ node.setAttribute('id', feature.getId());
+ }
+
+ // serialize properties (properties unknown to KML are not serialized)
+ var properties = feature.getProperties();
+
+ // don't export these to ExtendedData
+ var filter = {'address': 1, 'description': 1, 'name': 1, 'open': 1,
+ 'phoneNumber': 1, 'styleUrl': 1, 'visibility': 1};
+ filter[feature.getGeometryName()] = 1;
+ var keys = Object.keys(properties || {}).sort().filter(function(v) {
+ return !filter[v];
+ });
+
+ if (keys.length > 0) {
+ var sequence = ol.xml.makeSequence(properties, keys);
+ var namesAndValues = {names: keys, values: sequence};
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_,
+ ol.format.KML.EXTENDEDDATA_NODE_FACTORY_, [namesAndValues], objectStack);
+ }
+
+ var styleFunction = feature.getStyleFunction();
+ if (styleFunction) {
+ // FIXME the styles returned by the style function are supposed to be
+ // resolution-independent here
+ var styles = styleFunction.call(feature, 0);
+ if (styles) {
+ var style = Array.isArray(styles) ? styles[0] : styles;
+ if (this.writeStyles_) {
+ properties['Style'] = style;
+ }
+ var textStyle = style.getText();
+ if (textStyle) {
+ properties['name'] = textStyle.getText();
+ }
+ }
+ }
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.KML.PLACEMARK_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
+
+ // serialize geometry
+ var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ geometry =
+ ol.format.Feature.transformWithOptions(geometry, true, options);
+ }
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_,
+ ol.format.KML.GEOMETRY_NODE_FACTORY_, [geometry], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.SimpleGeometry} geometry Geometry.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writePrimitiveGeometry_ = function(node, geometry, objectStack) {
+ ol.DEBUG && console.assert(
+ (geometry instanceof ol.geom.Point) ||
+ (geometry instanceof ol.geom.LineString) ||
+ (geometry instanceof ol.geom.LinearRing),
+ 'geometry should be one of ol.geom.Point, ol.geom.LineString ' +
+ 'or ol.geom.LinearRing');
+ var flatCoordinates = geometry.getFlatCoordinates();
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ context['layout'] = geometry.getLayout();
+ context['stride'] = geometry.getStride();
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_,
+ ol.format.KML.COORDINATES_NODE_FACTORY_,
+ [flatCoordinates], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.geom.Polygon} polygon Polygon.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writePolygon_ = function(node, polygon, objectStack) {
+ var linearRings = polygon.getLinearRings();
+ ol.DEBUG && console.assert(linearRings.length > 0,
+ 'linearRings should not be empty');
+ var outerRing = linearRings.shift();
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ // inner rings
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.KML.POLYGON_SERIALIZERS_,
+ ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_,
+ linearRings, objectStack);
+ // outer ring
+ ol.xml.pushSerializeAndPop(context,
+ ol.format.KML.POLYGON_SERIALIZERS_,
+ ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_,
+ [outerRing], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.style.Fill} style Style.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writePolyStyle_ = function(node, style, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.POLY_STYLE_SERIALIZERS_,
+ ol.format.KML.COLOR_NODE_FACTORY_, [style.getColor()], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the scale to.
+ * @param {number|undefined} scale Scale.
+ * @private
+ */
+ol.format.KML.writeScaleTextNode_ = function(node, scale) {
+ // the Math is to remove any excess decimals created by float arithmetic
+ ol.format.XSD.writeDecimalTextNode(node,
+ Math.round(scale * 1e6) / 1e6);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.style.Style} style Style.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.KML.writeStyle_ = function(node, style, objectStack) {
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: node};
+ var properties = {};
+ var fillStyle = style.getFill();
+ var strokeStyle = style.getStroke();
+ var imageStyle = style.getImage();
+ var textStyle = style.getText();
+ if (imageStyle instanceof ol.style.Icon) {
+ properties['IconStyle'] = imageStyle;
+ }
+ if (textStyle) {
+ properties['LabelStyle'] = textStyle;
+ }
+ if (strokeStyle) {
+ properties['LineStyle'] = strokeStyle;
+ }
+ if (fillStyle) {
+ properties['PolyStyle'] = fillStyle;
+ }
+ var parentNode = objectStack[objectStack.length - 1].node;
+ var orderedKeys = ol.format.KML.STYLE_SEQUENCE_[parentNode.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.STYLE_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
+};
+
+
+/**
+ * @param {Node} node Node to append a TextNode with the Vec2 to.
+ * @param {ol.KMLVec2_} vec2 Vec2.
+ * @private
+ */
+ol.format.KML.writeVec2_ = function(node, vec2) {
+ node.setAttribute('x', vec2.x);
+ node.setAttribute('y', vec2.y);
+ node.setAttribute('xunits', vec2.xunits);
+ node.setAttribute('yunits', vec2.yunits);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.KML_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'Document', 'Placemark'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.KML_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Document': ol.xml.makeChildAppender(ol.format.KML.writeDocument_),
+ 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.DOCUMENT_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Data': ol.xml.makeChildAppender(ol.format.KML.writeDataNode_),
+ 'value': ol.xml.makeChildAppender(ol.format.KML.writeDataNodeValue_),
+ 'displayName': ol.xml.makeChildAppender(ol.format.KML.writeDataNodeName_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, string>}
+ * @private
+ */
+ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_ = {
+ 'Point': 'Point',
+ 'LineString': 'LineString',
+ 'LinearRing': 'LinearRing',
+ 'Polygon': 'Polygon',
+ 'MultiPoint': 'MultiGeometry',
+ 'MultiLineString': 'MultiGeometry',
+ 'MultiPolygon': 'MultiGeometry',
+ 'GeometryCollection': 'MultiGeometry'
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.ICON_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'href'
+ ],
+ ol.xml.makeStructureNS(ol.format.KML.GX_NAMESPACE_URIS_, [
+ 'x', 'y', 'w', 'h'
+ ]));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.ICON_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'href': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
+ }, ol.xml.makeStructureNS(
+ ol.format.KML.GX_NAMESPACE_URIS_, {
+ 'x': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'y': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'w': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'h': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode)
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.ICON_STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'scale', 'heading', 'Icon', 'hotSpot'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.ICON_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'Icon': ol.xml.makeChildAppender(ol.format.KML.writeIcon_),
+ 'heading': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
+ 'hotSpot': ol.xml.makeChildAppender(ol.format.KML.writeVec2_),
+ 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.LABEL_STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'color', 'scale'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.LABEL_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_),
+ 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.LINE_STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'color', 'width'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.LINE_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_),
+ 'width': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.BOUNDARY_IS_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'LinearRing': ol.xml.makeChildAppender(
+ ol.format.KML.writePrimitiveGeometry_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'LineString': ol.xml.makeChildAppender(
+ ol.format.KML.writePrimitiveGeometry_),
+ 'Point': ol.xml.makeChildAppender(
+ ol.format.KML.writePrimitiveGeometry_),
+ 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_),
+ 'GeometryCollection': ol.xml.makeChildAppender(
+ ol.format.KML.writeMultiGeometry_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.PLACEMARK_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'name', 'open', 'visibility', 'address', 'phoneNumber', 'description',
+ 'styleUrl', 'Style'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.PLACEMARK_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'ExtendedData': ol.xml.makeChildAppender(
+ ol.format.KML.writeExtendedData_),
+ 'MultiGeometry': ol.xml.makeChildAppender(
+ ol.format.KML.writeMultiGeometry_),
+ 'LineString': ol.xml.makeChildAppender(
+ ol.format.KML.writePrimitiveGeometry_),
+ 'LinearRing': ol.xml.makeChildAppender(
+ ol.format.KML.writePrimitiveGeometry_),
+ 'Point': ol.xml.makeChildAppender(
+ ol.format.KML.writePrimitiveGeometry_),
+ 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_),
+ 'Style': ol.xml.makeChildAppender(ol.format.KML.writeStyle_),
+ 'address': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'description': ol.xml.makeChildAppender(
+ ol.format.XSD.writeStringTextNode),
+ 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'open': ol.xml.makeChildAppender(ol.format.XSD.writeBooleanTextNode),
+ 'phoneNumber': ol.xml.makeChildAppender(
+ ol.format.XSD.writeStringTextNode),
+ 'styleUrl': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
+ 'visibility': ol.xml.makeChildAppender(
+ ol.format.XSD.writeBooleanTextNode)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'coordinates': ol.xml.makeChildAppender(
+ ol.format.KML.writeCoordinatesTextNode_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.POLYGON_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'outerBoundaryIs': ol.xml.makeChildAppender(
+ ol.format.KML.writeBoundaryIs_),
+ 'innerBoundaryIs': ol.xml.makeChildAppender(
+ ol.format.KML.writeBoundaryIs_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.POLY_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Array.<string>>}
+ * @private
+ */
+ol.format.KML.STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, [
+ 'IconStyle', 'LabelStyle', 'LineStyle', 'PolyStyle'
+ ]);
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.KML.STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
+ ol.format.KML.NAMESPACE_URIS_, {
+ 'IconStyle': ol.xml.makeChildAppender(ol.format.KML.writeIconStyle_),
+ 'LabelStyle': ol.xml.makeChildAppender(ol.format.KML.writeLabelStyle_),
+ 'LineStyle': ol.xml.makeChildAppender(ol.format.KML.writeLineStyle_),
+ 'PolyStyle': ol.xml.makeChildAppender(ol.format.KML.writePolyStyle_)
+ });
+
+
+/**
+ * @const
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node|undefined} Node.
+ * @private
+ */
+ol.format.KML.GX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
+ return ol.xml.createElementNS(ol.format.KML.GX_NAMESPACE_URIS_[0],
+ 'gx:' + opt_nodeName);
+};
+
+
+/**
+ * @const
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node|undefined} Node.
+ * @private
+ */
+ol.format.KML.DOCUMENT_NODE_FACTORY_ = function(value, objectStack,
+ opt_nodeName) {
+ var parentNode = objectStack[objectStack.length - 1].node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode),
+ 'parentNode should be an XML node');
+ return ol.xml.createElementNS(parentNode.namespaceURI, 'Placemark');
+};
+
+
+/**
+ * @const
+ * @param {*} value Value.
+ * @param {Array.<*>} objectStack Object stack.
+ * @param {string=} opt_nodeName Node name.
+ * @return {Node|undefined} Node.
+ * @private
+ */
+ol.format.KML.GEOMETRY_NODE_FACTORY_ = function(value, objectStack,
+ opt_nodeName) {
+ if (value) {
+ var parentNode = objectStack[objectStack.length - 1].node;
+ ol.DEBUG && console.assert(ol.xml.isNode(parentNode),
+ 'parentNode should be an XML node');
+ return ol.xml.createElementNS(parentNode.namespaceURI,
+ ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_[/** @type {ol.geom.Geometry} */ (value).getType()]);
+ }
+};
+
+
+/**
+ * A factory for creating coordinates nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.COLOR_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('color');
+
+
+/**
+ * A factory for creating coordinates nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.COORDINATES_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('coordinates');
+
+
+/**
+ * A factory for creating Data nodes.
+ * @const
+ * @type {function(*, Array.<*>): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.DATA_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('Data');
+
+
+/**
+ * A factory for creating ExtendedData nodes.
+ * @const
+ * @type {function(*, Array.<*>): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.EXTENDEDDATA_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('ExtendedData');
+
+
+/**
+ * A factory for creating innerBoundaryIs nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('innerBoundaryIs');
+
+
+/**
+ * A factory for creating Point nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.POINT_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('Point');
+
+
+/**
+ * A factory for creating LineString nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.LINE_STRING_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('LineString');
+
+
+/**
+ * A factory for creating LinearRing nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.LINEAR_RING_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('LinearRing');
+
+
+/**
+ * A factory for creating Polygon nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.POLYGON_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('Polygon');
+
+
+/**
+ * A factory for creating outerBoundaryIs nodes.
+ * @const
+ * @type {function(*, Array.<*>, string=): (Node|undefined)}
+ * @private
+ */
+ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_ =
+ ol.xml.makeSimpleNodeFactory('outerBoundaryIs');
+
+
+/**
+ * Encode an array of features in the KML format. GeometryCollections, MultiPoints,
+ * MultiLineStrings, and MultiPolygons are output as MultiGeometries.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {string} Result.
+ * @api stable
+ */
+ol.format.KML.prototype.writeFeatures;
+
+
+/**
+ * Encode an array of features in the KML format as an XML node. GeometryCollections,
+ * MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries.
+ *
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Options.
+ * @return {Node} Node.
+ * @api
+ */
+ol.format.KML.prototype.writeFeaturesNode = function(features, opt_options) {
+ opt_options = this.adaptOptions(opt_options);
+ var kml = ol.xml.createElementNS(ol.format.KML.NAMESPACE_URIS_[4], 'kml');
+ var xmlnsUri = 'http://www.w3.org/2000/xmlns/';
+ var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance';
+ ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:gx',
+ ol.format.KML.GX_NAMESPACE_URIS_[0]);
+ ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri);
+ ol.xml.setAttributeNS(kml, xmlSchemaInstanceUri, 'xsi:schemaLocation',
+ ol.format.KML.SCHEMA_LOCATION_);
+
+ var /** @type {ol.XmlNodeStackItem} */ context = {node: kml};
+ var properties = {};
+ if (features.length > 1) {
+ properties['Document'] = features;
+ } else if (features.length == 1) {
+ properties['Placemark'] = features[0];
+ }
+ var orderedKeys = ol.format.KML.KML_SEQUENCE_[kml.namespaceURI];
+ var values = ol.xml.makeSequence(properties, orderedKeys);
+ ol.xml.pushSerializeAndPop(context, ol.format.KML.KML_SERIALIZERS_,
+ ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys,
+ this);
+ return kml;
+};
+
+goog.provide('ol.ext.pbf');
+/** @typedef {function(*)} */
+ol.ext.pbf;
+(function() {
+var exports = {};
+var module = {exports: exports};
+var define;
+/**
+ * @fileoverview
+ * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility}
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pbf = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+exports.read = function (buffer, offset, isLE, mLen, nBytes) {
+ var e, m
+ var eLen = nBytes * 8 - mLen - 1
+ var eMax = (1 << eLen) - 1
+ var eBias = eMax >> 1
+ var nBits = -7
+ var i = isLE ? (nBytes - 1) : 0
+ var d = isLE ? -1 : 1
+ var s = buffer[offset + i]
+
+ i += d
+
+ e = s & ((1 << (-nBits)) - 1)
+ s >>= (-nBits)
+ nBits += eLen
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
+
+ m = e & ((1 << (-nBits)) - 1)
+ e >>= (-nBits)
+ nBits += mLen
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
+
+ if (e === 0) {
+ e = 1 - eBias
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity)
+ } else {
+ m = m + Math.pow(2, mLen)
+ e = e - eBias
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
+}
+
+exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
+ var e, m, c
+ var eLen = nBytes * 8 - mLen - 1
+ var eMax = (1 << eLen) - 1
+ var eBias = eMax >> 1
+ var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
+ var i = isLE ? 0 : (nBytes - 1)
+ var d = isLE ? 1 : -1
+ var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
+
+ value = Math.abs(value)
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0
+ e = eMax
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2)
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--
+ c *= 2
+ }
+ if (e + eBias >= 1) {
+ value += rt / c
+ } else {
+ value += rt * Math.pow(2, 1 - eBias)
+ }
+ if (value * c >= 2) {
+ e++
+ c /= 2
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0
+ e = eMax
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen)
+ e = e + eBias
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
+ e = 0
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
+
+ e = (e << mLen) | m
+ eLen += mLen
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
+
+ buffer[offset + i - d] |= s * 128
+}
+
+},{}],2:[function(_dereq_,module,exports){
+'use strict';
+
+module.exports = Pbf;
+
+var ieee754 = _dereq_('ieee754');
+
+function Pbf(buf) {
+ this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
+ this.pos = 0;
+ this.type = 0;
+ this.length = this.buf.length;
+}
+
+Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
+Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
+Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
+Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
+
+var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
+ SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
+
+Pbf.prototype = {
+
+ destroy: function() {
+ this.buf = null;
+ },
+
+ // === READING =================================================================
+
+ readFields: function(readField, result, end) {
+ end = end || this.length;
+
+ while (this.pos < end) {
+ var val = this.readVarint(),
+ tag = val >> 3,
+ startPos = this.pos;
+
+ this.type = val & 0x7;
+ readField(tag, result, this);
+
+ if (this.pos === startPos) this.skip(val);
+ }
+ return result;
+ },
+
+ readMessage: function(readField, result) {
+ return this.readFields(readField, result, this.readVarint() + this.pos);
+ },
+
+ readFixed32: function() {
+ var val = readUInt32(this.buf, this.pos);
+ this.pos += 4;
+ return val;
+ },
+
+ readSFixed32: function() {
+ var val = readInt32(this.buf, this.pos);
+ this.pos += 4;
+ return val;
+ },
+
+ // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
+
+ readFixed64: function() {
+ var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
+ this.pos += 8;
+ return val;
+ },
+
+ readSFixed64: function() {
+ var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
+ this.pos += 8;
+ return val;
+ },
+
+ readFloat: function() {
+ var val = ieee754.read(this.buf, this.pos, true, 23, 4);
+ this.pos += 4;
+ return val;
+ },
+
+ readDouble: function() {
+ var val = ieee754.read(this.buf, this.pos, true, 52, 8);
+ this.pos += 8;
+ return val;
+ },
+
+ readVarint: function(isSigned) {
+ var buf = this.buf,
+ val, b;
+
+ b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val;
+ b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val;
+ b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val;
+ b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val;
+ b = buf[this.pos]; val |= (b & 0x0f) << 28;
+
+ return readVarintRemainder(val, isSigned, this);
+ },
+
+ readVarint64: function() { // for compatibility with v2.0.1
+ return this.readVarint(true);
+ },
+
+ readSVarint: function() {
+ var num = this.readVarint();
+ return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
+ },
+
+ readBoolean: function() {
+ return Boolean(this.readVarint());
+ },
+
+ readString: function() {
+ var end = this.readVarint() + this.pos,
+ str = readUtf8(this.buf, this.pos, end);
+ this.pos = end;
+ return str;
+ },
+
+ readBytes: function() {
+ var end = this.readVarint() + this.pos,
+ buffer = this.buf.subarray(this.pos, end);
+ this.pos = end;
+ return buffer;
+ },
+
+ // verbose for performance reasons; doesn't affect gzipped size
+
+ readPackedVarint: function(arr, isSigned) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readVarint(isSigned));
+ return arr;
+ },
+ readPackedSVarint: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readSVarint());
+ return arr;
+ },
+ readPackedBoolean: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readBoolean());
+ return arr;
+ },
+ readPackedFloat: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readFloat());
+ return arr;
+ },
+ readPackedDouble: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readDouble());
+ return arr;
+ },
+ readPackedFixed32: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readFixed32());
+ return arr;
+ },
+ readPackedSFixed32: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readSFixed32());
+ return arr;
+ },
+ readPackedFixed64: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readFixed64());
+ return arr;
+ },
+ readPackedSFixed64: function(arr) {
+ var end = readPackedEnd(this);
+ arr = arr || [];
+ while (this.pos < end) arr.push(this.readSFixed64());
+ return arr;
+ },
+
+ skip: function(val) {
+ var type = val & 0x7;
+ if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {}
+ else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos;
+ else if (type === Pbf.Fixed32) this.pos += 4;
+ else if (type === Pbf.Fixed64) this.pos += 8;
+ else throw new Error('Unimplemented type: ' + type);
+ },
+
+ // === WRITING =================================================================
+
+ writeTag: function(tag, type) {
+ this.writeVarint((tag << 3) | type);
+ },
+
+ realloc: function(min) {
+ var length = this.length || 16;
+
+ while (length < this.pos + min) length *= 2;
+
+ if (length !== this.length) {
+ var buf = new Uint8Array(length);
+ buf.set(this.buf);
+ this.buf = buf;
+ this.length = length;
+ }
+ },
+
+ finish: function() {
+ this.length = this.pos;
+ this.pos = 0;
+ return this.buf.subarray(0, this.length);
+ },
+
+ writeFixed32: function(val) {
+ this.realloc(4);
+ writeInt32(this.buf, val, this.pos);
+ this.pos += 4;
+ },
+
+ writeSFixed32: function(val) {
+ this.realloc(4);
+ writeInt32(this.buf, val, this.pos);
+ this.pos += 4;
+ },
+
+ writeFixed64: function(val) {
+ this.realloc(8);
+ writeInt32(this.buf, val & -1, this.pos);
+ writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
+ this.pos += 8;
+ },
+
+ writeSFixed64: function(val) {
+ this.realloc(8);
+ writeInt32(this.buf, val & -1, this.pos);
+ writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
+ this.pos += 8;
+ },
+
+ writeVarint: function(val) {
+ val = +val || 0;
+
+ if (val > 0xfffffff || val < 0) {
+ writeBigVarint(val, this);
+ return;
+ }
+
+ this.realloc(4);
+
+ this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
+ this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
+ this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
+ this.buf[this.pos++] = (val >>> 7) & 0x7f;
+ },
+
+ writeSVarint: function(val) {
+ this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
+ },
+
+ writeBoolean: function(val) {
+ this.writeVarint(Boolean(val));
+ },
+
+ writeString: function(str) {
+ str = String(str);
+ this.realloc(str.length * 4);
+
+ this.pos++; // reserve 1 byte for short string length
+
+ var startPos = this.pos;
+ // write the string directly to the buffer and see how much was written
+ this.pos = writeUtf8(this.buf, str, this.pos);
+ var len = this.pos - startPos;
+
+ if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);
+
+ // finally, write the message length in the reserved place and restore the position
+ this.pos = startPos - 1;
+ this.writeVarint(len);
+ this.pos += len;
+ },
+
+ writeFloat: function(val) {
+ this.realloc(4);
+ ieee754.write(this.buf, val, this.pos, true, 23, 4);
+ this.pos += 4;
+ },
+
+ writeDouble: function(val) {
+ this.realloc(8);
+ ieee754.write(this.buf, val, this.pos, true, 52, 8);
+ this.pos += 8;
+ },
+
+ writeBytes: function(buffer) {
+ var len = buffer.length;
+ this.writeVarint(len);
+ this.realloc(len);
+ for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i];
+ },
+
+ writeRawMessage: function(fn, obj) {
+ this.pos++; // reserve 1 byte for short message length
+
+ // write the message directly to the buffer and see how much was written
+ var startPos = this.pos;
+ fn(obj, this);
+ var len = this.pos - startPos;
+
+ if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);
+
+ // finally, write the message length in the reserved place and restore the position
+ this.pos = startPos - 1;
+ this.writeVarint(len);
+ this.pos += len;
+ },
+
+ writeMessage: function(tag, fn, obj) {
+ this.writeTag(tag, Pbf.Bytes);
+ this.writeRawMessage(fn, obj);
+ },
+
+ writePackedVarint: function(tag, arr) { this.writeMessage(tag, writePackedVarint, arr); },
+ writePackedSVarint: function(tag, arr) { this.writeMessage(tag, writePackedSVarint, arr); },
+ writePackedBoolean: function(tag, arr) { this.writeMessage(tag, writePackedBoolean, arr); },
+ writePackedFloat: function(tag, arr) { this.writeMessage(tag, writePackedFloat, arr); },
+ writePackedDouble: function(tag, arr) { this.writeMessage(tag, writePackedDouble, arr); },
+ writePackedFixed32: function(tag, arr) { this.writeMessage(tag, writePackedFixed32, arr); },
+ writePackedSFixed32: function(tag, arr) { this.writeMessage(tag, writePackedSFixed32, arr); },
+ writePackedFixed64: function(tag, arr) { this.writeMessage(tag, writePackedFixed64, arr); },
+ writePackedSFixed64: function(tag, arr) { this.writeMessage(tag, writePackedSFixed64, arr); },
+
+ writeBytesField: function(tag, buffer) {
+ this.writeTag(tag, Pbf.Bytes);
+ this.writeBytes(buffer);
+ },
+ writeFixed32Field: function(tag, val) {
+ this.writeTag(tag, Pbf.Fixed32);
+ this.writeFixed32(val);
+ },
+ writeSFixed32Field: function(tag, val) {
+ this.writeTag(tag, Pbf.Fixed32);
+ this.writeSFixed32(val);
+ },
+ writeFixed64Field: function(tag, val) {
+ this.writeTag(tag, Pbf.Fixed64);
+ this.writeFixed64(val);
+ },
+ writeSFixed64Field: function(tag, val) {
+ this.writeTag(tag, Pbf.Fixed64);
+ this.writeSFixed64(val);
+ },
+ writeVarintField: function(tag, val) {
+ this.writeTag(tag, Pbf.Varint);
+ this.writeVarint(val);
+ },
+ writeSVarintField: function(tag, val) {
+ this.writeTag(tag, Pbf.Varint);
+ this.writeSVarint(val);
+ },
+ writeStringField: function(tag, str) {
+ this.writeTag(tag, Pbf.Bytes);
+ this.writeString(str);
+ },
+ writeFloatField: function(tag, val) {
+ this.writeTag(tag, Pbf.Fixed32);
+ this.writeFloat(val);
+ },
+ writeDoubleField: function(tag, val) {
+ this.writeTag(tag, Pbf.Fixed64);
+ this.writeDouble(val);
+ },
+ writeBooleanField: function(tag, val) {
+ this.writeVarintField(tag, Boolean(val));
+ }
+};
+
+function readVarintRemainder(l, s, p) {
+ var buf = p.buf,
+ h, b;
+
+ b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s);
+ b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s);
+ b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s);
+ b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s);
+ b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s);
+ b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s);
+
+ throw new Error('Expected varint not more than 10 bytes');
+}
+
+function readPackedEnd(pbf) {
+ return pbf.type === Pbf.Bytes ?
+ pbf.readVarint() + pbf.pos : pbf.pos + 1;
+}
+
+function toNum(low, high, isSigned) {
+ if (isSigned) {
+ return high * 0x100000000 + (low >>> 0);
+ }
+
+ return ((high >>> 0) * 0x100000000) + (low >>> 0);
+}
+
+function writeBigVarint(val, pbf) {
+ var low, high;
+
+ if (val >= 0) {
+ low = (val % 0x100000000) | 0;
+ high = (val / 0x100000000) | 0;
+ } else {
+ low = ~(-val % 0x100000000);
+ high = ~(-val / 0x100000000);
+
+ if (low ^ 0xffffffff) {
+ low = (low + 1) | 0;
+ } else {
+ low = 0;
+ high = (high + 1) | 0;
+ }
+ }
+
+ if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
+ throw new Error('Given varint doesn\'t fit into 10 bytes');
+ }
+
+ pbf.realloc(10);
+
+ writeBigVarintLow(low, high, pbf);
+ writeBigVarintHigh(high, pbf);
+}
+
+function writeBigVarintLow(low, high, pbf) {
+ pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
+ pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
+ pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
+ pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
+ pbf.buf[pbf.pos] = low & 0x7f;
+}
+
+function writeBigVarintHigh(high, pbf) {
+ var lsb = (high & 0x07) << 4;
+
+ pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return;
+ pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
+ pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
+ pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
+ pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
+ pbf.buf[pbf.pos++] = high & 0x7f;
+}
+
+function makeRoomForExtraLength(startPos, len, pbf) {
+ var extraLen =
+ len <= 0x3fff ? 1 :
+ len <= 0x1fffff ? 2 :
+ len <= 0xfffffff ? 3 : Math.ceil(Math.log(len) / (Math.LN2 * 7));
+
+ // if 1 byte isn't enough for encoding message length, shift the data to the right
+ pbf.realloc(extraLen);
+ for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i];
+}
+
+function writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); }
+function writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); }
+function writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); }
+function writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); }
+function writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); }
+function writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); }
+function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); }
+function writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); }
+function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); }
+
+// Buffer code below from https://github.com/feross/buffer, MIT-licensed
+
+function readUInt32(buf, pos) {
+ return ((buf[pos]) |
+ (buf[pos + 1] << 8) |
+ (buf[pos + 2] << 16)) +
+ (buf[pos + 3] * 0x1000000);
+}
+
+function writeInt32(buf, val, pos) {
+ buf[pos] = val;
+ buf[pos + 1] = (val >>> 8);
+ buf[pos + 2] = (val >>> 16);
+ buf[pos + 3] = (val >>> 24);
+}
+
+function readInt32(buf, pos) {
+ return ((buf[pos]) |
+ (buf[pos + 1] << 8) |
+ (buf[pos + 2] << 16)) +
+ (buf[pos + 3] << 24);
+}
+
+function readUtf8(buf, pos, end) {
+ var str = '';
+ var i = pos;
+
+ while (i < end) {
+ var b0 = buf[i];
+ var c = null; // codepoint
+ var bytesPerSequence =
+ b0 > 0xEF ? 4 :
+ b0 > 0xDF ? 3 :
+ b0 > 0xBF ? 2 : 1;
+
+ if (i + bytesPerSequence > end) break;
+
+ var b1, b2, b3;
+
+ if (bytesPerSequence === 1) {
+ if (b0 < 0x80) {
+ c = b0;
+ }
+ } else if (bytesPerSequence === 2) {
+ b1 = buf[i + 1];
+ if ((b1 & 0xC0) === 0x80) {
+ c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
+ if (c <= 0x7F) {
+ c = null;
+ }
+ }
+ } else if (bytesPerSequence === 3) {
+ b1 = buf[i + 1];
+ b2 = buf[i + 2];
+ if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
+ c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);
+ if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {
+ c = null;
+ }
+ }
+ } else if (bytesPerSequence === 4) {
+ b1 = buf[i + 1];
+ b2 = buf[i + 2];
+ b3 = buf[i + 3];
+ if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
+ c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);
+ if (c <= 0xFFFF || c >= 0x110000) {
+ c = null;
+ }
+ }
+ }
+
+ if (c === null) {
+ c = 0xFFFD;
+ bytesPerSequence = 1;
+
+ } else if (c > 0xFFFF) {
+ c -= 0x10000;
+ str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
+ c = 0xDC00 | c & 0x3FF;
+ }
+
+ str += String.fromCharCode(c);
+ i += bytesPerSequence;
+ }
+
+ return str;
+}
+
+function writeUtf8(buf, str, pos) {
+ for (var i = 0, c, lead; i < str.length; i++) {
+ c = str.charCodeAt(i); // code point
+
+ if (c > 0xD7FF && c < 0xE000) {
+ if (lead) {
+ if (c < 0xDC00) {
+ buf[pos++] = 0xEF;
+ buf[pos++] = 0xBF;
+ buf[pos++] = 0xBD;
+ lead = c;
+ continue;
+ } else {
+ c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
+ lead = null;
+ }
+ } else {
+ if (c > 0xDBFF || (i + 1 === str.length)) {
+ buf[pos++] = 0xEF;
+ buf[pos++] = 0xBF;
+ buf[pos++] = 0xBD;
+ } else {
+ lead = c;
+ }
+ continue;
+ }
+ } else if (lead) {
+ buf[pos++] = 0xEF;
+ buf[pos++] = 0xBF;
+ buf[pos++] = 0xBD;
+ lead = null;
+ }
+
+ if (c < 0x80) {
+ buf[pos++] = c;
+ } else {
+ if (c < 0x800) {
+ buf[pos++] = c >> 0x6 | 0xC0;
+ } else {
+ if (c < 0x10000) {
+ buf[pos++] = c >> 0xC | 0xE0;
+ } else {
+ buf[pos++] = c >> 0x12 | 0xF0;
+ buf[pos++] = c >> 0xC & 0x3F | 0x80;
+ }
+ buf[pos++] = c >> 0x6 & 0x3F | 0x80;
+ }
+ buf[pos++] = c & 0x3F | 0x80;
+ }
+ }
+ return pos;
+}
+
+},{"ieee754":1}]},{},[2])(2)
+});
+ol.ext.pbf = module.exports;
+})();
+
+goog.provide('ol.ext.vectortile');
+/** @typedef {function(*)} */
+ol.ext.vectortile;
+(function() {
+var exports = {};
+var module = {exports: exports};
+var define;
+/**
+ * @fileoverview
+ * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility}
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.vectortile = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+'use strict';
+
+module.exports = Point;
+
+function Point(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+Point.prototype = {
+ clone: function() { return new Point(this.x, this.y); },
+
+ add: function(p) { return this.clone()._add(p); },
+ sub: function(p) { return this.clone()._sub(p); },
+ mult: function(k) { return this.clone()._mult(k); },
+ div: function(k) { return this.clone()._div(k); },
+ rotate: function(a) { return this.clone()._rotate(a); },
+ matMult: function(m) { return this.clone()._matMult(m); },
+ unit: function() { return this.clone()._unit(); },
+ perp: function() { return this.clone()._perp(); },
+ round: function() { return this.clone()._round(); },
+
+ mag: function() {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ },
+
+ equals: function(p) {
+ return this.x === p.x &&
+ this.y === p.y;
+ },
+
+ dist: function(p) {
+ return Math.sqrt(this.distSqr(p));
+ },
+
+ distSqr: function(p) {
+ var dx = p.x - this.x,
+ dy = p.y - this.y;
+ return dx * dx + dy * dy;
+ },
+
+ angle: function() {
+ return Math.atan2(this.y, this.x);
+ },
+
+ angleTo: function(b) {
+ return Math.atan2(this.y - b.y, this.x - b.x);
+ },
+
+ angleWith: function(b) {
+ return this.angleWithSep(b.x, b.y);
+ },
+
+ // Find the angle of the two vectors, solving the formula for the cross product a x b = |a||b|sin(θ) for θ.
+ angleWithSep: function(x, y) {
+ return Math.atan2(
+ this.x * y - this.y * x,
+ this.x * x + this.y * y);
+ },
+
+ _matMult: function(m) {
+ var x = m[0] * this.x + m[1] * this.y,
+ y = m[2] * this.x + m[3] * this.y;
+ this.x = x;
+ this.y = y;
+ return this;
+ },
+
+ _add: function(p) {
+ this.x += p.x;
+ this.y += p.y;
+ return this;
+ },
+
+ _sub: function(p) {
+ this.x -= p.x;
+ this.y -= p.y;
+ return this;
+ },
+
+ _mult: function(k) {
+ this.x *= k;
+ this.y *= k;
+ return this;
+ },
+
+ _div: function(k) {
+ this.x /= k;
+ this.y /= k;
+ return this;
+ },
+
+ _unit: function() {
+ this._div(this.mag());
+ return this;
+ },
+
+ _perp: function() {
+ var y = this.y;
+ this.y = this.x;
+ this.x = -y;
+ return this;
+ },
+
+ _rotate: function(angle) {
+ var cos = Math.cos(angle),
+ sin = Math.sin(angle),
+ x = cos * this.x - sin * this.y,
+ y = sin * this.x + cos * this.y;
+ this.x = x;
+ this.y = y;
+ return this;
+ },
+
+ _round: function() {
+ this.x = Math.round(this.x);
+ this.y = Math.round(this.y);
+ return this;
+ }
+};
+
+// constructs Point from an array if necessary
+Point.convert = function (a) {
+ if (a instanceof Point) {
+ return a;
+ }
+ if (Array.isArray(a)) {
+ return new Point(a[0], a[1]);
+ }
+ return a;
+};
+
+},{}],2:[function(_dereq_,module,exports){
+module.exports.VectorTile = _dereq_('./lib/vectortile.js');
+module.exports.VectorTileFeature = _dereq_('./lib/vectortilefeature.js');
+module.exports.VectorTileLayer = _dereq_('./lib/vectortilelayer.js');
+
+},{"./lib/vectortile.js":3,"./lib/vectortilefeature.js":4,"./lib/vectortilelayer.js":5}],3:[function(_dereq_,module,exports){
+'use strict';
+
+var VectorTileLayer = _dereq_('./vectortilelayer');
+
+module.exports = VectorTile;
+
+function VectorTile(pbf, end) {
+ this.layers = pbf.readFields(readTile, {}, end);
+}
+
+function readTile(tag, layers, pbf) {
+ if (tag === 3) {
+ var layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
+ if (layer.length) layers[layer.name] = layer;
+ }
+}
+
+
+},{"./vectortilelayer":5}],4:[function(_dereq_,module,exports){
+'use strict';
+
+var Point = _dereq_('point-geometry');
+
+module.exports = VectorTileFeature;
+
+function VectorTileFeature(pbf, end, extent, keys, values) {
+ // Public
+ this.properties = {};
+ this.extent = extent;
+ this.type = 0;
+
+ // Private
+ this._pbf = pbf;
+ this._geometry = -1;
+ this._keys = keys;
+ this._values = values;
+
+ pbf.readFields(readFeature, this, end);
+}
+
+function readFeature(tag, feature, pbf) {
+ if (tag == 1) feature.id = pbf.readVarint();
+ else if (tag == 2) readTag(pbf, feature);
+ else if (tag == 3) feature.type = pbf.readVarint();
+ else if (tag == 4) feature._geometry = pbf.pos;
+}
+
+function readTag(pbf, feature) {
+ var end = pbf.readVarint() + pbf.pos;
+
+ while (pbf.pos < end) {
+ var key = feature._keys[pbf.readVarint()],
+ value = feature._values[pbf.readVarint()];
+ feature.properties[key] = value;
+ }
+}
+
+VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
+
+VectorTileFeature.prototype.loadGeometry = function() {
+ var pbf = this._pbf;
+ pbf.pos = this._geometry;
+
+ var end = pbf.readVarint() + pbf.pos,
+ cmd = 1,
+ length = 0,
+ x = 0,
+ y = 0,
+ lines = [],
+ line;
+
+ while (pbf.pos < end) {
+ if (!length) {
+ var cmdLen = pbf.readVarint();
+ cmd = cmdLen & 0x7;
+ length = cmdLen >> 3;
+ }
+
+ length--;
+
+ if (cmd === 1 || cmd === 2) {
+ x += pbf.readSVarint();
+ y += pbf.readSVarint();
+
+ if (cmd === 1) { // moveTo
+ if (line) lines.push(line);
+ line = [];
+ }
+
+ line.push(new Point(x, y));
+
+ } else if (cmd === 7) {
+
+ // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
+ if (line) {
+ line.push(line[0].clone()); // closePolygon
+ }
+
+ } else {
+ throw new Error('unknown command ' + cmd);
+ }
+ }
+
+ if (line) lines.push(line);
+
+ return lines;
+};
+
+VectorTileFeature.prototype.bbox = function() {
+ var pbf = this._pbf;
+ pbf.pos = this._geometry;
+
+ var end = pbf.readVarint() + pbf.pos,
+ cmd = 1,
+ length = 0,
+ x = 0,
+ y = 0,
+ x1 = Infinity,
+ x2 = -Infinity,
+ y1 = Infinity,
+ y2 = -Infinity;
+
+ while (pbf.pos < end) {
+ if (!length) {
+ var cmdLen = pbf.readVarint();
+ cmd = cmdLen & 0x7;
+ length = cmdLen >> 3;
+ }
+
+ length--;
+
+ if (cmd === 1 || cmd === 2) {
+ x += pbf.readSVarint();
+ y += pbf.readSVarint();
+ if (x < x1) x1 = x;
+ if (x > x2) x2 = x;
+ if (y < y1) y1 = y;
+ if (y > y2) y2 = y;
+
+ } else if (cmd !== 7) {
+ throw new Error('unknown command ' + cmd);
+ }
+ }
+
+ return [x1, y1, x2, y2];
+};
+
+VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
+ var size = this.extent * Math.pow(2, z),
+ x0 = this.extent * x,
+ y0 = this.extent * y,
+ coords = this.loadGeometry(),
+ type = VectorTileFeature.types[this.type],
+ i, j;
+
+ function project(line) {
+ for (var j = 0; j < line.length; j++) {
+ var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
+ line[j] = [
+ (p.x + x0) * 360 / size - 180,
+ 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
+ ];
+ }
+ }
+
+ switch (this.type) {
+ case 1:
+ var points = [];
+ for (i = 0; i < coords.length; i++) {
+ points[i] = coords[i][0];
+ }
+ coords = points;
+ project(coords);
+ break;
+
+ case 2:
+ for (i = 0; i < coords.length; i++) {
+ project(coords[i]);
+ }
+ break;
+
+ case 3:
+ coords = classifyRings(coords);
+ for (i = 0; i < coords.length; i++) {
+ for (j = 0; j < coords[i].length; j++) {
+ project(coords[i][j]);
+ }
+ }
+ break;
+ }
+
+ if (coords.length === 1) {
+ coords = coords[0];
+ } else {
+ type = 'Multi' + type;
+ }
+
+ var result = {
+ type: "Feature",
+ geometry: {
+ type: type,
+ coordinates: coords
+ },
+ properties: this.properties
+ };
+
+ if ('id' in this) {
+ result.id = this.id;
+ }
+
+ return result;
+};
+
+// classifies an array of rings into polygons with outer rings and holes
+
+function classifyRings(rings) {
+ var len = rings.length;
+
+ if (len <= 1) return [rings];
+
+ var polygons = [],
+ polygon,
+ ccw;
+
+ for (var i = 0; i < len; i++) {
+ var area = signedArea(rings[i]);
+ if (area === 0) continue;
+
+ if (ccw === undefined) ccw = area < 0;
+
+ if (ccw === area < 0) {
+ if (polygon) polygons.push(polygon);
+ polygon = [rings[i]];
+
+ } else {
+ polygon.push(rings[i]);
+ }
+ }
+ if (polygon) polygons.push(polygon);
+
+ return polygons;
+}
+
+function signedArea(ring) {
+ var sum = 0;
+ for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
+ p1 = ring[i];
+ p2 = ring[j];
+ sum += (p2.x - p1.x) * (p1.y + p2.y);
+ }
+ return sum;
+}
+
+},{"point-geometry":1}],5:[function(_dereq_,module,exports){
+'use strict';
+
+var VectorTileFeature = _dereq_('./vectortilefeature.js');
+
+module.exports = VectorTileLayer;
+
+function VectorTileLayer(pbf, end) {
+ // Public
+ this.version = 1;
+ this.name = null;
+ this.extent = 4096;
+ this.length = 0;
+
+ // Private
+ this._pbf = pbf;
+ this._keys = [];
+ this._values = [];
+ this._features = [];
+
+ pbf.readFields(readLayer, this, end);
+
+ this.length = this._features.length;
+}
+
+function readLayer(tag, layer, pbf) {
+ if (tag === 15) layer.version = pbf.readVarint();
+ else if (tag === 1) layer.name = pbf.readString();
+ else if (tag === 5) layer.extent = pbf.readVarint();
+ else if (tag === 2) layer._features.push(pbf.pos);
+ else if (tag === 3) layer._keys.push(pbf.readString());
+ else if (tag === 4) layer._values.push(readValueMessage(pbf));
+}
+
+function readValueMessage(pbf) {
+ var value = null,
+ end = pbf.readVarint() + pbf.pos;
+
+ while (pbf.pos < end) {
+ var tag = pbf.readVarint() >> 3;
+
+ value = tag === 1 ? pbf.readString() :
+ tag === 2 ? pbf.readFloat() :
+ tag === 3 ? pbf.readDouble() :
+ tag === 4 ? pbf.readVarint64() :
+ tag === 5 ? pbf.readVarint() :
+ tag === 6 ? pbf.readSVarint() :
+ tag === 7 ? pbf.readBoolean() : null;
+ }
+
+ return value;
+}
+
+// return feature `i` from this layer as a `VectorTileFeature`
+VectorTileLayer.prototype.feature = function(i) {
+ if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
+
+ this._pbf.pos = this._features[i];
+
+ var end = this._pbf.readVarint() + this._pbf.pos;
+ return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);
+};
+
+},{"./vectortilefeature.js":4}]},{},[2])(2)
+});
+ol.ext.vectortile = module.exports;
+})();
+
+goog.provide('ol.render.Feature');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryType');
+
+
+/**
+ * Lightweight, read-only, {@link ol.Feature} and {@link ol.geom.Geometry} like
+ * structure, optimized for rendering and styling. Geometry access through the
+ * API is limited to getting the type and extent of the geometry.
+ *
+ * @constructor
+ * @param {ol.geom.GeometryType} type Geometry type.
+ * @param {Array.<number>} flatCoordinates Flat coordinates. These always need
+ * to be right-handed for polygons.
+ * @param {Array.<number>|Array.<Array.<number>>} ends Ends or Endss.
+ * @param {Object.<string, *>} properties Properties.
+ */
+ol.render.Feature = function(type, flatCoordinates, ends, properties) {
+
+ /**
+ * @private
+ * @type {ol.Extent|undefined}
+ */
+ this.extent_;
+
+ ol.DEBUG && console.assert(type === ol.geom.GeometryType.POINT ||
+ type === ol.geom.GeometryType.MULTI_POINT ||
+ type === ol.geom.GeometryType.LINE_STRING ||
+ type === ol.geom.GeometryType.MULTI_LINE_STRING ||
+ type === ol.geom.GeometryType.POLYGON,
+ 'Need a Point, MultiPoint, LineString, MultiLineString or Polygon type');
+
+ /**
+ * @private
+ * @type {ol.geom.GeometryType}
+ */
+ this.type_ = type;
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.flatCoordinates_ = flatCoordinates;
+
+ /**
+ * @private
+ * @type {Array.<number>|Array.<Array.<number>>}
+ */
+ this.ends_ = ends;
+
+ /**
+ * @private
+ * @type {Object.<string, *>}
+ */
+ this.properties_ = properties;
+
+};
+
+
+/**
+ * Get a feature property by its key.
+ * @param {string} key Key
+ * @return {*} Value for the requested key.
+ * @api
+ */
+ol.render.Feature.prototype.get = function(key) {
+ return this.properties_[key];
+};
+
+
+/**
+ * @return {Array.<number>|Array.<Array.<number>>} Ends or endss.
+ */
+ol.render.Feature.prototype.getEnds = function() {
+ return this.ends_;
+};
+
+
+/**
+ * Get the extent of this feature's geometry.
+ * @return {ol.Extent} Extent.
+ * @api
+ */
+ol.render.Feature.prototype.getExtent = function() {
+ if (!this.extent_) {
+ this.extent_ = this.type_ === ol.geom.GeometryType.POINT ?
+ ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates_) :
+ ol.extent.createOrUpdateFromFlatCoordinates(
+ this.flatCoordinates_, 0, this.flatCoordinates_.length, 2);
+
+ }
+ return this.extent_;
+};
+
+
+/**
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.render.Feature.prototype.getOrientedFlatCoordinates = function() {
+ return this.flatCoordinates_;
+};
+
+
+/**
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.render.Feature.prototype.getFlatCoordinates =
+ ol.render.Feature.prototype.getOrientedFlatCoordinates;
+
+
+/**
+ * Get the feature for working with its geometry.
+ * @return {ol.render.Feature} Feature.
+ * @api
+ */
+ol.render.Feature.prototype.getGeometry = function() {
+ return this;
+};
+
+
+/**
+ * Get the feature properties.
+ * @return {Object.<string, *>} Feature properties.
+ * @api
+ */
+ol.render.Feature.prototype.getProperties = function() {
+ return this.properties_;
+};
+
+
+/**
+ * Get the feature for working with its geometry.
+ * @return {ol.render.Feature} Feature.
+ */
+ol.render.Feature.prototype.getSimplifiedGeometry =
+ ol.render.Feature.prototype.getGeometry;
+
+
+/**
+ * @return {number} Stride.
+ */
+ol.render.Feature.prototype.getStride = function() {
+ return 2;
+};
+
+
+/**
+ * @return {undefined}
+ */
+ol.render.Feature.prototype.getStyleFunction = ol.nullFunction;
+
+
+/**
+ * Get the type of this feature's geometry.
+ * @return {ol.geom.GeometryType} Geometry type.
+ * @api
+ */
+ol.render.Feature.prototype.getType = function() {
+ return this.type_;
+};
+
+//FIXME Implement projection handling
+
+goog.provide('ol.format.MVT');
+
+goog.require('ol');
+goog.require('ol.ext.pbf');
+goog.require('ol.ext.vectortile');
+goog.require('ol.format.Feature');
+goog.require('ol.format.FormatType');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.proj.Projection');
+goog.require('ol.proj.Units');
+goog.require('ol.render.Feature');
+
+
+/**
+ * @classdesc
+ * Feature format for reading data in the Mapbox MVT format.
+ *
+ * @constructor
+ * @extends {ol.format.Feature}
+ * @param {olx.format.MVTOptions=} opt_options Options.
+ * @api
+ */
+ol.format.MVT = function(opt_options) {
+
+ ol.format.Feature.call(this);
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @type {ol.proj.Projection}
+ */
+ this.defaultDataProjection = new ol.proj.Projection({
+ code: '',
+ units: ol.proj.Units.TILE_PIXELS
+ });
+
+ /**
+ * @private
+ * @type {function((ol.geom.Geometry|Object.<string, *>)=)|
+ * function(ol.geom.GeometryType,Array.<number>,
+ * (Array.<number>|Array.<Array.<number>>),Object.<string, *>)}
+ */
+ this.featureClass_ = options.featureClass ?
+ options.featureClass : ol.render.Feature;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.geometryName_ = options.geometryName ?
+ options.geometryName : 'geometry';
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.layerName_ = options.layerName ? options.layerName : 'layer';
+
+ /**
+ * @private
+ * @type {Array.<string>}
+ */
+ this.layers_ = options.layers ? options.layers : null;
+
+};
+ol.inherits(ol.format.MVT, ol.format.Feature);
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.MVT.prototype.getType = function() {
+ return ol.format.FormatType.ARRAY_BUFFER;
+};
+
+
+/**
+ * @private
+ * @param {Object} rawFeature Raw Mapbox feature.
+ * @param {string} layer Layer.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ */
+ol.format.MVT.prototype.readFeature_ = function(
+ rawFeature, layer, opt_options) {
+ var feature = new this.featureClass_();
+ var id = rawFeature.id;
+ var values = rawFeature.properties;
+ values[this.layerName_] = layer;
+ var geometry = ol.format.Feature.transformWithOptions(
+ ol.format.MVT.readGeometry_(rawFeature), false,
+ this.adaptOptions(opt_options));
+ if (geometry) {
+ values[this.geometryName_] = geometry;
+ }
+ feature.setId(id);
+ feature.setProperties(values);
+ feature.setGeometryName(this.geometryName_);
+ return feature;
+};
+
+
+/**
+ * @private
+ * @param {Object} rawFeature Raw Mapbox feature.
+ * @param {string} layer Layer.
+ * @return {ol.render.Feature} Feature.
+ */
+ol.format.MVT.prototype.readRenderFeature_ = function(rawFeature, layer) {
+ var coords = rawFeature.loadGeometry();
+ var ends = [];
+ var flatCoordinates = [];
+ ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends);
+
+ var type = rawFeature.type;
+ /** @type {ol.geom.GeometryType} */
+ var geometryType;
+ if (type === 1) {
+ geometryType = coords.length === 1 ?
+ ol.geom.GeometryType.POINT : ol.geom.GeometryType.MULTI_POINT;
+ } else if (type === 2) {
+ if (coords.length === 1) {
+ geometryType = ol.geom.GeometryType.LINE_STRING;
+ } else {
+ geometryType = ol.geom.GeometryType.MULTI_LINE_STRING;
+ }
+ } else if (type === 3) {
+ geometryType = ol.geom.GeometryType.POLYGON;
+ }
+
+ var values = rawFeature.properties;
+ values[this.layerName_] = layer;
+
+ return new this.featureClass_(geometryType, flatCoordinates, ends, values);
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.format.MVT.prototype.readFeatures = function(source, opt_options) {
+ var layers = this.layers_;
+
+ var pbf = new ol.ext.pbf(/** @type {ArrayBuffer} */ (source));
+ var tile = new ol.ext.vectortile.VectorTile(pbf);
+ var features = [];
+ var featureClass = this.featureClass_;
+ var layer, feature;
+ for (var name in tile.layers) {
+ if (layers && layers.indexOf(name) == -1) {
+ continue;
+ }
+ layer = tile.layers[name];
+
+ for (var i = 0, ii = layer.length; i < ii; ++i) {
+ if (featureClass === ol.render.Feature) {
+ feature = this.readRenderFeature_(layer.feature(i), name);
+ } else {
+ feature = this.readFeature_(layer.feature(i), name, opt_options);
+ }
+ features.push(feature);
+ }
+ }
+
+ return features;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.format.MVT.prototype.readProjection = function(source) {
+ return this.defaultDataProjection;
+};
+
+
+/**
+ * Sets the layers that features will be read from.
+ * @param {Array.<string>} layers Layers.
+ * @api
+ */
+ol.format.MVT.prototype.setLayers = function(layers) {
+ this.layers_ = layers;
+};
+
+
+/**
+ * @private
+ * @param {Object} coords Raw feature coordinates.
+ * @param {Array.<number>} flatCoordinates Flat coordinates to be populated by
+ * this function.
+ * @param {Array.<number>} ends Ends to be populated by this function.
+ */
+ol.format.MVT.calculateFlatCoordinates_ = function(
+ coords, flatCoordinates, ends) {
+ var end = 0;
+ for (var i = 0, ii = coords.length; i < ii; ++i) {
+ var line = coords[i];
+ var j, jj;
+ for (j = 0, jj = line.length; j < jj; ++j) {
+ var coord = line[j];
+ // Non-tilespace coords can be calculated here when a TileGrid and
+ // TileCoord are known.
+ flatCoordinates.push(coord.x, coord.y);
+ }
+ end += 2 * j;
+ ends.push(end);
+ }
+};
+
+
+/**
+ * @private
+ * @param {Object} rawFeature Raw Mapbox feature.
+ * @return {ol.geom.Geometry} Geometry.
+ */
+ol.format.MVT.readGeometry_ = function(rawFeature) {
+ var type = rawFeature.type;
+ if (type === 0) {
+ return null;
+ }
+
+ var coords = rawFeature.loadGeometry();
+ var ends = [];
+ var flatCoordinates = [];
+ ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends);
+
+ var geom;
+ if (type === 1) {
+ geom = coords.length === 1 ?
+ new ol.geom.Point(null) : new ol.geom.MultiPoint(null);
+ } else if (type === 2) {
+ if (coords.length === 1) {
+ geom = new ol.geom.LineString(null);
+ } else {
+ geom = new ol.geom.MultiLineString(null);
+ }
+ } else if (type === 3) {
+ geom = new ol.geom.Polygon(null);
+ }
+
+ geom.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates,
+ ends);
+
+ return geom;
+};
+
+// FIXME add typedef for stack state objects
+goog.provide('ol.format.OSMXML');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.XMLFeature');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Feature format for reading data in the
+ * [OSMXML format](http://wiki.openstreetmap.org/wiki/OSM_XML).
+ *
+ * @constructor
+ * @extends {ol.format.XMLFeature}
+ * @api stable
+ */
+ol.format.OSMXML = function() {
+ ol.format.XMLFeature.call(this);
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get('EPSG:4326');
+};
+ol.inherits(ol.format.OSMXML, ol.format.XMLFeature);
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.OSMXML.EXTENSIONS_ = ['.osm'];
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.OSMXML.prototype.getExtensions = function() {
+ return ol.format.OSMXML.EXTENSIONS_;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.OSMXML.readNode_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'node', 'localName should be node');
+ var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
+ var state = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var id = node.getAttribute('id');
+ /** @type {ol.Coordinate} */
+ var coordinates = [
+ parseFloat(node.getAttribute('lon')),
+ parseFloat(node.getAttribute('lat'))
+ ];
+ state.nodes[id] = coordinates;
+
+ var values = ol.xml.pushParseAndPop({
+ tags: {}
+ }, ol.format.OSMXML.NODE_PARSERS_, node, objectStack);
+ if (!ol.obj.isEmpty(values.tags)) {
+ var geometry = new ol.geom.Point(coordinates);
+ ol.format.Feature.transformWithOptions(geometry, false, options);
+ var feature = new ol.Feature(geometry);
+ feature.setId(id);
+ feature.setProperties(values.tags);
+ state.features.push(feature);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.OSMXML.readWay_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'way', 'localName should be way');
+ var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
+ var id = node.getAttribute('id');
+ var values = ol.xml.pushParseAndPop({
+ ndrefs: [],
+ tags: {}
+ }, ol.format.OSMXML.WAY_PARSERS_, node, objectStack);
+ var state = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ /** @type {Array.<number>} */
+ var flatCoordinates = [];
+ for (var i = 0, ii = values.ndrefs.length; i < ii; i++) {
+ var point = state.nodes[values.ndrefs[i]];
+ ol.array.extend(flatCoordinates, point);
+ }
+ var geometry;
+ if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) {
+ // closed way
+ geometry = new ol.geom.Polygon(null);
+ geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates,
+ [flatCoordinates.length]);
+ } else {
+ geometry = new ol.geom.LineString(null);
+ geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates);
+ }
+ ol.format.Feature.transformWithOptions(geometry, false, options);
+ var feature = new ol.Feature(geometry);
+ feature.setId(id);
+ feature.setProperties(values.tags);
+ state.features.push(feature);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.OSMXML.readNd_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'nd', 'localName should be nd');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ values.ndrefs.push(node.getAttribute('ref'));
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.OSMXML.readTag_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'tag', 'localName should be tag');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ values.tags[node.getAttribute('k')] = node.getAttribute('v');
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.<string>}
+ */
+ol.format.OSMXML.NAMESPACE_URIS_ = [
+ null
+];
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OSMXML.WAY_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OSMXML.NAMESPACE_URIS_, {
+ 'nd': ol.format.OSMXML.readNd_,
+ 'tag': ol.format.OSMXML.readTag_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OSMXML.PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OSMXML.NAMESPACE_URIS_, {
+ 'node': ol.format.OSMXML.readNode_,
+ 'way': ol.format.OSMXML.readWay_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OSMXML.NODE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OSMXML.NAMESPACE_URIS_, {
+ 'tag': ol.format.OSMXML.readTag_
+ });
+
+
+/**
+ * Read all features from an OSM source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.OSMXML.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ var options = this.getReadOptions(node, opt_options);
+ if (node.localName == 'osm') {
+ var state = ol.xml.pushParseAndPop({
+ nodes: {},
+ features: []
+ }, ol.format.OSMXML.PARSERS_, node, [options]);
+ if (state.features) {
+ return state.features;
+ }
+ }
+ return [];
+};
+
+
+/**
+ * Read the projection from an OSM source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.OSMXML.prototype.readProjection;
+
+goog.provide('ol.format.XLink');
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.XLink.NAMESPACE_URI = 'http://www.w3.org/1999/xlink';
+
+
+/**
+ * @param {Node} node Node.
+ * @return {boolean|undefined} Boolean.
+ */
+ol.format.XLink.readHref = function(node) {
+ return node.getAttributeNS(ol.format.XLink.NAMESPACE_URI, 'href');
+};
+
+goog.provide('ol.format.XML');
+
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Generic format for reading non-feature XML data
+ *
+ * @constructor
+ * @struct
+ */
+ol.format.XML = function() {
+};
+
+
+/**
+ * @param {Document|Node|string} source Source.
+ * @return {Object} The parsed result.
+ */
+ol.format.XML.prototype.read = function(source) {
+ if (ol.xml.isDocument(source)) {
+ return this.readFromDocument(/** @type {Document} */ (source));
+ } else if (ol.xml.isNode(source)) {
+ return this.readFromNode(/** @type {Node} */ (source));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readFromDocument(doc);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @abstract
+ * @param {Document} doc Document.
+ * @return {Object} Object
+ */
+ol.format.XML.prototype.readFromDocument = function(doc) {};
+
+
+/**
+ * @abstract
+ * @param {Node} node Node.
+ * @return {Object} Object
+ */
+ol.format.XML.prototype.readFromNode = function(node) {};
+
+goog.provide('ol.format.OWS');
+
+goog.require('ol');
+goog.require('ol.format.XLink');
+goog.require('ol.format.XML');
+goog.require('ol.format.XSD');
+goog.require('ol.xml');
+
+
+/**
+ * @constructor
+ * @extends {ol.format.XML}
+ */
+ol.format.OWS = function() {
+ ol.format.XML.call(this);
+};
+ol.inherits(ol.format.OWS, ol.format.XML);
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {Object} OWS object.
+ */
+ol.format.OWS.prototype.readFromDocument = function(doc) {
+ ol.DEBUG && console.assert(doc.nodeType == Node.DOCUMENT_NODE,
+ 'doc.nodeType should be DOCUMENT');
+ for (var n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ return this.readFromNode(n);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {Object} OWS object.
+ */
+ol.format.OWS.prototype.readFromNode = function(node) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ var owsObject = ol.xml.pushParseAndPop({},
+ ol.format.OWS.PARSERS_, node, []);
+ return owsObject ? owsObject : null;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The address.
+ */
+ol.format.OWS.readAddress_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Address',
+ 'localName should be Address');
+ return ol.xml.pushParseAndPop({},
+ ol.format.OWS.ADDRESS_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The values.
+ */
+ol.format.OWS.readAllowedValues_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'AllowedValues',
+ 'localName should be AllowedValues');
+ return ol.xml.pushParseAndPop({},
+ ol.format.OWS.ALLOWED_VALUES_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The constraint.
+ */
+ol.format.OWS.readConstraint_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Constraint',
+ 'localName should be Constraint');
+ var name = node.getAttribute('name');
+ if (!name) {
+ return undefined;
+ }
+ return ol.xml.pushParseAndPop({'name': name},
+ ol.format.OWS.CONSTRAINT_PARSERS_, node,
+ objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The contact info.
+ */
+ol.format.OWS.readContactInfo_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ContactInfo',
+ 'localName should be ContactInfo');
+ return ol.xml.pushParseAndPop({},
+ ol.format.OWS.CONTACT_INFO_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The DCP.
+ */
+ol.format.OWS.readDcp_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'DCP', 'localName should be DCP');
+ return ol.xml.pushParseAndPop({},
+ ol.format.OWS.DCP_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The GET object.
+ */
+ol.format.OWS.readGet_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Get', 'localName should be Get');
+ var href = ol.format.XLink.readHref(node);
+ if (!href) {
+ return undefined;
+ }
+ return ol.xml.pushParseAndPop({'href': href},
+ ol.format.OWS.REQUEST_METHOD_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The HTTP object.
+ */
+ol.format.OWS.readHttp_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'HTTP', 'localName should be HTTP');
+ return ol.xml.pushParseAndPop({}, ol.format.OWS.HTTP_PARSERS_,
+ node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The operation.
+ */
+ol.format.OWS.readOperation_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Operation',
+ 'localName should be Operation');
+ var name = node.getAttribute('name');
+ var value = ol.xml.pushParseAndPop({},
+ ol.format.OWS.OPERATION_PARSERS_, node, objectStack);
+ if (!value) {
+ return undefined;
+ }
+ var object = /** @type {Object} */
+ (objectStack[objectStack.length - 1]);
+ object[name] = value;
+
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The operations metadata.
+ */
+ol.format.OWS.readOperationsMetadata_ = function(node,
+ objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'OperationsMetadata',
+ 'localName should be OperationsMetadata');
+ return ol.xml.pushParseAndPop({},
+ ol.format.OWS.OPERATIONS_METADATA_PARSERS_, node,
+ objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The phone.
+ */
+ol.format.OWS.readPhone_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Phone', 'localName should be Phone');
+ return ol.xml.pushParseAndPop({},
+ ol.format.OWS.PHONE_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The service identification.
+ */
+ol.format.OWS.readServiceIdentification_ = function(node,
+ objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ServiceIdentification',
+ 'localName should be ServiceIdentification');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_, node,
+ objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The service contact.
+ */
+ol.format.OWS.readServiceContact_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ServiceContact',
+ 'localName should be ServiceContact');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.OWS.SERVICE_CONTACT_PARSERS_, node,
+ objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} The service provider.
+ */
+ol.format.OWS.readServiceProvider_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ServiceProvider',
+ 'localName should be ServiceProvider');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.OWS.SERVICE_PROVIDER_PARSERS_, node,
+ objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {string|undefined} The value.
+ */
+ol.format.OWS.readValue_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Value', 'localName should be Value');
+ return ol.format.XSD.readString(node);
+};
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ * @private
+ */
+ol.format.OWS.NAMESPACE_URIS_ = [
+ null,
+ 'http://www.opengis.net/ows/1.1'
+];
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'ServiceIdentification': ol.xml.makeObjectPropertySetter(
+ ol.format.OWS.readServiceIdentification_),
+ 'ServiceProvider': ol.xml.makeObjectPropertySetter(
+ ol.format.OWS.readServiceProvider_),
+ 'OperationsMetadata': ol.xml.makeObjectPropertySetter(
+ ol.format.OWS.readOperationsMetadata_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.ADDRESS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'DeliveryPoint': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'AdministrativeArea': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'PostalCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'ElectronicMailAddress': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.ALLOWED_VALUES_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Value': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readValue_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.CONSTRAINT_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'AllowedValues': ol.xml.makeObjectPropertySetter(
+ ol.format.OWS.readAllowedValues_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.CONTACT_INFO_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Phone': ol.xml.makeObjectPropertySetter(ol.format.OWS.readPhone_),
+ 'Address': ol.xml.makeObjectPropertySetter(ol.format.OWS.readAddress_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.DCP_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'HTTP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readHttp_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.HTTP_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Get': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readGet_),
+ 'Post': undefined // TODO
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.OPERATION_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'DCP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readDcp_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.OPERATIONS_METADATA_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Operation': ol.format.OWS.readOperation_
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.PHONE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Voice': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Facsimile': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.REQUEST_METHOD_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Constraint': ol.xml.makeObjectPropertyPusher(
+ ol.format.OWS.readConstraint_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.SERVICE_CONTACT_PARSERS_ =
+ ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'IndividualName': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'PositionName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'ContactInfo': ol.xml.makeObjectPropertySetter(
+ ol.format.OWS.readContactInfo_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_ =
+ ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'ServiceTypeVersion': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'ServiceType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.OWS.SERVICE_PROVIDER_PARSERS_ =
+ ol.xml.makeStructureNS(
+ ol.format.OWS.NAMESPACE_URIS_, {
+ 'ProviderName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'ProviderSite': ol.xml.makeObjectPropertySetter(ol.format.XLink.readHref),
+ 'ServiceContact': ol.xml.makeObjectPropertySetter(
+ ol.format.OWS.readServiceContact_)
+ });
+
+goog.provide('ol.geom.flat.flip');
+
+
+/**
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} stride Stride.
+ * @param {Array.<number>=} opt_dest Destination.
+ * @param {number=} opt_destOffset Destination offset.
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.geom.flat.flip.flipXY = function(flatCoordinates, offset, end, stride, opt_dest, opt_destOffset) {
+ var dest, destOffset;
+ if (opt_dest !== undefined) {
+ dest = opt_dest;
+ destOffset = opt_destOffset !== undefined ? opt_destOffset : 0;
+ } else {
+ dest = [];
+ destOffset = 0;
+ }
+ var j = offset;
+ while (j < end) {
+ var x = flatCoordinates[j++];
+ dest[destOffset++] = flatCoordinates[j++];
+ dest[destOffset++] = x;
+ for (var k = 2; k < stride; ++k) {
+ dest[destOffset++] = flatCoordinates[j++];
+ }
+ }
+ dest.length = destOffset;
+ return dest;
+};
+
+goog.provide('ol.format.Polyline');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.TextFeature');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.flip');
+goog.require('ol.geom.flat.inflate');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the Encoded
+ * Polyline Algorithm Format.
+ *
+ * @constructor
+ * @extends {ol.format.TextFeature}
+ * @param {olx.format.PolylineOptions=} opt_options
+ * Optional configuration object.
+ * @api stable
+ */
+ol.format.Polyline = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.TextFeature.call(this);
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get('EPSG:4326');
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.factor_ = options.factor ? options.factor : 1e5;
+
+ /**
+ * @private
+ * @type {ol.geom.GeometryLayout}
+ */
+ this.geometryLayout_ = options.geometryLayout ?
+ options.geometryLayout : ol.geom.GeometryLayout.XY;
+};
+ol.inherits(ol.format.Polyline, ol.format.TextFeature);
+
+
+/**
+ * Encode a list of n-dimensional points and return an encoded string
+ *
+ * Attention: This function will modify the passed array!
+ *
+ * @param {Array.<number>} numbers A list of n-dimensional points.
+ * @param {number} stride The number of dimension of the points in the list.
+ * @param {number=} opt_factor The factor by which the numbers will be
+ * multiplied. The remaining decimal places will get rounded away.
+ * Default is `1e5`.
+ * @return {string} The encoded string.
+ * @api
+ */
+ol.format.Polyline.encodeDeltas = function(numbers, stride, opt_factor) {
+ var factor = opt_factor ? opt_factor : 1e5;
+ var d;
+
+ var lastNumbers = new Array(stride);
+ for (d = 0; d < stride; ++d) {
+ lastNumbers[d] = 0;
+ }
+
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii;) {
+ for (d = 0; d < stride; ++d, ++i) {
+ var num = numbers[i];
+ var delta = num - lastNumbers[d];
+ lastNumbers[d] = num;
+
+ numbers[i] = delta;
+ }
+ }
+
+ return ol.format.Polyline.encodeFloats(numbers, factor);
+};
+
+
+/**
+ * Decode a list of n-dimensional points from an encoded string
+ *
+ * @param {string} encoded An encoded string.
+ * @param {number} stride The number of dimension of the points in the
+ * encoded string.
+ * @param {number=} opt_factor The factor by which the resulting numbers will
+ * be divided. Default is `1e5`.
+ * @return {Array.<number>} A list of n-dimensional points.
+ * @api
+ */
+ol.format.Polyline.decodeDeltas = function(encoded, stride, opt_factor) {
+ var factor = opt_factor ? opt_factor : 1e5;
+ var d;
+
+ /** @type {Array.<number>} */
+ var lastNumbers = new Array(stride);
+ for (d = 0; d < stride; ++d) {
+ lastNumbers[d] = 0;
+ }
+
+ var numbers = ol.format.Polyline.decodeFloats(encoded, factor);
+
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii;) {
+ for (d = 0; d < stride; ++d, ++i) {
+ lastNumbers[d] += numbers[i];
+
+ numbers[i] = lastNumbers[d];
+ }
+ }
+
+ return numbers;
+};
+
+
+/**
+ * Encode a list of floating point numbers and return an encoded string
+ *
+ * Attention: This function will modify the passed array!
+ *
+ * @param {Array.<number>} numbers A list of floating point numbers.
+ * @param {number=} opt_factor The factor by which the numbers will be
+ * multiplied. The remaining decimal places will get rounded away.
+ * Default is `1e5`.
+ * @return {string} The encoded string.
+ * @api
+ */
+ol.format.Polyline.encodeFloats = function(numbers, opt_factor) {
+ var factor = opt_factor ? opt_factor : 1e5;
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii; ++i) {
+ numbers[i] = Math.round(numbers[i] * factor);
+ }
+
+ return ol.format.Polyline.encodeSignedIntegers(numbers);
+};
+
+
+/**
+ * Decode a list of floating point numbers from an encoded string
+ *
+ * @param {string} encoded An encoded string.
+ * @param {number=} opt_factor The factor by which the result will be divided.
+ * Default is `1e5`.
+ * @return {Array.<number>} A list of floating point numbers.
+ * @api
+ */
+ol.format.Polyline.decodeFloats = function(encoded, opt_factor) {
+ var factor = opt_factor ? opt_factor : 1e5;
+ var numbers = ol.format.Polyline.decodeSignedIntegers(encoded);
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii; ++i) {
+ numbers[i] /= factor;
+ }
+ return numbers;
+};
+
+
+/**
+ * Encode a list of signed integers and return an encoded string
+ *
+ * Attention: This function will modify the passed array!
+ *
+ * @param {Array.<number>} numbers A list of signed integers.
+ * @return {string} The encoded string.
+ */
+ol.format.Polyline.encodeSignedIntegers = function(numbers) {
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii; ++i) {
+ var num = numbers[i];
+ numbers[i] = (num < 0) ? ~(num << 1) : (num << 1);
+ }
+ return ol.format.Polyline.encodeUnsignedIntegers(numbers);
+};
+
+
+/**
+ * Decode a list of signed integers from an encoded string
+ *
+ * @param {string} encoded An encoded string.
+ * @return {Array.<number>} A list of signed integers.
+ */
+ol.format.Polyline.decodeSignedIntegers = function(encoded) {
+ var numbers = ol.format.Polyline.decodeUnsignedIntegers(encoded);
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii; ++i) {
+ var num = numbers[i];
+ numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);
+ }
+ return numbers;
+};
+
+
+/**
+ * Encode a list of unsigned integers and return an encoded string
+ *
+ * @param {Array.<number>} numbers A list of unsigned integers.
+ * @return {string} The encoded string.
+ */
+ol.format.Polyline.encodeUnsignedIntegers = function(numbers) {
+ var encoded = '';
+ var i, ii;
+ for (i = 0, ii = numbers.length; i < ii; ++i) {
+ encoded += ol.format.Polyline.encodeUnsignedInteger(numbers[i]);
+ }
+ return encoded;
+};
+
+
+/**
+ * Decode a list of unsigned integers from an encoded string
+ *
+ * @param {string} encoded An encoded string.
+ * @return {Array.<number>} A list of unsigned integers.
+ */
+ol.format.Polyline.decodeUnsignedIntegers = function(encoded) {
+ var numbers = [];
+ var current = 0;
+ var shift = 0;
+ var i, ii;
+ for (i = 0, ii = encoded.length; i < ii; ++i) {
+ var b = encoded.charCodeAt(i) - 63;
+ current |= (b & 0x1f) << shift;
+ if (b < 0x20) {
+ numbers.push(current);
+ current = 0;
+ shift = 0;
+ } else {
+ shift += 5;
+ }
+ }
+ return numbers;
+};
+
+
+/**
+ * Encode one single unsigned integer and return an encoded string
+ *
+ * @param {number} num Unsigned integer that should be encoded.
+ * @return {string} The encoded string.
+ */
+ol.format.Polyline.encodeUnsignedInteger = function(num) {
+ var value, encoded = '';
+ while (num >= 0x20) {
+ value = (0x20 | (num & 0x1f)) + 63;
+ encoded += String.fromCharCode(value);
+ num >>= 5;
+ }
+ value = num + 63;
+ encoded += String.fromCharCode(value);
+ return encoded;
+};
+
+
+/**
+ * Read the feature from the Polyline source. The coordinates are assumed to be
+ * in two dimensions and in latitude, longitude order.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api stable
+ */
+ol.format.Polyline.prototype.readFeature;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.Polyline.prototype.readFeatureFromText = function(text, opt_options) {
+ var geometry = this.readGeometryFromText(text, opt_options);
+ return new ol.Feature(geometry);
+};
+
+
+/**
+ * Read the feature from the source. As Polyline sources contain a single
+ * feature, this will return the feature in an array.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.Polyline.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.Polyline.prototype.readFeaturesFromText = function(text, opt_options) {
+ var feature = this.readFeatureFromText(text, opt_options);
+ return [feature];
+};
+
+
+/**
+ * Read the geometry from the source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.geom.Geometry} Geometry.
+ * @api stable
+ */
+ol.format.Polyline.prototype.readGeometry;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.Polyline.prototype.readGeometryFromText = function(text, opt_options) {
+ var stride = ol.geom.SimpleGeometry.getStrideForLayout(this.geometryLayout_);
+ var flatCoordinates = ol.format.Polyline.decodeDeltas(
+ text, stride, this.factor_);
+ ol.geom.flat.flip.flipXY(
+ flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
+ var coordinates = ol.geom.flat.inflate.coordinates(
+ flatCoordinates, 0, flatCoordinates.length, stride);
+
+ return /** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(
+ new ol.geom.LineString(coordinates, this.geometryLayout_), false,
+ this.adaptOptions(opt_options)));
+};
+
+
+/**
+ * Read the projection from a Polyline source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.Polyline.prototype.readProjection;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.Polyline.prototype.writeFeatureText = function(feature, opt_options) {
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ return this.writeGeometryText(geometry, opt_options);
+ } else {
+ ol.asserts.assert(false, 40); // Expected `feature` to have a geometry
+ return '';
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.Polyline.prototype.writeFeaturesText = function(features, opt_options) {
+ ol.DEBUG && console.assert(features.length == 1,
+ 'features array should have 1 item');
+ return this.writeFeatureText(features[0], opt_options);
+};
+
+
+/**
+ * Write a single geometry in Polyline format.
+ *
+ * @function
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} Geometry.
+ * @api stable
+ */
+ol.format.Polyline.prototype.writeGeometry;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.Polyline.prototype.writeGeometryText = function(geometry, opt_options) {
+ geometry = /** @type {ol.geom.LineString} */
+ (ol.format.Feature.transformWithOptions(
+ geometry, true, this.adaptOptions(opt_options)));
+ var flatCoordinates = geometry.getFlatCoordinates();
+ var stride = geometry.getStride();
+ ol.geom.flat.flip.flipXY(
+ flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
+ return ol.format.Polyline.encodeDeltas(flatCoordinates, stride, this.factor_);
+};
+
+goog.provide('ol.format.TopoJSON');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.JSONFeature');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.obj');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Feature format for reading data in the TopoJSON format.
+ *
+ * @constructor
+ * @extends {ol.format.JSONFeature}
+ * @param {olx.format.TopoJSONOptions=} opt_options Options.
+ * @api stable
+ */
+ol.format.TopoJSON = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.JSONFeature.call(this);
+
+ /**
+ * @inheritDoc
+ */
+ this.defaultDataProjection = ol.proj.get(
+ options.defaultDataProjection ?
+ options.defaultDataProjection : 'EPSG:4326');
+
+};
+ol.inherits(ol.format.TopoJSON, ol.format.JSONFeature);
+
+
+/**
+ * @const {Array.<string>}
+ * @private
+ */
+ol.format.TopoJSON.EXTENSIONS_ = ['.topojson'];
+
+
+/**
+ * Concatenate arcs into a coordinate array.
+ * @param {Array.<number>} indices Indices of arcs to concatenate. Negative
+ * values indicate arcs need to be reversed.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs (already
+ * transformed).
+ * @return {Array.<ol.Coordinate>} Coordinates array.
+ * @private
+ */
+ol.format.TopoJSON.concatenateArcs_ = function(indices, arcs) {
+ /** @type {Array.<ol.Coordinate>} */
+ var coordinates = [];
+ var index, arc;
+ var i, ii;
+ var j, jj;
+ for (i = 0, ii = indices.length; i < ii; ++i) {
+ index = indices[i];
+ if (i > 0) {
+ // splicing together arcs, discard last point
+ coordinates.pop();
+ }
+ if (index >= 0) {
+ // forward arc
+ arc = arcs[index];
+ } else {
+ // reverse arc
+ arc = arcs[~index].slice().reverse();
+ }
+ coordinates.push.apply(coordinates, arc);
+ }
+ // provide fresh copies of coordinate arrays
+ for (j = 0, jj = coordinates.length; j < jj; ++j) {
+ coordinates[j] = coordinates[j].slice();
+ }
+ return coordinates;
+};
+
+
+/**
+ * Create a point from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON object.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @return {ol.geom.Point} Geometry.
+ * @private
+ */
+ol.format.TopoJSON.readPointGeometry_ = function(object, scale, translate) {
+ var coordinates = object.coordinates;
+ if (scale && translate) {
+ ol.format.TopoJSON.transformVertex_(coordinates, scale, translate);
+ }
+ return new ol.geom.Point(coordinates);
+};
+
+
+/**
+ * Create a multi-point from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON object.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @return {ol.geom.MultiPoint} Geometry.
+ * @private
+ */
+ol.format.TopoJSON.readMultiPointGeometry_ = function(object, scale,
+ translate) {
+ var coordinates = object.coordinates;
+ var i, ii;
+ if (scale && translate) {
+ for (i = 0, ii = coordinates.length; i < ii; ++i) {
+ ol.format.TopoJSON.transformVertex_(coordinates[i], scale, translate);
+ }
+ }
+ return new ol.geom.MultiPoint(coordinates);
+};
+
+
+/**
+ * Create a linestring from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON object.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @return {ol.geom.LineString} Geometry.
+ * @private
+ */
+ol.format.TopoJSON.readLineStringGeometry_ = function(object, arcs) {
+ var coordinates = ol.format.TopoJSON.concatenateArcs_(object.arcs, arcs);
+ return new ol.geom.LineString(coordinates);
+};
+
+
+/**
+ * Create a multi-linestring from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON object.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @return {ol.geom.MultiLineString} Geometry.
+ * @private
+ */
+ol.format.TopoJSON.readMultiLineStringGeometry_ = function(object, arcs) {
+ var coordinates = [];
+ var i, ii;
+ for (i = 0, ii = object.arcs.length; i < ii; ++i) {
+ coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs);
+ }
+ return new ol.geom.MultiLineString(coordinates);
+};
+
+
+/**
+ * Create a polygon from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON object.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @return {ol.geom.Polygon} Geometry.
+ * @private
+ */
+ol.format.TopoJSON.readPolygonGeometry_ = function(object, arcs) {
+ var coordinates = [];
+ var i, ii;
+ for (i = 0, ii = object.arcs.length; i < ii; ++i) {
+ coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs);
+ }
+ return new ol.geom.Polygon(coordinates);
+};
+
+
+/**
+ * Create a multi-polygon from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON object.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @return {ol.geom.MultiPolygon} Geometry.
+ * @private
+ */
+ol.format.TopoJSON.readMultiPolygonGeometry_ = function(object, arcs) {
+ var coordinates = [];
+ var polyArray, ringCoords, j, jj;
+ var i, ii;
+ for (i = 0, ii = object.arcs.length; i < ii; ++i) {
+ // for each polygon
+ polyArray = object.arcs[i];
+ ringCoords = [];
+ for (j = 0, jj = polyArray.length; j < jj; ++j) {
+ // for each ring
+ ringCoords[j] = ol.format.TopoJSON.concatenateArcs_(polyArray[j], arcs);
+ }
+ coordinates[i] = ringCoords;
+ }
+ return new ol.geom.MultiPolygon(coordinates);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TopoJSON.prototype.getExtensions = function() {
+ return ol.format.TopoJSON.EXTENSIONS_;
+};
+
+
+/**
+ * Create features from a TopoJSON GeometryCollection object.
+ *
+ * @param {TopoJSONGeometryCollection} collection TopoJSON Geometry
+ * object.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Array of features.
+ * @private
+ */
+ol.format.TopoJSON.readFeaturesFromGeometryCollection_ = function(
+ collection, arcs, scale, translate, opt_options) {
+ var geometries = collection.geometries;
+ var features = [];
+ var i, ii;
+ for (i = 0, ii = geometries.length; i < ii; ++i) {
+ features[i] = ol.format.TopoJSON.readFeatureFromGeometry_(
+ geometries[i], arcs, scale, translate, opt_options);
+ }
+ return features;
+};
+
+
+/**
+ * Create a feature from a TopoJSON geometry object.
+ *
+ * @param {TopoJSONGeometry} object TopoJSON geometry object.
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @private
+ */
+ol.format.TopoJSON.readFeatureFromGeometry_ = function(object, arcs,
+ scale, translate, opt_options) {
+ var geometry;
+ var type = object.type;
+ var geometryReader = ol.format.TopoJSON.GEOMETRY_READERS_[type];
+ if ((type === 'Point') || (type === 'MultiPoint')) {
+ geometry = geometryReader(object, scale, translate);
+ } else {
+ geometry = geometryReader(object, arcs);
+ }
+ var feature = new ol.Feature();
+ feature.setGeometry(/** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(geometry, false, opt_options)));
+ if (object.id !== undefined) {
+ feature.setId(object.id);
+ }
+ if (object.properties) {
+ feature.setProperties(object.properties);
+ }
+ return feature;
+};
+
+
+/**
+ * Read all features from a TopoJSON source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.TopoJSON.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.TopoJSON.prototype.readFeaturesFromObject = function(
+ object, opt_options) {
+ if (object.type == 'Topology') {
+ var topoJSONTopology = /** @type {TopoJSONTopology} */ (object);
+ var transform, scale = null, translate = null;
+ if (topoJSONTopology.transform) {
+ transform = topoJSONTopology.transform;
+ scale = transform.scale;
+ translate = transform.translate;
+ }
+ var arcs = topoJSONTopology.arcs;
+ if (transform) {
+ ol.format.TopoJSON.transformArcs_(arcs, scale, translate);
+ }
+ /** @type {Array.<ol.Feature>} */
+ var features = [];
+ var topoJSONFeatures = ol.obj.getValues(topoJSONTopology.objects);
+ var i, ii;
+ var feature;
+ for (i = 0, ii = topoJSONFeatures.length; i < ii; ++i) {
+ if (topoJSONFeatures[i].type === 'GeometryCollection') {
+ feature = /** @type {TopoJSONGeometryCollection} */
+ (topoJSONFeatures[i]);
+ features.push.apply(features,
+ ol.format.TopoJSON.readFeaturesFromGeometryCollection_(
+ feature, arcs, scale, translate, opt_options));
+ } else {
+ feature = /** @type {TopoJSONGeometry} */
+ (topoJSONFeatures[i]);
+ features.push(ol.format.TopoJSON.readFeatureFromGeometry_(
+ feature, arcs, scale, translate, opt_options));
+ }
+ }
+ return features;
+ } else {
+ return [];
+ }
+};
+
+
+/**
+ * Apply a linear transform to array of arcs. The provided array of arcs is
+ * modified in place.
+ *
+ * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @private
+ */
+ol.format.TopoJSON.transformArcs_ = function(arcs, scale, translate) {
+ var i, ii;
+ for (i = 0, ii = arcs.length; i < ii; ++i) {
+ ol.format.TopoJSON.transformArc_(arcs[i], scale, translate);
+ }
+};
+
+
+/**
+ * Apply a linear transform to an arc. The provided arc is modified in place.
+ *
+ * @param {Array.<ol.Coordinate>} arc Arc.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @private
+ */
+ol.format.TopoJSON.transformArc_ = function(arc, scale, translate) {
+ var x = 0;
+ var y = 0;
+ var vertex;
+ var i, ii;
+ for (i = 0, ii = arc.length; i < ii; ++i) {
+ vertex = arc[i];
+ x += vertex[0];
+ y += vertex[1];
+ vertex[0] = x;
+ vertex[1] = y;
+ ol.format.TopoJSON.transformVertex_(vertex, scale, translate);
+ }
+};
+
+
+/**
+ * Apply a linear transform to a vertex. The provided vertex is modified in
+ * place.
+ *
+ * @param {ol.Coordinate} vertex Vertex.
+ * @param {Array.<number>} scale Scale for each dimension.
+ * @param {Array.<number>} translate Translation for each dimension.
+ * @private
+ */
+ol.format.TopoJSON.transformVertex_ = function(vertex, scale, translate) {
+ vertex[0] = vertex[0] * scale[0] + translate[0];
+ vertex[1] = vertex[1] * scale[1] + translate[1];
+};
+
+
+/**
+ * Read the projection from a TopoJSON source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} object Source.
+ * @return {ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.TopoJSON.prototype.readProjection = function(object) {
+ return this.defaultDataProjection;
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Object.<string, function(TopoJSONGeometry, Array, ...Array): ol.geom.Geometry>}
+ */
+ol.format.TopoJSON.GEOMETRY_READERS_ = {
+ 'Point': ol.format.TopoJSON.readPointGeometry_,
+ 'LineString': ol.format.TopoJSON.readLineStringGeometry_,
+ 'Polygon': ol.format.TopoJSON.readPolygonGeometry_,
+ 'MultiPoint': ol.format.TopoJSON.readMultiPointGeometry_,
+ 'MultiLineString': ol.format.TopoJSON.readMultiLineStringGeometry_,
+ 'MultiPolygon': ol.format.TopoJSON.readMultiPolygonGeometry_
+};
+
+goog.provide('ol.format.WFS');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.format.GML3');
+goog.require('ol.format.GMLBase');
+goog.require('ol.format.filter');
+goog.require('ol.format.XMLFeature');
+goog.require('ol.format.XSD');
+goog.require('ol.geom.Geometry');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Feature format for reading and writing data in the WFS format.
+ * By default, supports WFS version 1.1.0. You can pass a GML format
+ * as option if you want to read a WFS that contains GML2 (WFS 1.0.0).
+ * Also see {@link ol.format.GMLBase} which is used by this format.
+ *
+ * @constructor
+ * @param {olx.format.WFSOptions=} opt_options
+ * Optional configuration object.
+ * @extends {ol.format.XMLFeature}
+ * @api stable
+ */
+ol.format.WFS = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {Array.<string>|string|undefined}
+ */
+ this.featureType_ = options.featureType;
+
+ /**
+ * @private
+ * @type {Object.<string, string>|string|undefined}
+ */
+ this.featureNS_ = options.featureNS;
+
+ /**
+ * @private
+ * @type {ol.format.GMLBase}
+ */
+ this.gmlFormat_ = options.gmlFormat ?
+ options.gmlFormat : new ol.format.GML3();
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.schemaLocation_ = options.schemaLocation ?
+ options.schemaLocation : ol.format.WFS.SCHEMA_LOCATION;
+
+ ol.format.XMLFeature.call(this);
+};
+ol.inherits(ol.format.WFS, ol.format.XMLFeature);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WFS.FEATURE_PREFIX = 'feature';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WFS.XMLNS = 'http://www.w3.org/2000/xmlns/';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WFS.OGCNS = 'http://www.opengis.net/ogc';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WFS.WFSNS = 'http://www.opengis.net/wfs';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WFS.SCHEMA_LOCATION = 'http://www.opengis.net/wfs ' +
+ 'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd';
+
+
+/**
+ * Read all features from a WFS FeatureCollection.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.WFS.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WFS.prototype.readFeaturesFromNode = function(node, opt_options) {
+ var context = /** @type {ol.XmlNodeStackItem} */ ({
+ 'featureType': this.featureType_,
+ 'featureNS': this.featureNS_
+ });
+ ol.obj.assign(context, this.getReadOptions(node,
+ opt_options ? opt_options : {}));
+ var objectStack = [context];
+ this.gmlFormat_.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][
+ 'featureMember'] =
+ ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal);
+ var features = ol.xml.pushParseAndPop([],
+ this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
+ objectStack, this.gmlFormat_);
+ if (!features) {
+ features = [];
+ }
+ return features;
+};
+
+
+/**
+ * Read transaction response of the source.
+ *
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.WFSTransactionResponse|undefined} Transaction response.
+ * @api stable
+ */
+ol.format.WFS.prototype.readTransactionResponse = function(source) {
+ if (ol.xml.isDocument(source)) {
+ return this.readTransactionResponseFromDocument(
+ /** @type {Document} */ (source));
+ } else if (ol.xml.isNode(source)) {
+ return this.readTransactionResponseFromNode(/** @type {Node} */ (source));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readTransactionResponseFromDocument(doc);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * Read feature collection metadata of the source.
+ *
+ * @param {Document|Node|Object|string} source Source.
+ * @return {ol.WFSFeatureCollectionMetadata|undefined}
+ * FeatureCollection metadata.
+ * @api stable
+ */
+ol.format.WFS.prototype.readFeatureCollectionMetadata = function(source) {
+ if (ol.xml.isDocument(source)) {
+ return this.readFeatureCollectionMetadataFromDocument(
+ /** @type {Document} */ (source));
+ } else if (ol.xml.isNode(source)) {
+ return this.readFeatureCollectionMetadataFromNode(
+ /** @type {Node} */ (source));
+ } else if (typeof source === 'string') {
+ var doc = ol.xml.parse(source);
+ return this.readFeatureCollectionMetadataFromDocument(doc);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {ol.WFSFeatureCollectionMetadata|undefined}
+ * FeatureCollection metadata.
+ */
+ol.format.WFS.prototype.readFeatureCollectionMetadataFromDocument = function(doc) {
+ ol.DEBUG && console.assert(doc.nodeType == Node.DOCUMENT_NODE,
+ 'doc.nodeType should be DOCUMENT');
+ for (var n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ return this.readFeatureCollectionMetadataFromNode(n);
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WFS.FEATURE_COLLECTION_PARSERS_ = {
+ 'http://www.opengis.net/gml': {
+ 'boundedBy': ol.xml.makeObjectPropertySetter(
+ ol.format.GMLBase.prototype.readGeometryElement, 'bounds')
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {ol.WFSFeatureCollectionMetadata|undefined}
+ * FeatureCollection metadata.
+ */
+ol.format.WFS.prototype.readFeatureCollectionMetadataFromNode = function(node) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'FeatureCollection',
+ 'localName should be FeatureCollection');
+ var result = {};
+ var value = ol.format.XSD.readNonNegativeIntegerString(
+ node.getAttribute('numberOfFeatures'));
+ result['numberOfFeatures'] = value;
+ return ol.xml.pushParseAndPop(
+ /** @type {ol.WFSFeatureCollectionMetadata} */ (result),
+ ol.format.WFS.FEATURE_COLLECTION_PARSERS_, node, [], this.gmlFormat_);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_ = {
+ 'http://www.opengis.net/wfs': {
+ 'totalInserted': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'totalUpdated': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'totalDeleted': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger)
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Transaction Summary.
+ * @private
+ */
+ol.format.WFS.readTransactionSummary_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WFS.OGC_FID_PARSERS_ = {
+ 'http://www.opengis.net/ogc': {
+ 'FeatureId': ol.xml.makeArrayPusher(function(node, objectStack) {
+ return node.getAttribute('fid');
+ })
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.WFS.fidParser_ = function(node, objectStack) {
+ ol.xml.parseNode(ol.format.WFS.OGC_FID_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WFS.INSERT_RESULTS_PARSERS_ = {
+ 'http://www.opengis.net/wfs': {
+ 'Feature': ol.format.WFS.fidParser_
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Array.<string>|undefined} Insert results.
+ * @private
+ */
+ol.format.WFS.readInsertResults_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop(
+ [], ol.format.WFS.INSERT_RESULTS_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_ = {
+ 'http://www.opengis.net/wfs': {
+ 'TransactionSummary': ol.xml.makeObjectPropertySetter(
+ ol.format.WFS.readTransactionSummary_, 'transactionSummary'),
+ 'InsertResults': ol.xml.makeObjectPropertySetter(
+ ol.format.WFS.readInsertResults_, 'insertIds')
+ }
+};
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {ol.WFSTransactionResponse|undefined} Transaction response.
+ */
+ol.format.WFS.prototype.readTransactionResponseFromDocument = function(doc) {
+ ol.DEBUG && console.assert(doc.nodeType == Node.DOCUMENT_NODE,
+ 'doc.nodeType should be DOCUMENT');
+ for (var n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ return this.readTransactionResponseFromNode(n);
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {ol.WFSTransactionResponse|undefined} Transaction response.
+ */
+ol.format.WFS.prototype.readTransactionResponseFromNode = function(node) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'TransactionResponse',
+ 'localName should be TransactionResponse');
+ return ol.xml.pushParseAndPop(
+ /** @type {ol.WFSTransactionResponse} */({}),
+ ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_, node, []);
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.WFS.QUERY_SERIALIZERS_ = {
+ 'http://www.opengis.net/wfs': {
+ 'PropertyName': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeFeature_ = function(node, feature, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ var featureType = context['featureType'];
+ var featureNS = context['featureNS'];
+ var child = ol.xml.createElementNS(featureNS, featureType);
+ node.appendChild(child);
+ ol.format.GML3.prototype.writeFeatureElement(child, feature, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {number|string} fid Feature identifier.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeOgcFidFilter_ = function(node, fid, objectStack) {
+ var filter = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'Filter');
+ var child = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'FeatureId');
+ filter.appendChild(child);
+ child.setAttribute('fid', fid);
+ node.appendChild(filter);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeDelete_ = function(node, feature, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ ol.asserts.assert(feature.getId() !== undefined, 26); // Features must have an id set
+ var featureType = context['featureType'];
+ var featurePrefix = context['featurePrefix'];
+ featurePrefix = featurePrefix ? featurePrefix :
+ ol.format.WFS.FEATURE_PREFIX;
+ var featureNS = context['featureNS'];
+ node.setAttribute('typeName', featurePrefix + ':' + featureType);
+ ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix,
+ featureNS);
+ var fid = feature.getId();
+ if (fid !== undefined) {
+ ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.Feature} feature Feature.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeUpdate_ = function(node, feature, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ ol.asserts.assert(feature.getId() !== undefined, 27); // Features must have an id set
+ var featureType = context['featureType'];
+ var featurePrefix = context['featurePrefix'];
+ featurePrefix = featurePrefix ? featurePrefix :
+ ol.format.WFS.FEATURE_PREFIX;
+ var featureNS = context['featureNS'];
+ node.setAttribute('typeName', featurePrefix + ':' + featureType);
+ ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix,
+ featureNS);
+ var fid = feature.getId();
+ if (fid !== undefined) {
+ var keys = feature.getKeys();
+ var values = [];
+ for (var i = 0, ii = keys.length; i < ii; i++) {
+ var value = feature.get(keys[i]);
+ if (value !== undefined) {
+ values.push({name: keys[i], value: value});
+ }
+ }
+ ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ (
+ {node: node, 'srsName': context['srsName']}),
+ ol.format.WFS.TRANSACTION_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('Property'), values,
+ objectStack);
+ ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Object} pair Property name and value.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeProperty_ = function(node, pair, objectStack) {
+ var name = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Name');
+ node.appendChild(name);
+ ol.format.XSD.writeStringTextNode(name, pair.name);
+ if (pair.value !== undefined && pair.value !== null) {
+ var value = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Value');
+ node.appendChild(value);
+ if (pair.value instanceof ol.geom.Geometry) {
+ ol.format.GML3.prototype.writeGeometryElement(value,
+ pair.value, objectStack);
+ } else {
+ ol.format.XSD.writeStringTextNode(value, pair.value);
+ }
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {{vendorId: string, safeToIgnore: boolean, value: string}}
+ * nativeElement The native element.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeNative_ = function(node, nativeElement, objectStack) {
+ if (nativeElement.vendorId) {
+ node.setAttribute('vendorId', nativeElement.vendorId);
+ }
+ if (nativeElement.safeToIgnore !== undefined) {
+ node.setAttribute('safeToIgnore', nativeElement.safeToIgnore);
+ }
+ if (nativeElement.value !== undefined) {
+ ol.format.XSD.writeStringTextNode(node, nativeElement.value);
+ }
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.WFS.TRANSACTION_SERIALIZERS_ = {
+ 'http://www.opengis.net/wfs': {
+ 'Insert': ol.xml.makeChildAppender(ol.format.WFS.writeFeature_),
+ 'Update': ol.xml.makeChildAppender(ol.format.WFS.writeUpdate_),
+ 'Delete': ol.xml.makeChildAppender(ol.format.WFS.writeDelete_),
+ 'Property': ol.xml.makeChildAppender(ol.format.WFS.writeProperty_),
+ 'Native': ol.xml.makeChildAppender(ol.format.WFS.writeNative_)
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {string} featureType Feature type.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeQuery_ = function(node, featureType, objectStack) {
+ var context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var featurePrefix = context['featurePrefix'];
+ var featureNS = context['featureNS'];
+ var propertyNames = context['propertyNames'];
+ var srsName = context['srsName'];
+ var prefix = featurePrefix ? featurePrefix + ':' : '';
+ node.setAttribute('typeName', prefix + featureType);
+ if (srsName) {
+ node.setAttribute('srsName', srsName);
+ }
+ if (featureNS) {
+ ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix,
+ featureNS);
+ }
+ var item = /** @type {ol.XmlNodeStackItem} */ (ol.obj.assign({}, context));
+ item.node = node;
+ ol.xml.pushSerializeAndPop(item,
+ ol.format.WFS.QUERY_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('PropertyName'), propertyNames,
+ objectStack);
+ var filter = context['filter'];
+ if (filter) {
+ var child = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'Filter');
+ node.appendChild(child);
+ ol.format.WFS.writeFilterCondition_(child, filter, objectStack);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.Filter} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeFilterCondition_ = function(node, filter, objectStack) {
+ /** @type {ol.XmlNodeStackItem} */
+ var item = {node: node};
+ ol.xml.pushSerializeAndPop(item,
+ ol.format.WFS.GETFEATURE_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory(filter.getTagName()),
+ [filter], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.Bbox} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeBboxFilter_ = function(node, filter, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ context['srsName'] = filter.srsName;
+
+ ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName);
+ ol.format.GML3.prototype.writeGeometryElement(node, filter.extent, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.Intersects} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeIntersectsFilter_ = function(node, filter, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ context['srsName'] = filter.srsName;
+
+ ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName);
+ ol.format.GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.Within} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeWithinFilter_ = function(node, filter, objectStack) {
+ var context = objectStack[objectStack.length - 1];
+ context['srsName'] = filter.srsName;
+
+ ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName);
+ ol.format.GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.LogicalBinary} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeLogicalFilter_ = function(node, filter, objectStack) {
+ /** @type {ol.XmlNodeStackItem} */
+ var item = {node: node};
+ var conditionA = filter.conditionA;
+ ol.xml.pushSerializeAndPop(item,
+ ol.format.WFS.GETFEATURE_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory(conditionA.getTagName()),
+ [conditionA], objectStack);
+ var conditionB = filter.conditionB;
+ ol.xml.pushSerializeAndPop(item,
+ ol.format.WFS.GETFEATURE_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory(conditionB.getTagName()),
+ [conditionB], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.Not} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeNotFilter_ = function(node, filter, objectStack) {
+ /** @type {ol.XmlNodeStackItem} */
+ var item = {node: node};
+ var condition = filter.condition;
+ ol.xml.pushSerializeAndPop(item,
+ ol.format.WFS.GETFEATURE_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory(condition.getTagName()),
+ [condition], objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.ComparisonBinary} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeComparisonFilter_ = function(node, filter, objectStack) {
+ if (filter.matchCase !== undefined) {
+ node.setAttribute('matchCase', filter.matchCase.toString());
+ }
+ ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName);
+ ol.format.WFS.writeOgcLiteral_(node, '' + filter.expression);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.IsNull} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeIsNullFilter_ = function(node, filter, objectStack) {
+ ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.IsBetween} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeIsBetweenFilter_ = function(node, filter, objectStack) {
+ ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName);
+
+ var lowerBoundary = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'LowerBoundary');
+ node.appendChild(lowerBoundary);
+ ol.format.WFS.writeOgcLiteral_(lowerBoundary, '' + filter.lowerBoundary);
+
+ var upperBoundary = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'UpperBoundary');
+ node.appendChild(upperBoundary);
+ ol.format.WFS.writeOgcLiteral_(upperBoundary, '' + filter.upperBoundary);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {ol.format.filter.IsLike} filter Filter.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeIsLikeFilter_ = function(node, filter, objectStack) {
+ node.setAttribute('wildCard', filter.wildCard);
+ node.setAttribute('singleChar', filter.singleChar);
+ node.setAttribute('escapeChar', filter.escapeChar);
+ if (filter.matchCase !== undefined) {
+ node.setAttribute('matchCase', filter.matchCase.toString());
+ }
+ ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName);
+ ol.format.WFS.writeOgcLiteral_(node, '' + filter.pattern);
+};
+
+
+/**
+ * @param {string} tagName Tag name.
+ * @param {Node} node Node.
+ * @param {string} value Value.
+ * @private
+ */
+ol.format.WFS.writeOgcExpression_ = function(tagName, node, value) {
+ var property = ol.xml.createElementNS(ol.format.WFS.OGCNS, tagName);
+ ol.format.XSD.writeStringTextNode(property, value);
+ node.appendChild(property);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {string} value PropertyName value.
+ * @private
+ */
+ol.format.WFS.writeOgcPropertyName_ = function(node, value) {
+ ol.format.WFS.writeOgcExpression_('PropertyName', node, value);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {string} value PropertyName value.
+ * @private
+ */
+ol.format.WFS.writeOgcLiteral_ = function(node, value) {
+ ol.format.WFS.writeOgcExpression_('Literal', node, value);
+};
+
+
+/**
+ * @type {Object.<string, Object.<string, ol.XmlSerializer>>}
+ * @private
+ */
+ol.format.WFS.GETFEATURE_SERIALIZERS_ = {
+ 'http://www.opengis.net/wfs': {
+ 'Query': ol.xml.makeChildAppender(ol.format.WFS.writeQuery_)
+ },
+ 'http://www.opengis.net/ogc': {
+ 'And': ol.xml.makeChildAppender(ol.format.WFS.writeLogicalFilter_),
+ 'Or': ol.xml.makeChildAppender(ol.format.WFS.writeLogicalFilter_),
+ 'Not': ol.xml.makeChildAppender(ol.format.WFS.writeNotFilter_),
+ 'BBOX': ol.xml.makeChildAppender(ol.format.WFS.writeBboxFilter_),
+ 'Intersects': ol.xml.makeChildAppender(ol.format.WFS.writeIntersectsFilter_),
+ 'Within': ol.xml.makeChildAppender(ol.format.WFS.writeWithinFilter_),
+ 'PropertyIsEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_),
+ 'PropertyIsNotEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_),
+ 'PropertyIsLessThan': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_),
+ 'PropertyIsLessThanOrEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_),
+ 'PropertyIsGreaterThan': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_),
+ 'PropertyIsGreaterThanOrEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_),
+ 'PropertyIsNull': ol.xml.makeChildAppender(ol.format.WFS.writeIsNullFilter_),
+ 'PropertyIsBetween': ol.xml.makeChildAppender(ol.format.WFS.writeIsBetweenFilter_),
+ 'PropertyIsLike': ol.xml.makeChildAppender(ol.format.WFS.writeIsLikeFilter_)
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<string>} featureTypes Feature types.
+ * @param {Array.<*>} objectStack Node stack.
+ * @private
+ */
+ol.format.WFS.writeGetFeature_ = function(node, featureTypes, objectStack) {
+ var context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var item = /** @type {ol.XmlNodeStackItem} */ (ol.obj.assign({}, context));
+ item.node = node;
+ ol.xml.pushSerializeAndPop(item,
+ ol.format.WFS.GETFEATURE_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('Query'), featureTypes,
+ objectStack);
+};
+
+
+/**
+ * Encode format as WFS `GetFeature` and return the Node.
+ *
+ * @param {olx.format.WFSWriteGetFeatureOptions} options Options.
+ * @return {Node} Result.
+ * @api stable
+ */
+ol.format.WFS.prototype.writeGetFeature = function(options) {
+ var node = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'GetFeature');
+ node.setAttribute('service', 'WFS');
+ node.setAttribute('version', '1.1.0');
+ var filter;
+ if (options) {
+ if (options.handle) {
+ node.setAttribute('handle', options.handle);
+ }
+ if (options.outputFormat) {
+ node.setAttribute('outputFormat', options.outputFormat);
+ }
+ if (options.maxFeatures !== undefined) {
+ node.setAttribute('maxFeatures', options.maxFeatures);
+ }
+ if (options.resultType) {
+ node.setAttribute('resultType', options.resultType);
+ }
+ if (options.startIndex !== undefined) {
+ node.setAttribute('startIndex', options.startIndex);
+ }
+ if (options.count !== undefined) {
+ node.setAttribute('count', options.count);
+ }
+ filter = options.filter;
+ if (options.bbox) {
+ ol.asserts.assert(options.geometryName,
+ 12); // `options.geometryName` must also be provided when `options.bbox` is set
+ var bbox = ol.format.filter.bbox(
+ /** @type {string} */ (options.geometryName), options.bbox, options.srsName);
+ if (filter) {
+ // if bbox and filter are both set, combine the two into a single filter
+ filter = ol.format.filter.and(filter, bbox);
+ } else {
+ filter = bbox;
+ }
+ }
+ }
+ ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance',
+ 'xsi:schemaLocation', this.schemaLocation_);
+ /** @type {ol.XmlNodeStackItem} */
+ var context = {
+ node: node,
+ 'srsName': options.srsName,
+ 'featureNS': options.featureNS ? options.featureNS : this.featureNS_,
+ 'featurePrefix': options.featurePrefix,
+ 'geometryName': options.geometryName,
+ 'filter': filter,
+ 'propertyNames': options.propertyNames ? options.propertyNames : []
+ };
+ ol.asserts.assert(Array.isArray(options.featureTypes),
+ 11); // `options.featureTypes` should be an Array
+ ol.format.WFS.writeGetFeature_(node, /** @type {!Array.<string>} */ (options.featureTypes), [context]);
+ return node;
+};
+
+
+/**
+ * Encode format as WFS `Transaction` and return the Node.
+ *
+ * @param {Array.<ol.Feature>} inserts The features to insert.
+ * @param {Array.<ol.Feature>} updates The features to update.
+ * @param {Array.<ol.Feature>} deletes The features to delete.
+ * @param {olx.format.WFSWriteTransactionOptions} options Write options.
+ * @return {Node} Result.
+ * @api stable
+ */
+ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes,
+ options) {
+ var objectStack = [];
+ var node = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Transaction');
+ node.setAttribute('service', 'WFS');
+ node.setAttribute('version', '1.1.0');
+ var baseObj;
+ /** @type {ol.XmlNodeStackItem} */
+ var obj;
+ if (options) {
+ baseObj = options.gmlOptions ? options.gmlOptions : {};
+ if (options.handle) {
+ node.setAttribute('handle', options.handle);
+ }
+ }
+ ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance',
+ 'xsi:schemaLocation', this.schemaLocation_);
+ if (inserts) {
+ obj = {node: node, 'featureNS': options.featureNS,
+ 'featureType': options.featureType, 'featurePrefix': options.featurePrefix,
+ 'srsName': options.srsName};
+ ol.obj.assign(obj, baseObj);
+ ol.xml.pushSerializeAndPop(obj,
+ ol.format.WFS.TRANSACTION_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('Insert'), inserts,
+ objectStack);
+ }
+ if (updates) {
+ obj = {node: node, 'featureNS': options.featureNS,
+ 'featureType': options.featureType, 'featurePrefix': options.featurePrefix,
+ 'srsName': options.srsName};
+ ol.obj.assign(obj, baseObj);
+ ol.xml.pushSerializeAndPop(obj,
+ ol.format.WFS.TRANSACTION_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('Update'), updates,
+ objectStack);
+ }
+ if (deletes) {
+ ol.xml.pushSerializeAndPop({node: node, 'featureNS': options.featureNS,
+ 'featureType': options.featureType, 'featurePrefix': options.featurePrefix,
+ 'srsName': options.srsName},
+ ol.format.WFS.TRANSACTION_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('Delete'), deletes,
+ objectStack);
+ }
+ if (options.nativeElements) {
+ ol.xml.pushSerializeAndPop({node: node, 'featureNS': options.featureNS,
+ 'featureType': options.featureType, 'featurePrefix': options.featurePrefix,
+ 'srsName': options.srsName},
+ ol.format.WFS.TRANSACTION_SERIALIZERS_,
+ ol.xml.makeSimpleNodeFactory('Native'), options.nativeElements,
+ objectStack);
+ }
+ return node;
+};
+
+
+/**
+ * Read the projection from a WFS source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @return {?ol.proj.Projection} Projection.
+ * @api stable
+ */
+ol.format.WFS.prototype.readProjection;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WFS.prototype.readProjectionFromDocument = function(doc) {
+ ol.DEBUG && console.assert(doc.nodeType == Node.DOCUMENT_NODE,
+ 'doc.nodeType should be a DOCUMENT');
+ for (var n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ return this.readProjectionFromNode(n);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WFS.prototype.readProjectionFromNode = function(node) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'FeatureCollection',
+ 'localName should be FeatureCollection');
+
+ if (node.firstElementChild &&
+ node.firstElementChild.firstElementChild) {
+ node = node.firstElementChild.firstElementChild;
+ for (var n = node.firstElementChild; n; n = n.nextElementSibling) {
+ if (!(n.childNodes.length === 0 ||
+ (n.childNodes.length === 1 &&
+ n.firstChild.nodeType === 3))) {
+ var objectStack = [{}];
+ this.gmlFormat_.readGeometryElement(n, objectStack);
+ return ol.proj.get(objectStack.pop().srsName);
+ }
+ }
+ }
+
+ return null;
+};
+
+goog.provide('ol.format.WKT');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.format.Feature');
+goog.require('ol.format.TextFeature');
+goog.require('ol.geom.GeometryCollection');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.geom.SimpleGeometry');
+
+
+/**
+ * @classdesc
+ * Geometry format for reading and writing data in the `WellKnownText` (WKT)
+ * format.
+ *
+ * @constructor
+ * @extends {ol.format.TextFeature}
+ * @param {olx.format.WKTOptions=} opt_options Options.
+ * @api stable
+ */
+ol.format.WKT = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.format.TextFeature.call(this);
+
+ /**
+ * Split GeometryCollection into multiple features.
+ * @type {boolean}
+ * @private
+ */
+ this.splitCollection_ = options.splitCollection !== undefined ?
+ options.splitCollection : false;
+
+};
+ol.inherits(ol.format.WKT, ol.format.TextFeature);
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WKT.EMPTY = 'EMPTY';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WKT.Z = 'Z';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WKT.M = 'M';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.WKT.ZM = 'ZM';
+
+
+/**
+ * @param {ol.geom.Point} geom Point geometry.
+ * @return {string} Coordinates part of Point as WKT.
+ * @private
+ */
+ol.format.WKT.encodePointGeometry_ = function(geom) {
+ var coordinates = geom.getCoordinates();
+ if (coordinates.length === 0) {
+ return '';
+ }
+ return coordinates.join(' ');
+};
+
+
+/**
+ * @param {ol.geom.MultiPoint} geom MultiPoint geometry.
+ * @return {string} Coordinates part of MultiPoint as WKT.
+ * @private
+ */
+ol.format.WKT.encodeMultiPointGeometry_ = function(geom) {
+ var array = [];
+ var components = geom.getPoints();
+ for (var i = 0, ii = components.length; i < ii; ++i) {
+ array.push('(' + ol.format.WKT.encodePointGeometry_(components[i]) + ')');
+ }
+ return array.join(',');
+};
+
+
+/**
+ * @param {ol.geom.GeometryCollection} geom GeometryCollection geometry.
+ * @return {string} Coordinates part of GeometryCollection as WKT.
+ * @private
+ */
+ol.format.WKT.encodeGeometryCollectionGeometry_ = function(geom) {
+ var array = [];
+ var geoms = geom.getGeometries();
+ for (var i = 0, ii = geoms.length; i < ii; ++i) {
+ array.push(ol.format.WKT.encode_(geoms[i]));
+ }
+ return array.join(',');
+};
+
+
+/**
+ * @param {ol.geom.LineString|ol.geom.LinearRing} geom LineString geometry.
+ * @return {string} Coordinates part of LineString as WKT.
+ * @private
+ */
+ol.format.WKT.encodeLineStringGeometry_ = function(geom) {
+ var coordinates = geom.getCoordinates();
+ var array = [];
+ for (var i = 0, ii = coordinates.length; i < ii; ++i) {
+ array.push(coordinates[i].join(' '));
+ }
+ return array.join(',');
+};
+
+
+/**
+ * @param {ol.geom.MultiLineString} geom MultiLineString geometry.
+ * @return {string} Coordinates part of MultiLineString as WKT.
+ * @private
+ */
+ol.format.WKT.encodeMultiLineStringGeometry_ = function(geom) {
+ var array = [];
+ var components = geom.getLineStrings();
+ for (var i = 0, ii = components.length; i < ii; ++i) {
+ array.push('(' + ol.format.WKT.encodeLineStringGeometry_(
+ components[i]) + ')');
+ }
+ return array.join(',');
+};
+
+
+/**
+ * @param {ol.geom.Polygon} geom Polygon geometry.
+ * @return {string} Coordinates part of Polygon as WKT.
+ * @private
+ */
+ol.format.WKT.encodePolygonGeometry_ = function(geom) {
+ var array = [];
+ var rings = geom.getLinearRings();
+ for (var i = 0, ii = rings.length; i < ii; ++i) {
+ array.push('(' + ol.format.WKT.encodeLineStringGeometry_(
+ rings[i]) + ')');
+ }
+ return array.join(',');
+};
+
+
+/**
+ * @param {ol.geom.MultiPolygon} geom MultiPolygon geometry.
+ * @return {string} Coordinates part of MultiPolygon as WKT.
+ * @private
+ */
+ol.format.WKT.encodeMultiPolygonGeometry_ = function(geom) {
+ var array = [];
+ var components = geom.getPolygons();
+ for (var i = 0, ii = components.length; i < ii; ++i) {
+ array.push('(' + ol.format.WKT.encodePolygonGeometry_(
+ components[i]) + ')');
+ }
+ return array.join(',');
+};
+
+/**
+ * @param {ol.geom.SimpleGeometry} geom SimpleGeometry geometry.
+ * @return {string} Potential dimensional information for WKT type.
+ * @private
+ */
+ol.format.WKT.encodeGeometryLayout_ = function(geom) {
+ var layout = geom.getLayout();
+ var dimInfo = '';
+ if (layout === ol.geom.GeometryLayout.XYZ || layout === ol.geom.GeometryLayout.XYZM) {
+ dimInfo += ol.format.WKT.Z;
+ }
+ if (layout === ol.geom.GeometryLayout.XYM || layout === ol.geom.GeometryLayout.XYZM) {
+ dimInfo += ol.format.WKT.M;
+ }
+ return dimInfo;
+};
+
+
+/**
+ * Encode a geometry as WKT.
+ * @param {ol.geom.Geometry} geom The geometry to encode.
+ * @return {string} WKT string for the geometry.
+ * @private
+ */
+ol.format.WKT.encode_ = function(geom) {
+ var type = geom.getType();
+ var geometryEncoder = ol.format.WKT.GeometryEncoder_[type];
+ ol.DEBUG && console.assert(geometryEncoder, 'geometryEncoder should be defined');
+ var enc = geometryEncoder(geom);
+ type = type.toUpperCase();
+ if (geom instanceof ol.geom.SimpleGeometry) {
+ var dimInfo = ol.format.WKT.encodeGeometryLayout_(geom);
+ if (dimInfo.length > 0) {
+ type += ' ' + dimInfo;
+ }
+ }
+ if (enc.length === 0) {
+ return type + ' ' + ol.format.WKT.EMPTY;
+ }
+ return type + '(' + enc + ')';
+};
+
+
+/**
+ * @const
+ * @type {Object.<string, function(ol.geom.Geometry): string>}
+ * @private
+ */
+ol.format.WKT.GeometryEncoder_ = {
+ 'Point': ol.format.WKT.encodePointGeometry_,
+ 'LineString': ol.format.WKT.encodeLineStringGeometry_,
+ 'Polygon': ol.format.WKT.encodePolygonGeometry_,
+ 'MultiPoint': ol.format.WKT.encodeMultiPointGeometry_,
+ 'MultiLineString': ol.format.WKT.encodeMultiLineStringGeometry_,
+ 'MultiPolygon': ol.format.WKT.encodeMultiPolygonGeometry_,
+ 'GeometryCollection': ol.format.WKT.encodeGeometryCollectionGeometry_
+};
+
+
+/**
+ * Parse a WKT string.
+ * @param {string} wkt WKT string.
+ * @return {ol.geom.Geometry|undefined}
+ * The geometry created.
+ * @private
+ */
+ol.format.WKT.prototype.parse_ = function(wkt) {
+ var lexer = new ol.format.WKT.Lexer(wkt);
+ var parser = new ol.format.WKT.Parser(lexer);
+ return parser.parse();
+};
+
+
+/**
+ * Read a feature from a WKT source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.Feature} Feature.
+ * @api stable
+ */
+ol.format.WKT.prototype.readFeature;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WKT.prototype.readFeatureFromText = function(text, opt_options) {
+ var geom = this.readGeometryFromText(text, opt_options);
+ if (geom) {
+ var feature = new ol.Feature();
+ feature.setGeometry(geom);
+ return feature;
+ }
+ return null;
+};
+
+
+/**
+ * Read all features from a WKT source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.WKT.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WKT.prototype.readFeaturesFromText = function(text, opt_options) {
+ var geometries = [];
+ var geometry = this.readGeometryFromText(text, opt_options);
+ if (this.splitCollection_ &&
+ geometry.getType() == ol.geom.GeometryType.GEOMETRY_COLLECTION) {
+ geometries = (/** @type {ol.geom.GeometryCollection} */ (geometry))
+ .getGeometriesArray();
+ } else {
+ geometries = [geometry];
+ }
+ var feature, features = [];
+ for (var i = 0, ii = geometries.length; i < ii; ++i) {
+ feature = new ol.Feature();
+ feature.setGeometry(geometries[i]);
+ features.push(feature);
+ }
+ return features;
+};
+
+
+/**
+ * Read a single geometry from a WKT source.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Read options.
+ * @return {ol.geom.Geometry} Geometry.
+ * @api stable
+ */
+ol.format.WKT.prototype.readGeometry;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WKT.prototype.readGeometryFromText = function(text, opt_options) {
+ var geometry = this.parse_(text);
+ if (geometry) {
+ return /** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(geometry, false, opt_options));
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * Encode a feature as a WKT string.
+ *
+ * @function
+ * @param {ol.Feature} feature Feature.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} WKT string.
+ * @api stable
+ */
+ol.format.WKT.prototype.writeFeature;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WKT.prototype.writeFeatureText = function(feature, opt_options) {
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ return this.writeGeometryText(geometry, opt_options);
+ }
+ return '';
+};
+
+
+/**
+ * Encode an array of features as a WKT string.
+ *
+ * @function
+ * @param {Array.<ol.Feature>} features Features.
+ * @param {olx.format.WriteOptions=} opt_options Write options.
+ * @return {string} WKT string.
+ * @api stable
+ */
+ol.format.WKT.prototype.writeFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WKT.prototype.writeFeaturesText = function(features, opt_options) {
+ if (features.length == 1) {
+ return this.writeFeatureText(features[0], opt_options);
+ }
+ var geometries = [];
+ for (var i = 0, ii = features.length; i < ii; ++i) {
+ geometries.push(features[i].getGeometry());
+ }
+ var collection = new ol.geom.GeometryCollection(geometries);
+ return this.writeGeometryText(collection, opt_options);
+};
+
+
+/**
+ * Write a single geometry as a WKT string.
+ *
+ * @function
+ * @param {ol.geom.Geometry} geometry Geometry.
+ * @return {string} WKT string.
+ * @api stable
+ */
+ol.format.WKT.prototype.writeGeometry;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WKT.prototype.writeGeometryText = function(geometry, opt_options) {
+ return ol.format.WKT.encode_(/** @type {ol.geom.Geometry} */ (
+ ol.format.Feature.transformWithOptions(geometry, true, opt_options)));
+};
+
+
+/**
+ * @const
+ * @enum {number}
+ */
+ol.format.WKT.TokenType = {
+ TEXT: 1,
+ LEFT_PAREN: 2,
+ RIGHT_PAREN: 3,
+ NUMBER: 4,
+ COMMA: 5,
+ EOF: 6
+};
+
+
+/**
+ * Class to tokenize a WKT string.
+ * @param {string} wkt WKT string.
+ * @constructor
+ * @protected
+ */
+ol.format.WKT.Lexer = function(wkt) {
+
+ /**
+ * @type {string}
+ */
+ this.wkt = wkt;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.index_ = -1;
+};
+
+
+/**
+ * @param {string} c Character.
+ * @return {boolean} Whether the character is alphabetic.
+ * @private
+ */
+ol.format.WKT.Lexer.prototype.isAlpha_ = function(c) {
+ return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
+};
+
+
+/**
+ * @param {string} c Character.
+ * @param {boolean=} opt_decimal Whether the string number
+ * contains a dot, i.e. is a decimal number.
+ * @return {boolean} Whether the character is numeric.
+ * @private
+ */
+ol.format.WKT.Lexer.prototype.isNumeric_ = function(c, opt_decimal) {
+ var decimal = opt_decimal !== undefined ? opt_decimal : false;
+ return c >= '0' && c <= '9' || c == '.' && !decimal;
+};
+
+
+/**
+ * @param {string} c Character.
+ * @return {boolean} Whether the character is whitespace.
+ * @private
+ */
+ol.format.WKT.Lexer.prototype.isWhiteSpace_ = function(c) {
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+};
+
+
+/**
+ * @return {string} Next string character.
+ * @private
+ */
+ol.format.WKT.Lexer.prototype.nextChar_ = function() {
+ return this.wkt.charAt(++this.index_);
+};
+
+
+/**
+ * Fetch and return the next token.
+ * @return {!ol.WKTToken} Next string token.
+ */
+ol.format.WKT.Lexer.prototype.nextToken = function() {
+ var c = this.nextChar_();
+ var token = {position: this.index_, value: c};
+
+ if (c == '(') {
+ token.type = ol.format.WKT.TokenType.LEFT_PAREN;
+ } else if (c == ',') {
+ token.type = ol.format.WKT.TokenType.COMMA;
+ } else if (c == ')') {
+ token.type = ol.format.WKT.TokenType.RIGHT_PAREN;
+ } else if (this.isNumeric_(c) || c == '-') {
+ token.type = ol.format.WKT.TokenType.NUMBER;
+ token.value = this.readNumber_();
+ } else if (this.isAlpha_(c)) {
+ token.type = ol.format.WKT.TokenType.TEXT;
+ token.value = this.readText_();
+ } else if (this.isWhiteSpace_(c)) {
+ return this.nextToken();
+ } else if (c === '') {
+ token.type = ol.format.WKT.TokenType.EOF;
+ } else {
+ throw new Error('Unexpected character: ' + c);
+ }
+
+ return token;
+};
+
+
+/**
+ * @return {number} Numeric token value.
+ * @private
+ */
+ol.format.WKT.Lexer.prototype.readNumber_ = function() {
+ var c, index = this.index_;
+ var decimal = false;
+ var scientificNotation = false;
+ do {
+ if (c == '.') {
+ decimal = true;
+ } else if (c == 'e' || c == 'E') {
+ scientificNotation = true;
+ }
+ c = this.nextChar_();
+ } while (
+ this.isNumeric_(c, decimal) ||
+ // if we haven't detected a scientific number before, 'e' or 'E'
+ // hint that we should continue to read
+ !scientificNotation && (c == 'e' || c == 'E') ||
+ // once we know that we have a scientific number, both '-' and '+'
+ // are allowed
+ scientificNotation && (c == '-' || c == '+')
+ );
+ return parseFloat(this.wkt.substring(index, this.index_--));
+};
+
+
+/**
+ * @return {string} String token value.
+ * @private
+ */
+ol.format.WKT.Lexer.prototype.readText_ = function() {
+ var c, index = this.index_;
+ do {
+ c = this.nextChar_();
+ } while (this.isAlpha_(c));
+ return this.wkt.substring(index, this.index_--).toUpperCase();
+};
+
+
+/**
+ * Class to parse the tokens from the WKT string.
+ * @param {ol.format.WKT.Lexer} lexer The lexer.
+ * @constructor
+ * @protected
+ */
+ol.format.WKT.Parser = function(lexer) {
+
+ /**
+ * @type {ol.format.WKT.Lexer}
+ * @private
+ */
+ this.lexer_ = lexer;
+
+ /**
+ * @type {ol.WKTToken}
+ * @private
+ */
+ this.token_;
+
+ /**
+ * @type {ol.geom.GeometryLayout}
+ * @private
+ */
+ this.layout_ = ol.geom.GeometryLayout.XY;
+};
+
+
+/**
+ * Fetch the next token form the lexer and replace the active token.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.consume_ = function() {
+ this.token_ = this.lexer_.nextToken();
+};
+
+/**
+ * Tests if the given type matches the type of the current token.
+ * @param {ol.format.WKT.TokenType} type Token type.
+ * @return {boolean} Whether the token matches the given type.
+ */
+ol.format.WKT.Parser.prototype.isTokenType = function(type) {
+ var isMatch = this.token_.type == type;
+ return isMatch;
+};
+
+
+/**
+ * If the given type matches the current token, consume it.
+ * @param {ol.format.WKT.TokenType} type Token type.
+ * @return {boolean} Whether the token matches the given type.
+ */
+ol.format.WKT.Parser.prototype.match = function(type) {
+ var isMatch = this.isTokenType(type);
+ if (isMatch) {
+ this.consume_();
+ }
+ return isMatch;
+};
+
+
+/**
+ * Try to parse the tokens provided by the lexer.
+ * @return {ol.geom.Geometry} The geometry.
+ */
+ol.format.WKT.Parser.prototype.parse = function() {
+ this.consume_();
+ var geometry = this.parseGeometry_();
+ ol.DEBUG && console.assert(this.token_.type == ol.format.WKT.TokenType.EOF,
+ 'token type should be end of file');
+ return geometry;
+};
+
+
+/**
+ * Try to parse the dimensional info.
+ * @return {ol.geom.GeometryLayout} The layout.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseGeometryLayout_ = function() {
+ var layout = ol.geom.GeometryLayout.XY;
+ var dimToken = this.token_;
+ if (this.isTokenType(ol.format.WKT.TokenType.TEXT)) {
+ var dimInfo = dimToken.value;
+ if (dimInfo === ol.format.WKT.Z) {
+ layout = ol.geom.GeometryLayout.XYZ;
+ } else if (dimInfo === ol.format.WKT.M) {
+ layout = ol.geom.GeometryLayout.XYM;
+ } else if (dimInfo === ol.format.WKT.ZM) {
+ layout = ol.geom.GeometryLayout.XYZM;
+ }
+ if (layout !== ol.geom.GeometryLayout.XY) {
+ this.consume_();
+ }
+ }
+ return layout;
+};
+
+
+/**
+ * @return {!ol.geom.Geometry} The geometry.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseGeometry_ = function() {
+ var token = this.token_;
+ if (this.match(ol.format.WKT.TokenType.TEXT)) {
+ var geomType = token.value;
+ this.layout_ = this.parseGeometryLayout_();
+ if (geomType == ol.geom.GeometryType.GEOMETRY_COLLECTION.toUpperCase()) {
+ var geometries = this.parseGeometryCollectionText_();
+ return new ol.geom.GeometryCollection(geometries);
+ } else {
+ var parser = ol.format.WKT.Parser.GeometryParser_[geomType];
+ var ctor = ol.format.WKT.Parser.GeometryConstructor_[geomType];
+ if (!parser || !ctor) {
+ throw new Error('Invalid geometry type: ' + geomType);
+ }
+ var coordinates = parser.call(this);
+ return new ctor(coordinates, this.layout_);
+ }
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<ol.geom.Geometry>} A collection of geometries.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseGeometryCollectionText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var geometries = [];
+ do {
+ geometries.push(this.parseGeometry_());
+ } while (this.match(ol.format.WKT.TokenType.COMMA));
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return geometries;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {Array.<number>} All values in a point.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parsePointText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var coordinates = this.parsePoint_();
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return null;
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} All points in a linestring.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseLineStringText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var coordinates = this.parsePointList_();
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} All points in a polygon.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parsePolygonText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var coordinates = this.parseLineStringTextList_();
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} All points in a multipoint.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseMultiPointText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var coordinates;
+ if (this.token_.type == ol.format.WKT.TokenType.LEFT_PAREN) {
+ coordinates = this.parsePointTextList_();
+ } else {
+ coordinates = this.parsePointList_();
+ }
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} All linestring points
+ * in a multilinestring.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseMultiLineStringText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var coordinates = this.parseLineStringTextList_();
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} All polygon points in a multipolygon.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseMultiPolygonText_ = function() {
+ if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
+ var coordinates = this.parsePolygonTextList_();
+ if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<number>} A point.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parsePoint_ = function() {
+ var coordinates = [];
+ var dimensions = this.layout_.length;
+ for (var i = 0; i < dimensions; ++i) {
+ var token = this.token_;
+ if (this.match(ol.format.WKT.TokenType.NUMBER)) {
+ coordinates.push(token.value);
+ } else {
+ break;
+ }
+ }
+ if (coordinates.length == dimensions) {
+ return coordinates;
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} An array of points.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parsePointList_ = function() {
+ var coordinates = [this.parsePoint_()];
+ while (this.match(ol.format.WKT.TokenType.COMMA)) {
+ coordinates.push(this.parsePoint_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} An array of points.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parsePointTextList_ = function() {
+ var coordinates = [this.parsePointText_()];
+ while (this.match(ol.format.WKT.TokenType.COMMA)) {
+ coordinates.push(this.parsePointText_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} An array of points.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parseLineStringTextList_ = function() {
+ var coordinates = [this.parseLineStringText_()];
+ while (this.match(ol.format.WKT.TokenType.COMMA)) {
+ coordinates.push(this.parseLineStringText_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {!Array.<!Array.<number>>} An array of points.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.parsePolygonTextList_ = function() {
+ var coordinates = [this.parsePolygonText_()];
+ while (this.match(ol.format.WKT.TokenType.COMMA)) {
+ coordinates.push(this.parsePolygonText_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {boolean} Whether the token implies an empty geometry.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.isEmptyGeometry_ = function() {
+ var isEmpty = this.isTokenType(ol.format.WKT.TokenType.TEXT) &&
+ this.token_.value == ol.format.WKT.EMPTY;
+ if (isEmpty) {
+ this.consume_();
+ }
+ return isEmpty;
+};
+
+
+/**
+ * Create an error message for an unexpected token error.
+ * @return {string} Error message.
+ * @private
+ */
+ol.format.WKT.Parser.prototype.formatErrorMessage_ = function() {
+ return 'Unexpected `' + this.token_.value + '` at position ' +
+ this.token_.position + ' in `' + this.lexer_.wkt + '`';
+};
+
+
+/**
+ * @enum {function (new:ol.geom.Geometry, Array, ol.geom.GeometryLayout)}
+ * @private
+ */
+ol.format.WKT.Parser.GeometryConstructor_ = {
+ 'POINT': ol.geom.Point,
+ 'LINESTRING': ol.geom.LineString,
+ 'POLYGON': ol.geom.Polygon,
+ 'MULTIPOINT': ol.geom.MultiPoint,
+ 'MULTILINESTRING': ol.geom.MultiLineString,
+ 'MULTIPOLYGON': ol.geom.MultiPolygon
+};
+
+
+/**
+ * @enum {(function(): Array)}
+ * @private
+ */
+ol.format.WKT.Parser.GeometryParser_ = {
+ 'POINT': ol.format.WKT.Parser.prototype.parsePointText_,
+ 'LINESTRING': ol.format.WKT.Parser.prototype.parseLineStringText_,
+ 'POLYGON': ol.format.WKT.Parser.prototype.parsePolygonText_,
+ 'MULTIPOINT': ol.format.WKT.Parser.prototype.parseMultiPointText_,
+ 'MULTILINESTRING': ol.format.WKT.Parser.prototype.parseMultiLineStringText_,
+ 'MULTIPOLYGON': ol.format.WKT.Parser.prototype.parseMultiPolygonText_
+};
+
+goog.provide('ol.format.WMSCapabilities');
+
+goog.require('ol');
+goog.require('ol.format.XLink');
+goog.require('ol.format.XML');
+goog.require('ol.format.XSD');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Format for reading WMS capabilities data
+ *
+ * @constructor
+ * @extends {ol.format.XML}
+ * @api
+ */
+ol.format.WMSCapabilities = function() {
+
+ ol.format.XML.call(this);
+
+ /**
+ * @type {string|undefined}
+ */
+ this.version = undefined;
+};
+ol.inherits(ol.format.WMSCapabilities, ol.format.XML);
+
+
+/**
+ * Read a WMS capabilities document.
+ *
+ * @function
+ * @param {Document|Node|string} source The XML source.
+ * @return {Object} An object representing the WMS capabilities.
+ * @api
+ */
+ol.format.WMSCapabilities.prototype.read;
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {Object} WMS Capability object.
+ */
+ol.format.WMSCapabilities.prototype.readFromDocument = function(doc) {
+ ol.DEBUG && console.assert(doc.nodeType == Node.DOCUMENT_NODE,
+ 'doc.nodeType should be DOCUMENT');
+ for (var n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ return this.readFromNode(n);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {Object} WMS Capability object.
+ */
+ol.format.WMSCapabilities.prototype.readFromNode = function(node) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'WMS_Capabilities' ||
+ node.localName == 'WMT_MS_Capabilities',
+ 'localName should be WMS_Capabilities or WMT_MS_Capabilities');
+ this.version = node.getAttribute('version').trim();
+ var wmsCapabilityObject = ol.xml.pushParseAndPop({
+ 'version': this.version
+ }, ol.format.WMSCapabilities.PARSERS_, node, []);
+ return wmsCapabilityObject ? wmsCapabilityObject : null;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Attribution object.
+ */
+ol.format.WMSCapabilities.readAttribution_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Attribution',
+ 'localName should be Attribution');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object} Bounding box object.
+ */
+ol.format.WMSCapabilities.readBoundingBox_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'BoundingBox',
+ 'localName should be BoundingBox');
+
+ var extent = [
+ ol.format.XSD.readDecimalString(node.getAttribute('minx')),
+ ol.format.XSD.readDecimalString(node.getAttribute('miny')),
+ ol.format.XSD.readDecimalString(node.getAttribute('maxx')),
+ ol.format.XSD.readDecimalString(node.getAttribute('maxy'))
+ ];
+
+ var resolutions = [
+ ol.format.XSD.readDecimalString(node.getAttribute('resx')),
+ ol.format.XSD.readDecimalString(node.getAttribute('resy'))
+ ];
+
+ return {
+ 'crs': node.getAttribute('CRS'),
+ 'extent': extent,
+ 'res': resolutions
+ };
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {ol.Extent|undefined} Bounding box object.
+ */
+ol.format.WMSCapabilities.readEXGeographicBoundingBox_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'EX_GeographicBoundingBox',
+ 'localName should be EX_GeographicBoundingBox');
+ var geographicBoundingBox = ol.xml.pushParseAndPop(
+ {},
+ ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_,
+ node, objectStack);
+ if (!geographicBoundingBox) {
+ return undefined;
+ }
+ var westBoundLongitude = /** @type {number|undefined} */
+ (geographicBoundingBox['westBoundLongitude']);
+ var southBoundLatitude = /** @type {number|undefined} */
+ (geographicBoundingBox['southBoundLatitude']);
+ var eastBoundLongitude = /** @type {number|undefined} */
+ (geographicBoundingBox['eastBoundLongitude']);
+ var northBoundLatitude = /** @type {number|undefined} */
+ (geographicBoundingBox['northBoundLatitude']);
+ if (westBoundLongitude === undefined || southBoundLatitude === undefined ||
+ eastBoundLongitude === undefined || northBoundLatitude === undefined) {
+ return undefined;
+ }
+ return [
+ westBoundLongitude, southBoundLatitude,
+ eastBoundLongitude, northBoundLatitude
+ ];
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} Capability object.
+ */
+ol.format.WMSCapabilities.readCapability_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Capability',
+ 'localName should be Capability');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.CAPABILITY_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} Service object.
+ */
+ol.format.WMSCapabilities.readService_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Service',
+ 'localName should be Service');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.SERVICE_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} Contact information object.
+ */
+ol.format.WMSCapabilities.readContactInformation_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType shpuld be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ContactInformation',
+ 'localName should be ContactInformation');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_,
+ node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} Contact person object.
+ */
+ol.format.WMSCapabilities.readContactPersonPrimary_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ContactPersonPrimary',
+ 'localName should be ContactPersonPrimary');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_,
+ node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} Contact address object.
+ */
+ol.format.WMSCapabilities.readContactAddress_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'ContactAddress',
+ 'localName should be ContactAddress');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_,
+ node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Array.<string>|undefined} Format array.
+ */
+ol.format.WMSCapabilities.readException_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Exception',
+ 'localName should be Exception');
+ return ol.xml.pushParseAndPop(
+ [], ol.format.WMSCapabilities.EXCEPTION_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {Object|undefined} Layer object.
+ */
+ol.format.WMSCapabilities.readCapabilityLayer_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Layer', 'localName should be Layer');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Layer object.
+ */
+ol.format.WMSCapabilities.readLayer_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Layer', 'localName should be Layer');
+ var parentLayerObject = /** @type {Object.<string,*>} */
+ (objectStack[objectStack.length - 1]);
+
+ var layerObject = ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack);
+
+ if (!layerObject) {
+ return undefined;
+ }
+ var queryable =
+ ol.format.XSD.readBooleanString(node.getAttribute('queryable'));
+ if (queryable === undefined) {
+ queryable = parentLayerObject['queryable'];
+ }
+ layerObject['queryable'] = queryable !== undefined ? queryable : false;
+
+ var cascaded = ol.format.XSD.readNonNegativeIntegerString(
+ node.getAttribute('cascaded'));
+ if (cascaded === undefined) {
+ cascaded = parentLayerObject['cascaded'];
+ }
+ layerObject['cascaded'] = cascaded;
+
+ var opaque = ol.format.XSD.readBooleanString(node.getAttribute('opaque'));
+ if (opaque === undefined) {
+ opaque = parentLayerObject['opaque'];
+ }
+ layerObject['opaque'] = opaque !== undefined ? opaque : false;
+
+ var noSubsets =
+ ol.format.XSD.readBooleanString(node.getAttribute('noSubsets'));
+ if (noSubsets === undefined) {
+ noSubsets = parentLayerObject['noSubsets'];
+ }
+ layerObject['noSubsets'] = noSubsets !== undefined ? noSubsets : false;
+
+ var fixedWidth =
+ ol.format.XSD.readDecimalString(node.getAttribute('fixedWidth'));
+ if (!fixedWidth) {
+ fixedWidth = parentLayerObject['fixedWidth'];
+ }
+ layerObject['fixedWidth'] = fixedWidth;
+
+ var fixedHeight =
+ ol.format.XSD.readDecimalString(node.getAttribute('fixedHeight'));
+ if (!fixedHeight) {
+ fixedHeight = parentLayerObject['fixedHeight'];
+ }
+ layerObject['fixedHeight'] = fixedHeight;
+
+ // See 7.2.4.8
+ var addKeys = ['Style', 'CRS', 'AuthorityURL'];
+ addKeys.forEach(function(key) {
+ if (key in parentLayerObject) {
+ var childValue = layerObject[key] || [];
+ layerObject[key] = childValue.concat(parentLayerObject[key]);
+ }
+ });
+
+ var replaceKeys = ['EX_GeographicBoundingBox', 'BoundingBox', 'Dimension',
+ 'Attribution', 'MinScaleDenominator', 'MaxScaleDenominator'];
+ replaceKeys.forEach(function(key) {
+ if (!(key in layerObject)) {
+ var parentValue = parentLayerObject[key];
+ layerObject[key] = parentValue;
+ }
+ });
+
+ return layerObject;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object} Dimension object.
+ */
+ol.format.WMSCapabilities.readDimension_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Dimension',
+ 'localName should be Dimension');
+ var dimensionObject = {
+ 'name': node.getAttribute('name'),
+ 'units': node.getAttribute('units'),
+ 'unitSymbol': node.getAttribute('unitSymbol'),
+ 'default': node.getAttribute('default'),
+ 'multipleValues': ol.format.XSD.readBooleanString(
+ node.getAttribute('multipleValues')),
+ 'nearestValue': ol.format.XSD.readBooleanString(
+ node.getAttribute('nearestValue')),
+ 'current': ol.format.XSD.readBooleanString(node.getAttribute('current')),
+ 'values': ol.format.XSD.readString(node)
+ };
+ return dimensionObject;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Online resource object.
+ */
+ol.format.WMSCapabilities.readFormatOnlineresource_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_,
+ node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Request object.
+ */
+ol.format.WMSCapabilities.readRequest_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Request',
+ 'localName should be Request');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.REQUEST_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} DCP type object.
+ */
+ol.format.WMSCapabilities.readDCPType_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'DCPType',
+ 'localName should be DCPType');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.DCPTYPE_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} HTTP object.
+ */
+ol.format.WMSCapabilities.readHTTP_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'HTTP', 'localName should be HTTP');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.HTTP_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Operation type object.
+ */
+ol.format.WMSCapabilities.readOperationType_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Online resource object.
+ */
+ol.format.WMSCapabilities.readSizedFormatOnlineresource_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ var formatOnlineresource =
+ ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack);
+ if (formatOnlineresource) {
+ var size = [
+ ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('width')),
+ ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('height'))
+ ];
+ formatOnlineresource['size'] = size;
+ return formatOnlineresource;
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Authority URL object.
+ */
+ol.format.WMSCapabilities.readAuthorityURL_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'AuthorityURL',
+ 'localName should be AuthorityURL');
+ var authorityObject =
+ ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack);
+ if (authorityObject) {
+ authorityObject['name'] = node.getAttribute('name');
+ return authorityObject;
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Metadata URL object.
+ */
+ol.format.WMSCapabilities.readMetadataURL_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'MetadataURL',
+ 'localName should be MetadataURL');
+ var metadataObject =
+ ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack);
+ if (metadataObject) {
+ metadataObject['type'] = node.getAttribute('type');
+ return metadataObject;
+ }
+ return undefined;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Style object.
+ */
+ol.format.WMSCapabilities.readStyle_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Style', 'localName should be Style');
+ return ol.xml.pushParseAndPop(
+ {}, ol.format.WMSCapabilities.STYLE_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Array.<string>|undefined} Keyword list.
+ */
+ol.format.WMSCapabilities.readKeywordList_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'KeywordList',
+ 'localName should be KeywordList');
+ return ol.xml.pushParseAndPop(
+ [], ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.<string>}
+ */
+ol.format.WMSCapabilities.NAMESPACE_URIS_ = [
+ null,
+ 'http://www.opengis.net/wms'
+];
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Service': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readService_),
+ 'Capability': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readCapability_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.CAPABILITY_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Request': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readRequest_),
+ 'Exception': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readException_),
+ 'Layer': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readCapabilityLayer_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.SERVICE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'KeywordList': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readKeywordList_),
+ 'OnlineResource': ol.xml.makeObjectPropertySetter(
+ ol.format.XLink.readHref),
+ 'ContactInformation': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readContactInformation_),
+ 'Fees': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'AccessConstraints': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'LayerLimit': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MaxWidth': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MaxHeight': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'ContactPersonPrimary': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readContactPersonPrimary_),
+ 'ContactPosition': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'ContactAddress': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readContactAddress_),
+ 'ContactVoiceTelephone': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'ContactFacsimileTelephone': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'ContactElectronicMailAddress': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'ContactPerson': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'ContactOrganization': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'AddressType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'StateOrProvince': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'PostCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.EXCEPTION_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Format': ol.xml.makeArrayPusher(ol.format.XSD.readString)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.LAYER_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'KeywordList': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readKeywordList_),
+ 'CRS': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString),
+ 'EX_GeographicBoundingBox': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readEXGeographicBoundingBox_),
+ 'BoundingBox': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readBoundingBox_),
+ 'Dimension': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readDimension_),
+ 'Attribution': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readAttribution_),
+ 'AuthorityURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readAuthorityURL_),
+ 'Identifier': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString),
+ 'MetadataURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readMetadataURL_),
+ 'DataURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readFormatOnlineresource_),
+ 'FeatureListURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readFormatOnlineresource_),
+ 'Style': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readStyle_),
+ 'MinScaleDenominator': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal),
+ 'MaxScaleDenominator': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal),
+ 'Layer': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readLayer_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'OnlineResource': ol.xml.makeObjectPropertySetter(
+ ol.format.XLink.readHref),
+ 'LogoURL': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readSizedFormatOnlineresource_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_ =
+ ol.xml.makeStructureNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'westBoundLongitude': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal),
+ 'eastBoundLongitude': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal),
+ 'southBoundLatitude': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal),
+ 'northBoundLatitude': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.REQUEST_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'GetCapabilities': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readOperationType_),
+ 'GetMap': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readOperationType_),
+ 'GetFeatureInfo': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readOperationType_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Format': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString),
+ 'DCPType': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readDCPType_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.DCPTYPE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'HTTP': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readHTTP_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.HTTP_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Get': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readFormatOnlineresource_),
+ 'Post': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readFormatOnlineresource_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'LegendURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMSCapabilities.readSizedFormatOnlineresource_),
+ 'StyleSheetURL': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readFormatOnlineresource_),
+ 'StyleURL': ol.xml.makeObjectPropertySetter(
+ ol.format.WMSCapabilities.readFormatOnlineresource_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_ =
+ ol.xml.makeStructureNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Format': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'OnlineResource': ol.xml.makeObjectPropertySetter(
+ ol.format.XLink.readHref)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMSCapabilities.NAMESPACE_URIS_, {
+ 'Keyword': ol.xml.makeArrayPusher(ol.format.XSD.readString)
+ });
+
+goog.provide('ol.format.WMSGetFeatureInfo');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.format.GML2');
+goog.require('ol.format.XMLFeature');
+goog.require('ol.obj');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Format for reading WMSGetFeatureInfo format. It uses
+ * {@link ol.format.GML2} to read features.
+ *
+ * @constructor
+ * @extends {ol.format.XMLFeature}
+ * @param {olx.format.WMSGetFeatureInfoOptions=} opt_options Options.
+ * @api
+ */
+ol.format.WMSGetFeatureInfo = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver';
+
+
+ /**
+ * @private
+ * @type {ol.format.GML2}
+ */
+ this.gmlFormat_ = new ol.format.GML2();
+
+
+ /**
+ * @private
+ * @type {Array.<string>}
+ */
+ this.layers_ = options.layers ? options.layers : null;
+
+ ol.format.XMLFeature.call(this);
+};
+ol.inherits(ol.format.WMSGetFeatureInfo, ol.format.XMLFeature);
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ol.format.WMSGetFeatureInfo.featureIdentifier_ = '_feature';
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ol.format.WMSGetFeatureInfo.layerIdentifier_ = '_layer';
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Array.<ol.Feature>} Features.
+ * @private
+ */
+ol.format.WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) {
+
+ node.setAttribute('namespaceURI', this.featureNS_);
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ var localName = node.localName;
+ /** @type {Array.<ol.Feature>} */
+ var features = [];
+ if (node.childNodes.length === 0) {
+ return features;
+ }
+ if (localName == 'msGMLOutput') {
+ for (var i = 0, ii = node.childNodes.length; i < ii; i++) {
+ var layer = node.childNodes[i];
+ if (layer.nodeType !== Node.ELEMENT_NODE) {
+ continue;
+ }
+ var context = objectStack[0];
+
+ ol.DEBUG && console.assert(layer.localName.indexOf(
+ ol.format.WMSGetFeatureInfo.layerIdentifier_) >= 0,
+ 'localName of layer node should match layerIdentifier');
+
+ var toRemove = ol.format.WMSGetFeatureInfo.layerIdentifier_;
+ var layerName = layer.localName.replace(toRemove, '');
+
+ if (this.layers_ && !ol.array.includes(this.layers_, layerName)) {
+ continue;
+ }
+
+ var featureType = layerName +
+ ol.format.WMSGetFeatureInfo.featureIdentifier_;
+
+ context['featureType'] = featureType;
+ context['featureNS'] = this.featureNS_;
+
+ var parsers = {};
+ parsers[featureType] = ol.xml.makeArrayPusher(
+ this.gmlFormat_.readFeatureElement, this.gmlFormat_);
+ var parsersNS = ol.xml.makeStructureNS(
+ [context['featureNS'], null], parsers);
+ layer.setAttribute('namespaceURI', this.featureNS_);
+ var layerFeatures = ol.xml.pushParseAndPop(
+ [], parsersNS, layer, objectStack, this.gmlFormat_);
+ if (layerFeatures) {
+ ol.array.extend(features, layerFeatures);
+ }
+ }
+ }
+ if (localName == 'FeatureCollection') {
+ var gmlFeatures = ol.xml.pushParseAndPop([],
+ this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
+ [{}], this.gmlFormat_);
+ if (gmlFeatures) {
+ features = gmlFeatures;
+ }
+ }
+ return features;
+};
+
+
+/**
+ * Read all features from a WMSGetFeatureInfo response.
+ *
+ * @function
+ * @param {Document|Node|Object|string} source Source.
+ * @param {olx.format.ReadOptions=} opt_options Options.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.format.WMSGetFeatureInfo.prototype.readFeatures;
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.WMSGetFeatureInfo.prototype.readFeaturesFromNode = function(node, opt_options) {
+ var options = {};
+ if (opt_options) {
+ ol.obj.assign(options, this.getReadOptions(node, opt_options));
+ }
+ return this.readFeatures_(node, [options]);
+};
+
+goog.provide('ol.format.WMTSCapabilities');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.format.OWS');
+goog.require('ol.format.XLink');
+goog.require('ol.format.XML');
+goog.require('ol.format.XSD');
+goog.require('ol.xml');
+
+
+/**
+ * @classdesc
+ * Format for reading WMTS capabilities data.
+ *
+ * @constructor
+ * @extends {ol.format.XML}
+ * @api
+ */
+ol.format.WMTSCapabilities = function() {
+ ol.format.XML.call(this);
+
+ /**
+ * @type {ol.format.OWS}
+ * @private
+ */
+ this.owsParser_ = new ol.format.OWS();
+};
+ol.inherits(ol.format.WMTSCapabilities, ol.format.XML);
+
+
+/**
+ * Read a WMTS capabilities document.
+ *
+ * @function
+ * @param {Document|Node|string} source The XML source.
+ * @return {Object} An object representing the WMTS capabilities.
+ * @api
+ */
+ol.format.WMTSCapabilities.prototype.read;
+
+
+/**
+ * @param {Document} doc Document.
+ * @return {Object} WMTS Capability object.
+ */
+ol.format.WMTSCapabilities.prototype.readFromDocument = function(doc) {
+ ol.DEBUG && console.assert(doc.nodeType == Node.DOCUMENT_NODE,
+ 'doc.nodeType should be DOCUMENT');
+ for (var n = doc.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType == Node.ELEMENT_NODE) {
+ return this.readFromNode(n);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {Object} WMTS Capability object.
+ */
+ol.format.WMTSCapabilities.prototype.readFromNode = function(node) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Capabilities',
+ 'localName should be Capabilities');
+ var version = node.getAttribute('version').trim();
+ var WMTSCapabilityObject = this.owsParser_.readFromNode(node);
+ if (!WMTSCapabilityObject) {
+ return null;
+ }
+ WMTSCapabilityObject['version'] = version;
+ WMTSCapabilityObject = ol.xml.pushParseAndPop(WMTSCapabilityObject,
+ ol.format.WMTSCapabilities.PARSERS_, node, []);
+ return WMTSCapabilityObject ? WMTSCapabilityObject : null;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Attribution object.
+ */
+ol.format.WMTSCapabilities.readContents_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Contents',
+ 'localName should be Contents');
+
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.CONTENTS_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Layers object.
+ */
+ol.format.WMTSCapabilities.readLayer_ = function(node, objectStack) {
+ ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE,
+ 'node.nodeType should be ELEMENT');
+ ol.DEBUG && console.assert(node.localName == 'Layer', 'localName should be Layer');
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.LAYER_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Tile Matrix Set object.
+ */
+ol.format.WMTSCapabilities.readTileMatrixSet_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.TMS_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Style object.
+ */
+ol.format.WMTSCapabilities.readStyle_ = function(node, objectStack) {
+ var style = ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.STYLE_PARSERS_, node, objectStack);
+ if (!style) {
+ return undefined;
+ }
+ var isDefault = node.getAttribute('isDefault') === 'true';
+ style['isDefault'] = isDefault;
+ return style;
+
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Tile Matrix Set Link object.
+ */
+ol.format.WMTSCapabilities.readTileMatrixSetLink_ = function(node,
+ objectStack) {
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Dimension object.
+ */
+ol.format.WMTSCapabilities.readDimensions_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.DIMENSION_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Resource URL object.
+ */
+ol.format.WMTSCapabilities.readResourceUrl_ = function(node, objectStack) {
+ var format = node.getAttribute('format');
+ var template = node.getAttribute('template');
+ var resourceType = node.getAttribute('resourceType');
+ var resource = {};
+ if (format) {
+ resource['format'] = format;
+ }
+ if (template) {
+ resource['template'] = template;
+ }
+ if (resourceType) {
+ resource['resourceType'] = resourceType;
+ }
+ return resource;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} WGS84 BBox object.
+ */
+ol.format.WMTSCapabilities.readWgs84BoundingBox_ = function(node, objectStack) {
+ var coordinates = ol.xml.pushParseAndPop([],
+ ol.format.WMTSCapabilities.WGS84_BBOX_READERS_, node, objectStack);
+ if (coordinates.length != 2) {
+ return undefined;
+ }
+ return ol.extent.boundingExtent(coordinates);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Legend object.
+ */
+ol.format.WMTSCapabilities.readLegendUrl_ = function(node, objectStack) {
+ var legend = {};
+ legend['format'] = node.getAttribute('format');
+ legend['href'] = ol.format.XLink.readHref(node);
+ return legend;
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} Coordinates object.
+ */
+ol.format.WMTSCapabilities.readCoordinates_ = function(node, objectStack) {
+ var coordinates = ol.format.XSD.readString(node).split(' ');
+ if (!coordinates || coordinates.length != 2) {
+ return undefined;
+ }
+ var x = +coordinates[0];
+ var y = +coordinates[1];
+ if (isNaN(x) || isNaN(y)) {
+ return undefined;
+ }
+ return [x, y];
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} TileMatrix object.
+ */
+ol.format.WMTSCapabilities.readTileMatrix_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.TM_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} TileMatrixSetLimits Object.
+ */
+ol.format.WMTSCapabilities.readTileMatrixLimitsList_ = function(node,
+ objectStack) {
+ return ol.xml.pushParseAndPop([],
+ ol.format.WMTSCapabilities.TMS_LIMITS_LIST_PARSERS_, node,
+ objectStack);
+};
+
+
+/**
+ * @private
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @return {Object|undefined} TileMatrixLimits Array.
+ */
+ol.format.WMTSCapabilities.readTileMatrixLimits_ = function(node, objectStack) {
+ return ol.xml.pushParseAndPop({},
+ ol.format.WMTSCapabilities.TMS_LIMITS_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.<string>}
+ */
+ol.format.WMTSCapabilities.NAMESPACE_URIS_ = [
+ null,
+ 'http://www.opengis.net/wmts/1.0'
+];
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.<string>}
+ */
+ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_ = [
+ null,
+ 'http://www.opengis.net/ows/1.1'
+];
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'Contents': ol.xml.makeObjectPropertySetter(
+ ol.format.WMTSCapabilities.readContents_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.CONTENTS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'Layer': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readLayer_),
+ 'TileMatrixSet': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readTileMatrixSet_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.LAYER_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'Style': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readStyle_),
+ 'Format': ol.xml.makeObjectPropertyPusher(
+ ol.format.XSD.readString),
+ 'TileMatrixSetLink': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readTileMatrixSetLink_),
+ 'Dimension': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readDimensions_),
+ 'ResourceURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readResourceUrl_)
+ }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
+ 'Title': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'Abstract': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'WGS84BoundingBox': ol.xml.makeObjectPropertySetter(
+ ol.format.WMTSCapabilities.readWgs84BoundingBox_),
+ 'Identifier': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.STYLE_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'LegendURL': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readLegendUrl_)
+ }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
+ 'Title': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'Identifier': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'TileMatrixSet': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'TileMatrixSetLimits': ol.xml.makeObjectPropertySetter(
+ ol.format.WMTSCapabilities.readTileMatrixLimitsList_)
+ });
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.TMS_LIMITS_LIST_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'TileMatrixLimits': ol.xml.makeArrayPusher(
+ ol.format.WMTSCapabilities.readTileMatrixLimits_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.TMS_LIMITS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'TileMatrix': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'MinTileRow': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MaxTileRow': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MinTileCol': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MaxTileCol': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.DIMENSION_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'Default': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'Value': ol.xml.makeObjectPropertyPusher(
+ ol.format.XSD.readString)
+ }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
+ 'Identifier': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.WGS84_BBOX_READERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
+ 'LowerCorner': ol.xml.makeArrayPusher(
+ ol.format.WMTSCapabilities.readCoordinates_),
+ 'UpperCorner': ol.xml.makeArrayPusher(
+ ol.format.WMTSCapabilities.readCoordinates_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.TMS_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'WellKnownScaleSet': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'TileMatrix': ol.xml.makeObjectPropertyPusher(
+ ol.format.WMTSCapabilities.readTileMatrix_)
+ }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
+ 'SupportedCRS': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString),
+ 'Identifier': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ }));
+
+
+/**
+ * @const
+ * @type {Object.<string, Object.<string, ol.XmlParser>>}
+ * @private
+ */
+ol.format.WMTSCapabilities.TM_PARSERS_ = ol.xml.makeStructureNS(
+ ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
+ 'TopLeftCorner': ol.xml.makeObjectPropertySetter(
+ ol.format.WMTSCapabilities.readCoordinates_),
+ 'ScaleDenominator': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readDecimal),
+ 'TileWidth': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'TileHeight': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MatrixWidth': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'MatrixHeight': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger)
+ }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
+ 'Identifier': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readString)
+ }));
+
+// FIXME handle geolocation not supported
+
+goog.provide('ol.Geolocation');
+
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.geom.Polygon');
+goog.require('ol.has');
+goog.require('ol.math');
+goog.require('ol.proj');
+goog.require('ol.sphere.WGS84');
+
+
+/**
+ * @classdesc
+ * Helper class for providing HTML5 Geolocation capabilities.
+ * The [Geolocation API](http://www.w3.org/TR/geolocation-API/)
+ * is used to locate a user's position.
+ *
+ * To get notified of position changes, register a listener for the generic
+ * `change` event on your instance of `ol.Geolocation`.
+ *
+ * Example:
+ *
+ * var geolocation = new ol.Geolocation({
+ * // take the projection to use from the map's view
+ * projection: view.getProjection()
+ * });
+ * // listen to changes in position
+ * geolocation.on('change', function(evt) {
+ * window.console.log(geolocation.getPosition());
+ * });
+ *
+ * @fires error
+ * @constructor
+ * @extends {ol.Object}
+ * @param {olx.GeolocationOptions=} opt_options Options.
+ * @api stable
+ */
+ol.Geolocation = function(opt_options) {
+
+ ol.Object.call(this);
+
+ var options = opt_options || {};
+
+ /**
+ * The unprojected (EPSG:4326) device position.
+ * @private
+ * @type {ol.Coordinate}
+ */
+ this.position_ = null;
+
+ /**
+ * @private
+ * @type {ol.TransformFunction}
+ */
+ this.transform_ = ol.proj.identityTransform;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.watchId_ = undefined;
+
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Geolocation.Property.PROJECTION),
+ this.handleProjectionChanged_, this);
+ ol.events.listen(
+ this, ol.Object.getChangeEventType(ol.Geolocation.Property.TRACKING),
+ this.handleTrackingChanged_, this);
+
+ if (options.projection !== undefined) {
+ this.setProjection(ol.proj.get(options.projection));
+ }
+ if (options.trackingOptions !== undefined) {
+ this.setTrackingOptions(options.trackingOptions);
+ }
+
+ this.setTracking(options.tracking !== undefined ? options.tracking : false);
+
+};
+ol.inherits(ol.Geolocation, ol.Object);
+
+
+/**
+ * @inheritDoc
+ */
+ol.Geolocation.prototype.disposeInternal = function() {
+ this.setTracking(false);
+ ol.Object.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * @private
+ */
+ol.Geolocation.prototype.handleProjectionChanged_ = function() {
+ var projection = this.getProjection();
+ if (projection) {
+ this.transform_ = ol.proj.getTransformFromProjections(
+ ol.proj.get('EPSG:4326'), projection);
+ if (this.position_) {
+ this.set(
+ ol.Geolocation.Property.POSITION, this.transform_(this.position_));
+ }
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.Geolocation.prototype.handleTrackingChanged_ = function() {
+ if (ol.has.GEOLOCATION) {
+ var tracking = this.getTracking();
+ if (tracking && this.watchId_ === undefined) {
+ this.watchId_ = navigator.geolocation.watchPosition(
+ this.positionChange_.bind(this),
+ this.positionError_.bind(this),
+ this.getTrackingOptions());
+ } else if (!tracking && this.watchId_ !== undefined) {
+ navigator.geolocation.clearWatch(this.watchId_);
+ this.watchId_ = undefined;
+ }
+ }
+};
+
+
+/**
+ * @private
+ * @param {GeolocationPosition} position position event.
+ */
+ol.Geolocation.prototype.positionChange_ = function(position) {
+ var coords = position.coords;
+ this.set(ol.Geolocation.Property.ACCURACY, coords.accuracy);
+ this.set(ol.Geolocation.Property.ALTITUDE,
+ coords.altitude === null ? undefined : coords.altitude);
+ this.set(ol.Geolocation.Property.ALTITUDE_ACCURACY,
+ coords.altitudeAccuracy === null ?
+ undefined : coords.altitudeAccuracy);
+ this.set(ol.Geolocation.Property.HEADING, coords.heading === null ?
+ undefined : ol.math.toRadians(coords.heading));
+ if (!this.position_) {
+ this.position_ = [coords.longitude, coords.latitude];
+ } else {
+ this.position_[0] = coords.longitude;
+ this.position_[1] = coords.latitude;
+ }
+ var projectedPosition = this.transform_(this.position_);
+ this.set(ol.Geolocation.Property.POSITION, projectedPosition);
+ this.set(ol.Geolocation.Property.SPEED,
+ coords.speed === null ? undefined : coords.speed);
+ var geometry = ol.geom.Polygon.circular(
+ ol.sphere.WGS84, this.position_, coords.accuracy);
+ geometry.applyTransform(this.transform_);
+ this.set(ol.Geolocation.Property.ACCURACY_GEOMETRY, geometry);
+ this.changed();
+};
+
+/**
+ * Triggered when the Geolocation returns an error.
+ * @event error
+ * @api
+ */
+
+/**
+ * @private
+ * @param {GeolocationPositionError} error error object.
+ */
+ol.Geolocation.prototype.positionError_ = function(error) {
+ error.type = ol.events.EventType.ERROR;
+ this.setTracking(false);
+ this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error));
+};
+
+
+/**
+ * Get the accuracy of the position in meters.
+ * @return {number|undefined} The accuracy of the position measurement in
+ * meters.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getAccuracy = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.Geolocation.Property.ACCURACY));
+};
+
+
+/**
+ * Get a geometry of the position accuracy.
+ * @return {?ol.geom.Geometry} A geometry of the position accuracy.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getAccuracyGeometry = function() {
+ return /** @type {?ol.geom.Geometry} */ (
+ this.get(ol.Geolocation.Property.ACCURACY_GEOMETRY) || null);
+};
+
+
+/**
+ * Get the altitude associated with the position.
+ * @return {number|undefined} The altitude of the position in meters above mean
+ * sea level.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getAltitude = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.Geolocation.Property.ALTITUDE));
+};
+
+
+/**
+ * Get the altitude accuracy of the position.
+ * @return {number|undefined} The accuracy of the altitude measurement in
+ * meters.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getAltitudeAccuracy = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.Geolocation.Property.ALTITUDE_ACCURACY));
+};
+
+
+/**
+ * Get the heading as radians clockwise from North.
+ * @return {number|undefined} The heading of the device in radians from north.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getHeading = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.Geolocation.Property.HEADING));
+};
+
+
+/**
+ * Get the position of the device.
+ * @return {ol.Coordinate|undefined} The current position of the device reported
+ * in the current projection.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getPosition = function() {
+ return /** @type {ol.Coordinate|undefined} */ (
+ this.get(ol.Geolocation.Property.POSITION));
+};
+
+
+/**
+ * Get the projection associated with the position.
+ * @return {ol.proj.Projection|undefined} The projection the position is
+ * reported in.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getProjection = function() {
+ return /** @type {ol.proj.Projection|undefined} */ (
+ this.get(ol.Geolocation.Property.PROJECTION));
+};
+
+
+/**
+ * Get the speed in meters per second.
+ * @return {number|undefined} The instantaneous speed of the device in meters
+ * per second.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getSpeed = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.Geolocation.Property.SPEED));
+};
+
+
+/**
+ * Determine if the device location is being tracked.
+ * @return {boolean} The device location is being tracked.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getTracking = function() {
+ return /** @type {boolean} */ (
+ this.get(ol.Geolocation.Property.TRACKING));
+};
+
+
+/**
+ * Get the tracking options.
+ * @see http://www.w3.org/TR/geolocation-API/#position-options
+ * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by
+ * the [HTML5 Geolocation spec
+ * ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.getTrackingOptions = function() {
+ return /** @type {GeolocationPositionOptions|undefined} */ (
+ this.get(ol.Geolocation.Property.TRACKING_OPTIONS));
+};
+
+
+/**
+ * Set the projection to use for transforming the coordinates.
+ * @param {ol.proj.Projection} projection The projection the position is
+ * reported in.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.setProjection = function(projection) {
+ this.set(ol.Geolocation.Property.PROJECTION, projection);
+};
+
+
+/**
+ * Enable or disable tracking.
+ * @param {boolean} tracking Enable tracking.
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.setTracking = function(tracking) {
+ this.set(ol.Geolocation.Property.TRACKING, tracking);
+};
+
+
+/**
+ * Set the tracking options.
+ * @see http://www.w3.org/TR/geolocation-API/#position-options
+ * @param {GeolocationPositionOptions} options PositionOptions as defined by the
+ * [HTML5 Geolocation spec
+ * ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
+ * @observable
+ * @api stable
+ */
+ol.Geolocation.prototype.setTrackingOptions = function(options) {
+ this.set(ol.Geolocation.Property.TRACKING_OPTIONS, options);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.Geolocation.Property = {
+ ACCURACY: 'accuracy',
+ ACCURACY_GEOMETRY: 'accuracyGeometry',
+ ALTITUDE: 'altitude',
+ ALTITUDE_ACCURACY: 'altitudeAccuracy',
+ HEADING: 'heading',
+ POSITION: 'position',
+ PROJECTION: 'projection',
+ SPEED: 'speed',
+ TRACKING: 'tracking',
+ TRACKING_OPTIONS: 'trackingOptions'
+};
+
+goog.provide('ol.geom.Circle');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.geom.flat.deflate');
+
+
+/**
+ * @classdesc
+ * Circle geometry.
+ *
+ * @constructor
+ * @extends {ol.geom.SimpleGeometry}
+ * @param {ol.Coordinate} center Center.
+ * @param {number=} opt_radius Radius.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api
+ */
+ol.geom.Circle = function(center, opt_radius, opt_layout) {
+ ol.geom.SimpleGeometry.call(this);
+ var radius = opt_radius ? opt_radius : 0;
+ this.setCenterAndRadius(center, radius, opt_layout);
+};
+ol.inherits(ol.geom.Circle, ol.geom.SimpleGeometry);
+
+
+/**
+ * Make a complete copy of the geometry.
+ * @return {!ol.geom.Circle} Clone.
+ * @api
+ */
+ol.geom.Circle.prototype.clone = function() {
+ var circle = new ol.geom.Circle(null);
+ circle.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
+ return circle;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Circle.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
+ var flatCoordinates = this.flatCoordinates;
+ var dx = x - flatCoordinates[0];
+ var dy = y - flatCoordinates[1];
+ var squaredDistance = dx * dx + dy * dy;
+ if (squaredDistance < minSquaredDistance) {
+ var i;
+ if (squaredDistance === 0) {
+ for (i = 0; i < this.stride; ++i) {
+ closestPoint[i] = flatCoordinates[i];
+ }
+ } else {
+ var delta = this.getRadius() / Math.sqrt(squaredDistance);
+ closestPoint[0] = flatCoordinates[0] + delta * dx;
+ closestPoint[1] = flatCoordinates[1] + delta * dy;
+ for (i = 2; i < this.stride; ++i) {
+ closestPoint[i] = flatCoordinates[i];
+ }
+ }
+ closestPoint.length = this.stride;
+ return squaredDistance;
+ } else {
+ return minSquaredDistance;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Circle.prototype.containsXY = function(x, y) {
+ var flatCoordinates = this.flatCoordinates;
+ var dx = x - flatCoordinates[0];
+ var dy = y - flatCoordinates[1];
+ return dx * dx + dy * dy <= this.getRadiusSquared_();
+};
+
+
+/**
+ * Return the center of the circle as {@link ol.Coordinate coordinate}.
+ * @return {ol.Coordinate} Center.
+ * @api
+ */
+ol.geom.Circle.prototype.getCenter = function() {
+ return this.flatCoordinates.slice(0, this.stride);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.geom.Circle.prototype.computeExtent = function(extent) {
+ var flatCoordinates = this.flatCoordinates;
+ var radius = flatCoordinates[this.stride] - flatCoordinates[0];
+ return ol.extent.createOrUpdate(
+ flatCoordinates[0] - radius, flatCoordinates[1] - radius,
+ flatCoordinates[0] + radius, flatCoordinates[1] + radius,
+ extent);
+};
+
+
+/**
+ * Return the radius of the circle.
+ * @return {number} Radius.
+ * @api
+ */
+ol.geom.Circle.prototype.getRadius = function() {
+ return Math.sqrt(this.getRadiusSquared_());
+};
+
+
+/**
+ * @private
+ * @return {number} Radius squared.
+ */
+ol.geom.Circle.prototype.getRadiusSquared_ = function() {
+ var dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0];
+ var dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1];
+ return dx * dx + dy * dy;
+};
+
+
+/**
+ * @inheritDoc
+ * @api
+ */
+ol.geom.Circle.prototype.getType = function() {
+ return ol.geom.GeometryType.CIRCLE;
+};
+
+
+/**
+ * @inheritDoc
+ * @api stable
+ */
+ol.geom.Circle.prototype.intersectsExtent = function(extent) {
+ var circleExtent = this.getExtent();
+ if (ol.extent.intersects(extent, circleExtent)) {
+ var center = this.getCenter();
+
+ if (extent[0] <= center[0] && extent[2] >= center[0]) {
+ return true;
+ }
+ if (extent[1] <= center[1] && extent[3] >= center[1]) {
+ return true;
+ }
+
+ return ol.extent.forEachCorner(extent, this.intersectsCoordinate, this);
+ }
+ return false;
+
+};
+
+
+/**
+ * Set the center of the circle as {@link ol.Coordinate coordinate}.
+ * @param {ol.Coordinate} center Center.
+ * @api
+ */
+ol.geom.Circle.prototype.setCenter = function(center) {
+ var stride = this.stride;
+ ol.DEBUG && console.assert(center.length == stride,
+ 'center array length should match stride');
+ var radius = this.flatCoordinates[stride] - this.flatCoordinates[0];
+ var flatCoordinates = center.slice();
+ flatCoordinates[stride] = flatCoordinates[0] + radius;
+ var i;
+ for (i = 1; i < stride; ++i) {
+ flatCoordinates[stride + i] = center[i];
+ }
+ this.setFlatCoordinates(this.layout, flatCoordinates);
+};
+
+
+/**
+ * Set the center (as {@link ol.Coordinate coordinate}) and the radius (as
+ * number) of the circle.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} radius Radius.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ * @api
+ */
+ol.geom.Circle.prototype.setCenterAndRadius = function(center, radius, opt_layout) {
+ if (!center) {
+ this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
+ } else {
+ this.setLayout(opt_layout, center, 0);
+ if (!this.flatCoordinates) {
+ this.flatCoordinates = [];
+ }
+ /** @type {Array.<number>} */
+ var flatCoordinates = this.flatCoordinates;
+ var offset = ol.geom.flat.deflate.coordinate(
+ flatCoordinates, 0, center, this.stride);
+ flatCoordinates[offset++] = flatCoordinates[0] + radius;
+ var i, ii;
+ for (i = 1, ii = this.stride; i < ii; ++i) {
+ flatCoordinates[offset++] = flatCoordinates[i];
+ }
+ flatCoordinates.length = offset;
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {ol.geom.GeometryLayout} layout Layout.
+ * @param {Array.<number>} flatCoordinates Flat coordinates.
+ */
+ol.geom.Circle.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
+ this.setFlatCoordinatesInternal(layout, flatCoordinates);
+ this.changed();
+};
+
+
+/**
+ * Set the radius of the circle. The radius is in the units of the projection.
+ * @param {number} radius Radius.
+ * @api
+ */
+ol.geom.Circle.prototype.setRadius = function(radius) {
+ this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius;
+ this.changed();
+};
+
+
+/**
+ * Transform each coordinate of the circle from one coordinate reference system
+ * to another. The geometry is modified in place.
+ * If you do not want the geometry modified in place, first clone() it and
+ * then use this function on the clone.
+ *
+ * Internally a circle is currently represented by two points: the center of
+ * the circle `[cx, cy]`, and the point to the right of the circle
+ * `[cx + r, cy]`. This `transform` function just transforms these two points.
+ * So the resulting geometry is also a circle, and that circle does not
+ * correspond to the shape that would be obtained by transforming every point
+ * of the original circle.
+ *
+ * @param {ol.ProjectionLike} source The current projection. Can be a
+ * string identifier or a {@link ol.proj.Projection} object.
+ * @param {ol.ProjectionLike} destination The desired projection. Can be a
+ * string identifier or a {@link ol.proj.Projection} object.
+ * @return {ol.geom.Circle} This geometry. Note that original geometry is
+ * modified in place.
+ * @function
+ * @api stable
+ */
+ol.geom.Circle.prototype.transform;
+
+goog.provide('ol.geom.flat.geodesic');
+
+goog.require('ol');
+goog.require('ol.math');
+goog.require('ol.proj');
+
+
+/**
+ * @private
+ * @param {function(number): ol.Coordinate} interpolate Interpolate function.
+ * @param {ol.TransformFunction} transform Transform from longitude/latitude to
+ * projected coordinates.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.geom.flat.geodesic.line_ = function(interpolate, transform, squaredTolerance) {
+ // FIXME reduce garbage generation
+ // FIXME optimize stack operations
+
+ /** @type {Array.<number>} */
+ var flatCoordinates = [];
+
+ var geoA = interpolate(0);
+ var geoB = interpolate(1);
+
+ var a = transform(geoA);
+ var b = transform(geoB);
+
+ /** @type {Array.<ol.Coordinate>} */
+ var geoStack = [geoB, geoA];
+ /** @type {Array.<ol.Coordinate>} */
+ var stack = [b, a];
+ /** @type {Array.<number>} */
+ var fractionStack = [1, 0];
+
+ /** @type {Object.<string, boolean>} */
+ var fractions = {};
+
+ var maxIterations = 1e5;
+ var geoM, m, fracA, fracB, fracM, key;
+
+ while (--maxIterations > 0 && fractionStack.length > 0) {
+ // Pop the a coordinate off the stack
+ fracA = fractionStack.pop();
+ geoA = geoStack.pop();
+ a = stack.pop();
+ // Add the a coordinate if it has not been added yet
+ key = fracA.toString();
+ if (!(key in fractions)) {
+ flatCoordinates.push(a[0], a[1]);
+ fractions[key] = true;
+ }
+ // Pop the b coordinate off the stack
+ fracB = fractionStack.pop();
+ geoB = geoStack.pop();
+ b = stack.pop();
+ // Find the m point between the a and b coordinates
+ fracM = (fracA + fracB) / 2;
+ geoM = interpolate(fracM);
+ m = transform(geoM);
+ if (ol.math.squaredSegmentDistance(m[0], m[1], a[0], a[1],
+ b[0], b[1]) < squaredTolerance) {
+ // If the m point is sufficiently close to the straight line, then we
+ // discard it. Just use the b coordinate and move on to the next line
+ // segment.
+ flatCoordinates.push(b[0], b[1]);
+ key = fracB.toString();
+ ol.DEBUG && console.assert(!(key in fractions),
+ 'fractions object should contain key : ' + key);
+ fractions[key] = true;
+ } else {
+ // Otherwise, we need to subdivide the current line segment. Split it
+ // into two and push the two line segments onto the stack.
+ fractionStack.push(fracB, fracM, fracM, fracA);
+ stack.push(b, m, m, a);
+ geoStack.push(geoB, geoM, geoM, geoA);
+ }
+ }
+ ol.DEBUG && console.assert(maxIterations > 0,
+ 'maxIterations should be more than 0');
+
+ return flatCoordinates;
+};
+
+
+/**
+* Generate a great-circle arcs between two lat/lon points.
+* @param {number} lon1 Longitude 1 in degrees.
+* @param {number} lat1 Latitude 1 in degrees.
+* @param {number} lon2 Longitude 2 in degrees.
+* @param {number} lat2 Latitude 2 in degrees.
+ * @param {ol.proj.Projection} projection Projection.
+* @param {number} squaredTolerance Squared tolerance.
+* @return {Array.<number>} Flat coordinates.
+*/
+ol.geom.flat.geodesic.greatCircleArc = function(
+ lon1, lat1, lon2, lat2, projection, squaredTolerance) {
+
+ var geoProjection = ol.proj.get('EPSG:4326');
+
+ var cosLat1 = Math.cos(ol.math.toRadians(lat1));
+ var sinLat1 = Math.sin(ol.math.toRadians(lat1));
+ var cosLat2 = Math.cos(ol.math.toRadians(lat2));
+ var sinLat2 = Math.sin(ol.math.toRadians(lat2));
+ var cosDeltaLon = Math.cos(ol.math.toRadians(lon2 - lon1));
+ var sinDeltaLon = Math.sin(ol.math.toRadians(lon2 - lon1));
+ var d = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosDeltaLon;
+
+ return ol.geom.flat.geodesic.line_(
+ /**
+ * @param {number} frac Fraction.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ function(frac) {
+ if (1 <= d) {
+ return [lon2, lat2];
+ }
+ var D = frac * Math.acos(d);
+ var cosD = Math.cos(D);
+ var sinD = Math.sin(D);
+ var y = sinDeltaLon * cosLat2;
+ var x = cosLat1 * sinLat2 - sinLat1 * cosLat2 * cosDeltaLon;
+ var theta = Math.atan2(y, x);
+ var lat = Math.asin(sinLat1 * cosD + cosLat1 * sinD * Math.cos(theta));
+ var lon = ol.math.toRadians(lon1) +
+ Math.atan2(Math.sin(theta) * sinD * cosLat1,
+ cosD - sinLat1 * Math.sin(lat));
+ return [ol.math.toDegrees(lon), ol.math.toDegrees(lat)];
+ }, ol.proj.getTransform(geoProjection, projection), squaredTolerance);
+};
+
+
+/**
+ * Generate a meridian (line at constant longitude).
+ * @param {number} lon Longitude.
+ * @param {number} lat1 Latitude 1.
+ * @param {number} lat2 Latitude 2.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.geom.flat.geodesic.meridian = function(lon, lat1, lat2, projection, squaredTolerance) {
+ var epsg4326Projection = ol.proj.get('EPSG:4326');
+ return ol.geom.flat.geodesic.line_(
+ /**
+ * @param {number} frac Fraction.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ function(frac) {
+ return [lon, lat1 + ((lat2 - lat1) * frac)];
+ },
+ ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance);
+};
+
+
+/**
+ * Generate a parallel (line at constant latitude).
+ * @param {number} lat Latitude.
+ * @param {number} lon1 Longitude 1.
+ * @param {number} lon2 Longitude 2.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {Array.<number>} Flat coordinates.
+ */
+ol.geom.flat.geodesic.parallel = function(lat, lon1, lon2, projection, squaredTolerance) {
+ var epsg4326Projection = ol.proj.get('EPSG:4326');
+ return ol.geom.flat.geodesic.line_(
+ /**
+ * @param {number} frac Fraction.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ function(frac) {
+ return [lon1 + ((lon2 - lon1) * frac), lat];
+ },
+ ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance);
+};
+
+goog.provide('ol.Graticule');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryLayout');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.flat.geodesic');
+goog.require('ol.math');
+goog.require('ol.proj');
+goog.require('ol.render.Event');
+goog.require('ol.style.Stroke');
+
+
+/**
+ * Render a grid for a coordinate system on a map.
+ * @constructor
+ * @param {olx.GraticuleOptions=} opt_options Options.
+ * @api
+ */
+ol.Graticule = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * @type {ol.Map}
+ * @private
+ */
+ this.map_ = null;
+
+ /**
+ * @type {ol.proj.Projection}
+ * @private
+ */
+ this.projection_ = null;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.maxLat_ = Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.maxLon_ = Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.minLat_ = -Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.minLon_ = -Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.maxLatP_ = Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.maxLonP_ = Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.minLatP_ = -Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.minLonP_ = -Infinity;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.targetSize_ = options.targetSize !== undefined ?
+ options.targetSize : 100;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.maxLines_ = options.maxLines !== undefined ? options.maxLines : 100;
+ ol.DEBUG && console.assert(this.maxLines_ > 0,
+ 'this.maxLines_ should be more than 0');
+
+ /**
+ * @type {Array.<ol.geom.LineString>}
+ * @private
+ */
+ this.meridians_ = [];
+
+ /**
+ * @type {Array.<ol.geom.LineString>}
+ * @private
+ */
+ this.parallels_ = [];
+
+ /**
+ * @type {ol.style.Stroke}
+ * @private
+ */
+ this.strokeStyle_ = options.strokeStyle !== undefined ?
+ options.strokeStyle : ol.Graticule.DEFAULT_STROKE_STYLE_;
+
+ /**
+ * @type {ol.TransformFunction|undefined}
+ * @private
+ */
+ this.fromLonLatTransform_ = undefined;
+
+ /**
+ * @type {ol.TransformFunction|undefined}
+ * @private
+ */
+ this.toLonLatTransform_ = undefined;
+
+ /**
+ * @type {ol.Coordinate}
+ * @private
+ */
+ this.projectionCenterLonLat_ = null;
+
+ this.setMap(options.map !== undefined ? options.map : null);
+};
+
+
+/**
+ * @type {ol.style.Stroke}
+ * @private
+ * @const
+ */
+ol.Graticule.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({
+ color: 'rgba(0,0,0,0.2)'
+});
+
+
+/**
+ * TODO can be configurable
+ * @type {Array.<number>}
+ * @private
+ */
+ol.Graticule.intervals_ = [90, 45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05,
+ 0.01, 0.005, 0.002, 0.001];
+
+
+/**
+ * @param {number} lon Longitude.
+ * @param {number} minLat Minimal latitude.
+ * @param {number} maxLat Maximal latitude.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} index Index.
+ * @return {number} Index.
+ * @private
+ */
+ol.Graticule.prototype.addMeridian_ = function(lon, minLat, maxLat, squaredTolerance, extent, index) {
+ var lineString = this.getMeridian_(lon, minLat, maxLat,
+ squaredTolerance, index);
+ if (ol.extent.intersects(lineString.getExtent(), extent)) {
+ this.meridians_[index++] = lineString;
+ }
+ return index;
+};
+
+
+/**
+ * @param {number} lat Latitude.
+ * @param {number} minLon Minimal longitude.
+ * @param {number} maxLon Maximal longitude.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} index Index.
+ * @return {number} Index.
+ * @private
+ */
+ol.Graticule.prototype.addParallel_ = function(lat, minLon, maxLon, squaredTolerance, extent, index) {
+ var lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance,
+ index);
+ if (ol.extent.intersects(lineString.getExtent(), extent)) {
+ this.parallels_[index++] = lineString;
+ }
+ return index;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Coordinate} center Center.
+ * @param {number} resolution Resolution.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @private
+ */
+ol.Graticule.prototype.createGraticule_ = function(extent, center, resolution, squaredTolerance) {
+
+ var interval = this.getInterval_(resolution);
+ if (interval == -1) {
+ this.meridians_.length = this.parallels_.length = 0;
+ return;
+ }
+
+ var centerLonLat = this.toLonLatTransform_(center);
+ var centerLon = centerLonLat[0];
+ var centerLat = centerLonLat[1];
+ var maxLines = this.maxLines_;
+ var cnt, idx, lat, lon;
+
+ var validExtent = [
+ Math.max(extent[0], this.minLonP_),
+ Math.max(extent[1], this.minLatP_),
+ Math.min(extent[2], this.maxLonP_),
+ Math.min(extent[3], this.maxLatP_)
+ ];
+
+ validExtent = ol.proj.transformExtent(validExtent, this.projection_,
+ 'EPSG:4326');
+ var maxLat = validExtent[3];
+ var maxLon = validExtent[2];
+ var minLat = validExtent[1];
+ var minLon = validExtent[0];
+
+ // Create meridians
+
+ centerLon = Math.floor(centerLon / interval) * interval;
+ lon = ol.math.clamp(centerLon, this.minLon_, this.maxLon_);
+
+ idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0);
+
+ cnt = 0;
+ while (lon != this.minLon_ && cnt++ < maxLines) {
+ lon = Math.max(lon - interval, this.minLon_);
+ idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
+ }
+
+ lon = ol.math.clamp(centerLon, this.minLon_, this.maxLon_);
+
+ cnt = 0;
+ while (lon != this.maxLon_ && cnt++ < maxLines) {
+ lon = Math.min(lon + interval, this.maxLon_);
+ idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
+ }
+
+ this.meridians_.length = idx;
+
+ // Create parallels
+
+ centerLat = Math.floor(centerLat / interval) * interval;
+ lat = ol.math.clamp(centerLat, this.minLat_, this.maxLat_);
+
+ idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, 0);
+
+ cnt = 0;
+ while (lat != this.minLat_ && cnt++ < maxLines) {
+ lat = Math.max(lat - interval, this.minLat_);
+ idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx);
+ }
+
+ lat = ol.math.clamp(centerLat, this.minLat_, this.maxLat_);
+
+ cnt = 0;
+ while (lat != this.maxLat_ && cnt++ < maxLines) {
+ lat = Math.min(lat + interval, this.maxLat_);
+ idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx);
+ }
+
+ this.parallels_.length = idx;
+
+};
+
+
+/**
+ * @param {number} resolution Resolution.
+ * @return {number} The interval in degrees.
+ * @private
+ */
+ol.Graticule.prototype.getInterval_ = function(resolution) {
+ var centerLon = this.projectionCenterLonLat_[0];
+ var centerLat = this.projectionCenterLonLat_[1];
+ var interval = -1;
+ var i, ii, delta, dist;
+ var target = Math.pow(this.targetSize_ * resolution, 2);
+ /** @type {Array.<number>} **/
+ var p1 = [];
+ /** @type {Array.<number>} **/
+ var p2 = [];
+ for (i = 0, ii = ol.Graticule.intervals_.length; i < ii; ++i) {
+ delta = ol.Graticule.intervals_[i] / 2;
+ p1[0] = centerLon - delta;
+ p1[1] = centerLat - delta;
+ p2[0] = centerLon + delta;
+ p2[1] = centerLat + delta;
+ this.fromLonLatTransform_(p1, p1);
+ this.fromLonLatTransform_(p2, p2);
+ dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2);
+ if (dist <= target) {
+ break;
+ }
+ interval = ol.Graticule.intervals_[i];
+ }
+ return interval;
+};
+
+
+/**
+ * Get the map associated with this graticule.
+ * @return {ol.Map} The map.
+ * @api
+ */
+ol.Graticule.prototype.getMap = function() {
+ return this.map_;
+};
+
+
+/**
+ * @param {number} lon Longitude.
+ * @param {number} minLat Minimal latitude.
+ * @param {number} maxLat Maximal latitude.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {ol.geom.LineString} The meridian line string.
+ * @param {number} index Index.
+ * @private
+ */
+ol.Graticule.prototype.getMeridian_ = function(lon, minLat, maxLat,
+ squaredTolerance, index) {
+ ol.DEBUG && console.assert(lon >= this.minLon_,
+ 'lon should be larger than or equal to this.minLon_');
+ ol.DEBUG && console.assert(lon <= this.maxLon_,
+ 'lon should be smaller than or equal to this.maxLon_');
+ var flatCoordinates = ol.geom.flat.geodesic.meridian(lon,
+ minLat, maxLat, this.projection_, squaredTolerance);
+ ol.DEBUG && console.assert(flatCoordinates.length > 0,
+ 'flatCoordinates cannot be empty');
+ var lineString = this.meridians_[index] !== undefined ?
+ this.meridians_[index] : new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates);
+ return lineString;
+};
+
+
+/**
+ * Get the list of meridians. Meridians are lines of equal longitude.
+ * @return {Array.<ol.geom.LineString>} The meridians.
+ * @api
+ */
+ol.Graticule.prototype.getMeridians = function() {
+ return this.meridians_;
+};
+
+
+/**
+ * @param {number} lat Latitude.
+ * @param {number} minLon Minimal longitude.
+ * @param {number} maxLon Maximal longitude.
+ * @param {number} squaredTolerance Squared tolerance.
+ * @return {ol.geom.LineString} The parallel line string.
+ * @param {number} index Index.
+ * @private
+ */
+ol.Graticule.prototype.getParallel_ = function(lat, minLon, maxLon,
+ squaredTolerance, index) {
+ ol.DEBUG && console.assert(lat >= this.minLat_,
+ 'lat should be larger than or equal to this.minLat_');
+ ol.DEBUG && console.assert(lat <= this.maxLat_,
+ 'lat should be smaller than or equal to this.maxLat_');
+ var flatCoordinates = ol.geom.flat.geodesic.parallel(lat,
+ this.minLon_, this.maxLon_, this.projection_, squaredTolerance);
+ ol.DEBUG && console.assert(flatCoordinates.length > 0,
+ 'flatCoordinates cannot be empty');
+ var lineString = this.parallels_[index] !== undefined ?
+ this.parallels_[index] : new ol.geom.LineString(null);
+ lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates);
+ return lineString;
+};
+
+
+/**
+ * Get the list of parallels. Pallels are lines of equal latitude.
+ * @return {Array.<ol.geom.LineString>} The parallels.
+ * @api
+ */
+ol.Graticule.prototype.getParallels = function() {
+ return this.parallels_;
+};
+
+
+/**
+ * @param {ol.render.Event} e Event.
+ * @private
+ */
+ol.Graticule.prototype.handlePostCompose_ = function(e) {
+ var vectorContext = e.vectorContext;
+ var frameState = e.frameState;
+ var extent = frameState.extent;
+ var viewState = frameState.viewState;
+ var center = viewState.center;
+ var projection = viewState.projection;
+ var resolution = viewState.resolution;
+ var pixelRatio = frameState.pixelRatio;
+ var squaredTolerance =
+ resolution * resolution / (4 * pixelRatio * pixelRatio);
+
+ var updateProjectionInfo = !this.projection_ ||
+ !ol.proj.equivalent(this.projection_, projection);
+
+ if (updateProjectionInfo) {
+ this.updateProjectionInfo_(projection);
+ }
+
+ //Fix the extent if wrapped.
+ //(note: this is the same extent as vectorContext.extent_)
+ var offsetX = 0;
+ if (projection.canWrapX()) {
+ var projectionExtent = projection.getExtent();
+ var worldWidth = ol.extent.getWidth(projectionExtent);
+ var x = frameState.focus[0];
+ if (x < projectionExtent[0] || x > projectionExtent[2]) {
+ var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth);
+ offsetX = worldWidth * worldsAway;
+ extent = [
+ extent[0] + offsetX, extent[1],
+ extent[2] + offsetX, extent[3]
+ ];
+ }
+ }
+
+ this.createGraticule_(extent, center, resolution, squaredTolerance);
+
+ // Draw the lines
+ vectorContext.setFillStrokeStyle(null, this.strokeStyle_);
+ var i, l, line;
+ for (i = 0, l = this.meridians_.length; i < l; ++i) {
+ line = this.meridians_[i];
+ vectorContext.drawLineString(line, null);
+ }
+ for (i = 0, l = this.parallels_.length; i < l; ++i) {
+ line = this.parallels_[i];
+ vectorContext.drawLineString(line, null);
+ }
+};
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @private
+ */
+ol.Graticule.prototype.updateProjectionInfo_ = function(projection) {
+ var epsg4326Projection = ol.proj.get('EPSG:4326');
+
+ var extent = projection.getExtent();
+ var worldExtent = projection.getWorldExtent();
+ var worldExtentP = ol.proj.transformExtent(worldExtent,
+ epsg4326Projection, projection);
+
+ var maxLat = worldExtent[3];
+ var maxLon = worldExtent[2];
+ var minLat = worldExtent[1];
+ var minLon = worldExtent[0];
+
+ var maxLatP = worldExtentP[3];
+ var maxLonP = worldExtentP[2];
+ var minLatP = worldExtentP[1];
+ var minLonP = worldExtentP[0];
+
+ ol.DEBUG && console.assert(maxLat !== undefined, 'maxLat should be defined');
+ ol.DEBUG && console.assert(maxLon !== undefined, 'maxLon should be defined');
+ ol.DEBUG && console.assert(minLat !== undefined, 'minLat should be defined');
+ ol.DEBUG && console.assert(minLon !== undefined, 'minLon should be defined');
+
+ ol.DEBUG && console.assert(maxLatP !== undefined,
+ 'projected maxLat should be defined');
+ ol.DEBUG && console.assert(maxLonP !== undefined,
+ 'projected maxLon should be defined');
+ ol.DEBUG && console.assert(minLatP !== undefined,
+ 'projected minLat should be defined');
+ ol.DEBUG && console.assert(minLonP !== undefined,
+ 'projected minLon should be defined');
+
+ this.maxLat_ = maxLat;
+ this.maxLon_ = maxLon;
+ this.minLat_ = minLat;
+ this.minLon_ = minLon;
+
+ this.maxLatP_ = maxLatP;
+ this.maxLonP_ = maxLonP;
+ this.minLatP_ = minLatP;
+ this.minLonP_ = minLonP;
+
+
+ this.fromLonLatTransform_ = ol.proj.getTransform(
+ epsg4326Projection, projection);
+
+ this.toLonLatTransform_ = ol.proj.getTransform(
+ projection, epsg4326Projection);
+
+ this.projectionCenterLonLat_ = this.toLonLatTransform_(
+ ol.extent.getCenter(extent));
+
+ this.projection_ = projection;
+};
+
+
+/**
+ * Set the map for this graticule. The graticule will be rendered on the
+ * provided map.
+ * @param {ol.Map} map Map.
+ * @api
+ */
+ol.Graticule.prototype.setMap = function(map) {
+ if (this.map_) {
+ this.map_.un(ol.render.Event.Type.POSTCOMPOSE,
+ this.handlePostCompose_, this);
+ this.map_.render();
+ }
+ if (map) {
+ map.on(ol.render.Event.Type.POSTCOMPOSE,
+ this.handlePostCompose_, this);
+ map.render();
+ }
+ this.map_ = map;
+};
+
+goog.provide('ol.ImageTile');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+
+
+/**
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Tile.State} state State.
+ * @param {string} src Image source URI.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
+ */
+ol.ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction) {
+
+ ol.Tile.call(this, tileCoord, state);
+
+ /**
+ * Image URI
+ *
+ * @private
+ * @type {string}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {Image}
+ */
+ this.image_ = new Image();
+ if (crossOrigin !== null) {
+ this.image_.crossOrigin = crossOrigin;
+ }
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.imageListenerKeys_ = null;
+
+ /**
+ * @private
+ * @type {ol.TileLoadFunctionType}
+ */
+ this.tileLoadFunction_ = tileLoadFunction;
+
+};
+ol.inherits(ol.ImageTile, ol.Tile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.ImageTile.prototype.disposeInternal = function() {
+ if (this.state == ol.Tile.State.LOADING) {
+ this.unlistenImage_();
+ }
+ if (this.interimTile) {
+ this.interimTile.dispose();
+ }
+ this.state = ol.Tile.State.ABORT;
+ this.changed();
+ ol.Tile.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * Get the image element for this tile.
+ * @inheritDoc
+ * @api
+ */
+ol.ImageTile.prototype.getImage = function() {
+ return this.image_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.ImageTile.prototype.getKey = function() {
+ return this.src_;
+};
+
+
+/**
+ * Tracks loading or read errors.
+ *
+ * @private
+ */
+ol.ImageTile.prototype.handleImageError_ = function() {
+ this.state = ol.Tile.State.ERROR;
+ this.unlistenImage_();
+ this.changed();
+};
+
+
+/**
+ * Tracks successful image load.
+ *
+ * @private
+ */
+ol.ImageTile.prototype.handleImageLoad_ = function() {
+ if (this.image_.naturalWidth && this.image_.naturalHeight) {
+ this.state = ol.Tile.State.LOADED;
+ } else {
+ this.state = ol.Tile.State.EMPTY;
+ }
+ this.unlistenImage_();
+ this.changed();
+};
+
+
+/**
+ * Load the image or retry if loading previously failed.
+ * Loading is taken care of by the tile queue, and calling this method is
+ * only needed for preloading or for reloading in case of an error.
+ * @api
+ */
+ol.ImageTile.prototype.load = function() {
+ if (this.state == ol.Tile.State.IDLE || this.state == ol.Tile.State.ERROR) {
+ this.state = ol.Tile.State.LOADING;
+ this.changed();
+ ol.DEBUG && console.assert(!this.imageListenerKeys_,
+ 'this.imageListenerKeys_ should be null');
+ this.imageListenerKeys_ = [
+ ol.events.listenOnce(this.image_, ol.events.EventType.ERROR,
+ this.handleImageError_, this),
+ ol.events.listenOnce(this.image_, ol.events.EventType.LOAD,
+ this.handleImageLoad_, this)
+ ];
+ this.tileLoadFunction_(this, this.src_);
+ }
+};
+
+
+/**
+ * Discards event handlers which listen for load completion or errors.
+ *
+ * @private
+ */
+ol.ImageTile.prototype.unlistenImage_ = function() {
+ this.imageListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.imageListenerKeys_ = null;
+};
+
+// FIXME should handle all geo-referenced data, not just vector data
+
+goog.provide('ol.interaction.DragAndDrop');
+
+goog.require('ol');
+goog.require('ol.functions');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.EventType');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.proj');
+
+
+/**
+ * @classdesc
+ * Handles input of vector data by drag and drop.
+ *
+ * @constructor
+ * @extends {ol.interaction.Interaction}
+ * @fires ol.interaction.DragAndDrop.Event
+ * @param {olx.interaction.DragAndDropOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DragAndDrop = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: ol.interaction.DragAndDrop.handleEvent
+ });
+
+ /**
+ * @private
+ * @type {Array.<function(new: ol.format.Feature)>}
+ */
+ this.formatConstructors_ = options.formatConstructors ?
+ options.formatConstructors : [];
+
+ /**
+ * @private
+ * @type {ol.proj.Projection}
+ */
+ this.projection_ = options.projection ?
+ ol.proj.get(options.projection) : null;
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.dropListenKeys_ = null;
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.target = options.target ? options.target : null;
+
+};
+ol.inherits(ol.interaction.DragAndDrop, ol.interaction.Interaction);
+
+
+/**
+ * @param {Event} event Event.
+ * @this {ol.interaction.DragAndDrop}
+ * @private
+ */
+ol.interaction.DragAndDrop.handleDrop_ = function(event) {
+ var files = event.dataTransfer.files;
+ var i, ii, file;
+ for (i = 0, ii = files.length; i < ii; ++i) {
+ file = files.item(i);
+ var reader = new FileReader();
+ reader.addEventListener(ol.events.EventType.LOAD,
+ this.handleResult_.bind(this, file));
+ reader.readAsText(file);
+ }
+};
+
+
+/**
+ * @param {Event} event Event.
+ * @private
+ */
+ol.interaction.DragAndDrop.handleStop_ = function(event) {
+ event.stopPropagation();
+ event.preventDefault();
+ event.dataTransfer.dropEffect = 'copy';
+};
+
+
+/**
+ * @param {File} file File.
+ * @param {Event} event Load event.
+ * @private
+ */
+ol.interaction.DragAndDrop.prototype.handleResult_ = function(file, event) {
+ var result = event.target.result;
+ var map = this.getMap();
+ var projection = this.projection_;
+ if (!projection) {
+ var view = map.getView();
+ projection = view.getProjection();
+ ol.DEBUG && console.assert(projection !== undefined,
+ 'projection should be defined');
+ }
+ var formatConstructors = this.formatConstructors_;
+ var features = [];
+ var i, ii;
+ for (i = 0, ii = formatConstructors.length; i < ii; ++i) {
+ var formatConstructor = formatConstructors[i];
+ var format = new formatConstructor();
+ features = this.tryReadFeatures_(format, result, {
+ featureProjection: projection
+ });
+ if (features && features.length > 0) {
+ break;
+ }
+ }
+ this.dispatchEvent(
+ new ol.interaction.DragAndDrop.Event(
+ ol.interaction.DragAndDrop.EventType.ADD_FEATURES, file,
+ features, projection));
+};
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} unconditionally and
+ * neither prevents the browser default nor stops event propagation.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.DragAndDrop}
+ * @api
+ */
+ol.interaction.DragAndDrop.handleEvent = ol.functions.TRUE;
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.DragAndDrop.prototype.setMap = function(map) {
+ if (this.dropListenKeys_) {
+ this.dropListenKeys_.forEach(ol.events.unlistenByKey);
+ this.dropListenKeys_ = null;
+ }
+ ol.interaction.Interaction.prototype.setMap.call(this, map);
+ if (map) {
+ var dropArea = this.target ? this.target : map.getViewport();
+ this.dropListenKeys_ = [
+ ol.events.listen(dropArea, ol.events.EventType.DROP,
+ ol.interaction.DragAndDrop.handleDrop_, this),
+ ol.events.listen(dropArea, ol.events.EventType.DRAGENTER,
+ ol.interaction.DragAndDrop.handleStop_, this),
+ ol.events.listen(dropArea, ol.events.EventType.DRAGOVER,
+ ol.interaction.DragAndDrop.handleStop_, this),
+ ol.events.listen(dropArea, ol.events.EventType.DROP,
+ ol.interaction.DragAndDrop.handleStop_, this)
+ ];
+ }
+};
+
+
+/**
+ * @param {ol.format.Feature} format Format.
+ * @param {string} text Text.
+ * @param {olx.format.ReadOptions} options Read options.
+ * @private
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.interaction.DragAndDrop.prototype.tryReadFeatures_ = function(format, text, options) {
+ try {
+ return format.readFeatures(text, options);
+ } catch (e) {
+ return null;
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.DragAndDrop.EventType = {
+ /**
+ * Triggered when features are added
+ * @event ol.interaction.DragAndDrop.Event#addfeatures
+ * @api stable
+ */
+ ADD_FEATURES: 'addfeatures'
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.DragAndDrop} instances are instances
+ * of this type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.interaction.DragAndDropEvent}
+ * @param {ol.interaction.DragAndDrop.EventType} type Type.
+ * @param {File} file File.
+ * @param {Array.<ol.Feature>=} opt_features Features.
+ * @param {ol.proj.Projection=} opt_projection Projection.
+ */
+ol.interaction.DragAndDrop.Event = function(type, file, opt_features, opt_projection) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The features parsed from dropped data.
+ * @type {Array.<ol.Feature>|undefined}
+ * @api stable
+ */
+ this.features = opt_features;
+
+ /**
+ * The dropped file.
+ * @type {File}
+ * @api stable
+ */
+ this.file = file;
+
+ /**
+ * The feature projection.
+ * @type {ol.proj.Projection|undefined}
+ * @api
+ */
+ this.projection = opt_projection;
+
+};
+ol.inherits(ol.interaction.DragAndDrop.Event, ol.events.Event);
+
+goog.provide('ol.interaction.DragRotateAndZoom');
+
+goog.require('ol');
+goog.require('ol.View');
+goog.require('ol.events.condition');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.interaction.Pointer');
+
+
+/**
+ * @classdesc
+ * Allows the user to zoom and rotate the map by clicking and dragging
+ * on the map. By default, this interaction is limited to when the shift
+ * key is held down.
+ *
+ * This interaction is only supported for mouse devices.
+ *
+ * And this interaction is not included in the default interactions.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.DragRotateAndZoomOptions=} opt_options Options.
+ * @api stable
+ */
+ol.interaction.DragRotateAndZoom = function(opt_options) {
+
+ var options = opt_options ? opt_options : {};
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.DragRotateAndZoom.handleDownEvent_,
+ handleDragEvent: ol.interaction.DragRotateAndZoom.handleDragEvent_,
+ handleUpEvent: ol.interaction.DragRotateAndZoom.handleUpEvent_
+ });
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.shiftKeyOnly;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.lastAngle_ = undefined;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.lastMagnitude_ = undefined;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.lastScaleDelta_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.duration_ = options.duration !== undefined ? options.duration : 400;
+
+};
+ol.inherits(ol.interaction.DragRotateAndZoom, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @this {ol.interaction.DragRotateAndZoom}
+ * @private
+ */
+ol.interaction.DragRotateAndZoom.handleDragEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return;
+ }
+
+ var map = mapBrowserEvent.map;
+ var size = map.getSize();
+ var offset = mapBrowserEvent.pixel;
+ var deltaX = offset[0] - size[0] / 2;
+ var deltaY = size[1] / 2 - offset[1];
+ var theta = Math.atan2(deltaY, deltaX);
+ var magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ var view = map.getView();
+ if (this.lastAngle_ !== undefined) {
+ var angleDelta = theta - this.lastAngle_;
+ ol.interaction.Interaction.rotateWithoutConstraints(
+ map, view, view.getRotation() - angleDelta);
+ }
+ this.lastAngle_ = theta;
+ if (this.lastMagnitude_ !== undefined) {
+ var resolution = this.lastMagnitude_ * (view.getResolution() / magnitude);
+ ol.interaction.Interaction.zoomWithoutConstraints(map, view, resolution);
+ }
+ if (this.lastMagnitude_ !== undefined) {
+ this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
+ }
+ this.lastMagnitude_ = magnitude;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.DragRotateAndZoom}
+ * @private
+ */
+ol.interaction.DragRotateAndZoom.handleUpEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return true;
+ }
+
+ var map = mapBrowserEvent.map;
+ var view = map.getView();
+ view.setHint(ol.View.Hint.INTERACTING, -1);
+ var direction = this.lastScaleDelta_ - 1;
+ ol.interaction.Interaction.rotate(map, view, view.getRotation());
+ ol.interaction.Interaction.zoom(map, view, view.getResolution(),
+ undefined, this.duration_, direction);
+ this.lastScaleDelta_ = 0;
+ return false;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.DragRotateAndZoom}
+ * @private
+ */
+ol.interaction.DragRotateAndZoom.handleDownEvent_ = function(mapBrowserEvent) {
+ if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
+ return false;
+ }
+
+ if (this.condition_(mapBrowserEvent)) {
+ mapBrowserEvent.map.getView().setHint(ol.View.Hint.INTERACTING, 1);
+ this.lastAngle_ = undefined;
+ this.lastMagnitude_ = undefined;
+ return true;
+ } else {
+ return false;
+ }
+};
+
+goog.provide('ol.loadingstrategy');
+
+
+/**
+ * Strategy function for loading all features with a single request.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {Array.<ol.Extent>} Extents.
+ * @api
+ */
+ol.loadingstrategy.all = function(extent, resolution) {
+ return [[-Infinity, -Infinity, Infinity, Infinity]];
+};
+
+
+/**
+ * Strategy function for loading features based on the view's extent and
+ * resolution.
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {Array.<ol.Extent>} Extents.
+ * @api
+ */
+ol.loadingstrategy.bbox = function(extent, resolution) {
+ return [extent];
+};
+
+
+/**
+ * Creates a strategy function for loading features based on a tile grid.
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @return {function(ol.Extent, number): Array.<ol.Extent>} Loading strategy.
+ * @api
+ */
+ol.loadingstrategy.tile = function(tileGrid) {
+ return (
+ /**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {Array.<ol.Extent>} Extents.
+ */
+ function(extent, resolution) {
+ var z = tileGrid.getZForResolution(resolution);
+ var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
+ /** @type {Array.<ol.Extent>} */
+ var extents = [];
+ /** @type {ol.TileCoord} */
+ var tileCoord = [z, 0, 0];
+ for (tileCoord[1] = tileRange.minX; tileCoord[1] <= tileRange.maxX;
+ ++tileCoord[1]) {
+ for (tileCoord[2] = tileRange.minY; tileCoord[2] <= tileRange.maxY;
+ ++tileCoord[2]) {
+ extents.push(tileGrid.getTileCoordExtent(tileCoord));
+ }
+ }
+ return extents;
+ });
+};
+
+// FIXME bulk feature upload - suppress events
+// FIXME make change-detection more refined (notably, geometry hint)
+
+goog.provide('ol.source.Vector');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.Object');
+goog.require('ol.array');
+goog.require('ol.asserts');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.featureloader');
+goog.require('ol.functions');
+goog.require('ol.loadingstrategy');
+goog.require('ol.obj');
+goog.require('ol.source.Source');
+goog.require('ol.source.State');
+goog.require('ol.structs.RBush');
+
+
+/**
+ * @classdesc
+ * Provides a source of features for vector layers. Vector features provided
+ * by this source are suitable for editing. See {@link ol.source.VectorTile} for
+ * vector data that is optimized for rendering.
+ *
+ * @constructor
+ * @extends {ol.source.Source}
+ * @fires ol.source.Vector.Event
+ * @param {olx.source.VectorOptions=} opt_options Vector source options.
+ * @api stable
+ */
+ol.source.Vector = function(opt_options) {
+
+ var options = opt_options || {};
+
+ ol.source.Source.call(this, {
+ attributions: options.attributions,
+ logo: options.logo,
+ projection: undefined,
+ state: ol.source.State.READY,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+ /**
+ * @private
+ * @type {ol.FeatureLoader}
+ */
+ this.loader_ = ol.nullFunction;
+
+ /**
+ * @private
+ * @type {ol.format.Feature|undefined}
+ */
+ this.format_ = options.format;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.overlaps_ = options.overlaps == undefined ? true : options.overlaps;
+
+ /**
+ * @private
+ * @type {string|ol.FeatureUrlFunction|undefined}
+ */
+ this.url_ = options.url;
+
+ if (options.loader !== undefined) {
+ this.loader_ = options.loader;
+ } else if (this.url_ !== undefined) {
+ ol.asserts.assert(this.format_, 7); // `format` must be set when `url` is set
+ // create a XHR feature loader for "url" and "format"
+ this.loader_ = ol.featureloader.xhr(this.url_, /** @type {ol.format.Feature} */ (this.format_));
+ }
+
+ /**
+ * @private
+ * @type {ol.LoadingStrategy}
+ */
+ this.strategy_ = options.strategy !== undefined ? options.strategy :
+ ol.loadingstrategy.all;
+
+ var useSpatialIndex =
+ options.useSpatialIndex !== undefined ? options.useSpatialIndex : true;
+
+ /**
+ * @private
+ * @type {ol.structs.RBush.<ol.Feature>}
+ */
+ this.featuresRtree_ = useSpatialIndex ? new ol.structs.RBush() : null;
+
+ /**
+ * @private
+ * @type {ol.structs.RBush.<{extent: ol.Extent}>}
+ */
+ this.loadedExtentsRtree_ = new ol.structs.RBush();
+
+ /**
+ * @private
+ * @type {Object.<string, ol.Feature>}
+ */
+ this.nullGeometryFeatures_ = {};
+
+ /**
+ * A lookup of features by id (the return from feature.getId()).
+ * @private
+ * @type {Object.<string, ol.Feature>}
+ */
+ this.idIndex_ = {};
+
+ /**
+ * A lookup of features without id (keyed by ol.getUid(feature)).
+ * @private
+ * @type {Object.<string, ol.Feature>}
+ */
+ this.undefIdIndex_ = {};
+
+ /**
+ * @private
+ * @type {Object.<string, Array.<ol.EventsKey>>}
+ */
+ this.featureChangeKeys_ = {};
+
+ /**
+ * @private
+ * @type {ol.Collection.<ol.Feature>}
+ */
+ this.featuresCollection_ = null;
+
+ var collection, features;
+ if (options.features instanceof ol.Collection) {
+ collection = options.features;
+ features = collection.getArray();
+ } else if (Array.isArray(options.features)) {
+ features = options.features;
+ }
+ if (!useSpatialIndex && collection === undefined) {
+ collection = new ol.Collection(features);
+ }
+ if (features !== undefined) {
+ this.addFeaturesInternal(features);
+ }
+ if (collection !== undefined) {
+ this.bindFeaturesCollection_(collection);
+ }
+
+};
+ol.inherits(ol.source.Vector, ol.source.Source);
+
+
+/**
+ * Add a single feature to the source. If you want to add a batch of features
+ * at once, call {@link ol.source.Vector#addFeatures source.addFeatures()}
+ * instead.
+ * @param {ol.Feature} feature Feature to add.
+ * @api stable
+ */
+ol.source.Vector.prototype.addFeature = function(feature) {
+ this.addFeatureInternal(feature);
+ this.changed();
+};
+
+
+/**
+ * Add a feature without firing a `change` event.
+ * @param {ol.Feature} feature Feature.
+ * @protected
+ */
+ol.source.Vector.prototype.addFeatureInternal = function(feature) {
+ var featureKey = ol.getUid(feature).toString();
+
+ if (!this.addToIndex_(featureKey, feature)) {
+ return;
+ }
+
+ this.setupChangeEvents_(featureKey, feature);
+
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ var extent = geometry.getExtent();
+ if (this.featuresRtree_) {
+ this.featuresRtree_.insert(extent, feature);
+ }
+ } else {
+ this.nullGeometryFeatures_[featureKey] = feature;
+ }
+
+ this.dispatchEvent(
+ new ol.source.Vector.Event(ol.source.Vector.EventType.ADDFEATURE, feature));
+};
+
+
+/**
+ * @param {string} featureKey Unique identifier for the feature.
+ * @param {ol.Feature} feature The feature.
+ * @private
+ */
+ol.source.Vector.prototype.setupChangeEvents_ = function(featureKey, feature) {
+ ol.DEBUG && console.assert(!(featureKey in this.featureChangeKeys_),
+ 'key (%s) not yet registered in featureChangeKey', featureKey);
+ this.featureChangeKeys_[featureKey] = [
+ ol.events.listen(feature, ol.events.EventType.CHANGE,
+ this.handleFeatureChange_, this),
+ ol.events.listen(feature, ol.Object.EventType.PROPERTYCHANGE,
+ this.handleFeatureChange_, this)
+ ];
+};
+
+
+/**
+ * @param {string} featureKey Unique identifier for the feature.
+ * @param {ol.Feature} feature The feature.
+ * @return {boolean} The feature is "valid", in the sense that it is also a
+ * candidate for insertion into the Rtree.
+ * @private
+ */
+ol.source.Vector.prototype.addToIndex_ = function(featureKey, feature) {
+ var valid = true;
+ var id = feature.getId();
+ if (id !== undefined) {
+ if (!(id.toString() in this.idIndex_)) {
+ this.idIndex_[id.toString()] = feature;
+ } else {
+ valid = false;
+ }
+ } else {
+ ol.asserts.assert(!(featureKey in this.undefIdIndex_),
+ 30); // The passed `feature` was already added to the source
+ this.undefIdIndex_[featureKey] = feature;
+ }
+ return valid;
+};
+
+
+/**
+ * Add a batch of features to the source.
+ * @param {Array.<ol.Feature>} features Features to add.
+ * @api stable
+ */
+ol.source.Vector.prototype.addFeatures = function(features) {
+ this.addFeaturesInternal(features);
+ this.changed();
+};
+
+
+/**
+ * Add features without firing a `change` event.
+ * @param {Array.<ol.Feature>} features Features.
+ * @protected
+ */
+ol.source.Vector.prototype.addFeaturesInternal = function(features) {
+ var featureKey, i, length, feature;
+
+ var extents = [];
+ var newFeatures = [];
+ var geometryFeatures = [];
+
+ for (i = 0, length = features.length; i < length; i++) {
+ feature = features[i];
+ featureKey = ol.getUid(feature).toString();
+ if (this.addToIndex_(featureKey, feature)) {
+ newFeatures.push(feature);
+ }
+ }
+
+ for (i = 0, length = newFeatures.length; i < length; i++) {
+ feature = newFeatures[i];
+ featureKey = ol.getUid(feature).toString();
+ this.setupChangeEvents_(featureKey, feature);
+
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ var extent = geometry.getExtent();
+ extents.push(extent);
+ geometryFeatures.push(feature);
+ } else {
+ this.nullGeometryFeatures_[featureKey] = feature;
+ }
+ }
+ if (this.featuresRtree_) {
+ this.featuresRtree_.load(extents, geometryFeatures);
+ }
+
+ for (i = 0, length = newFeatures.length; i < length; i++) {
+ this.dispatchEvent(new ol.source.Vector.Event(
+ ol.source.Vector.EventType.ADDFEATURE, newFeatures[i]));
+ }
+};
+
+
+/**
+ * @param {!ol.Collection.<ol.Feature>} collection Collection.
+ * @private
+ */
+ol.source.Vector.prototype.bindFeaturesCollection_ = function(collection) {
+ ol.DEBUG && console.assert(!this.featuresCollection_,
+ 'bindFeaturesCollection can only be called once');
+ var modifyingCollection = false;
+ ol.events.listen(this, ol.source.Vector.EventType.ADDFEATURE,
+ function(evt) {
+ if (!modifyingCollection) {
+ modifyingCollection = true;
+ collection.push(evt.feature);
+ modifyingCollection = false;
+ }
+ });
+ ol.events.listen(this, ol.source.Vector.EventType.REMOVEFEATURE,
+ function(evt) {
+ if (!modifyingCollection) {
+ modifyingCollection = true;
+ collection.remove(evt.feature);
+ modifyingCollection = false;
+ }
+ });
+ ol.events.listen(collection, ol.Collection.EventType.ADD,
+ function(evt) {
+ if (!modifyingCollection) {
+ modifyingCollection = true;
+ this.addFeature(/** @type {ol.Feature} */ (evt.element));
+ modifyingCollection = false;
+ }
+ }, this);
+ ol.events.listen(collection, ol.Collection.EventType.REMOVE,
+ function(evt) {
+ if (!modifyingCollection) {
+ modifyingCollection = true;
+ this.removeFeature(/** @type {ol.Feature} */ (evt.element));
+ modifyingCollection = false;
+ }
+ }, this);
+ this.featuresCollection_ = collection;
+};
+
+
+/**
+ * Remove all features from the source.
+ * @param {boolean=} opt_fast Skip dispatching of {@link removefeature} events.
+ * @api stable
+ */
+ol.source.Vector.prototype.clear = function(opt_fast) {
+ if (opt_fast) {
+ for (var featureId in this.featureChangeKeys_) {
+ var keys = this.featureChangeKeys_[featureId];
+ keys.forEach(ol.events.unlistenByKey);
+ }
+ if (!this.featuresCollection_) {
+ this.featureChangeKeys_ = {};
+ this.idIndex_ = {};
+ this.undefIdIndex_ = {};
+ }
+ } else {
+ if (this.featuresRtree_) {
+ this.featuresRtree_.forEach(this.removeFeatureInternal, this);
+ for (var id in this.nullGeometryFeatures_) {
+ this.removeFeatureInternal(this.nullGeometryFeatures_[id]);
+ }
+ }
+ }
+ if (this.featuresCollection_) {
+ this.featuresCollection_.clear();
+ }
+ ol.DEBUG && console.assert(ol.obj.isEmpty(this.featureChangeKeys_),
+ 'featureChangeKeys is an empty object now');
+ ol.DEBUG && console.assert(ol.obj.isEmpty(this.idIndex_),
+ 'idIndex is an empty object now');
+ ol.DEBUG && console.assert(ol.obj.isEmpty(this.undefIdIndex_),
+ 'undefIdIndex is an empty object now');
+
+ if (this.featuresRtree_) {
+ this.featuresRtree_.clear();
+ }
+ this.loadedExtentsRtree_.clear();
+ this.nullGeometryFeatures_ = {};
+
+ var clearEvent = new ol.source.Vector.Event(ol.source.Vector.EventType.CLEAR);
+ this.dispatchEvent(clearEvent);
+ this.changed();
+};
+
+
+/**
+ * Iterate through all features on the source, calling the provided callback
+ * with each one. If the callback returns any "truthy" value, iteration will
+ * stop and the function will return the same value.
+ *
+ * @param {function(this: T, ol.Feature): S} callback Called with each feature
+ * on the source. Return a truthy value to stop iteration.
+ * @param {T=} opt_this The object to use as `this` in the callback.
+ * @return {S|undefined} The return value from the last call to the callback.
+ * @template T,S
+ * @api stable
+ */
+ol.source.Vector.prototype.forEachFeature = function(callback, opt_this) {
+ if (this.featuresRtree_) {
+ return this.featuresRtree_.forEach(callback, opt_this);
+ } else if (this.featuresCollection_) {
+ return this.featuresCollection_.forEach(callback, opt_this);
+ }
+};
+
+
+/**
+ * Iterate through all features whose geometries contain the provided
+ * coordinate, calling the callback with each feature. If the callback returns
+ * a "truthy" value, iteration will stop and the function will return the same
+ * value.
+ *
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {function(this: T, ol.Feature): S} callback Called with each feature
+ * whose goemetry contains the provided coordinate.
+ * @param {T=} opt_this The object to use as `this` in the callback.
+ * @return {S|undefined} The return value from the last call to the callback.
+ * @template T,S
+ */
+ol.source.Vector.prototype.forEachFeatureAtCoordinateDirect = function(coordinate, callback, opt_this) {
+ var extent = [coordinate[0], coordinate[1], coordinate[0], coordinate[1]];
+ return this.forEachFeatureInExtent(extent, function(feature) {
+ var geometry = feature.getGeometry();
+ ol.DEBUG && console.assert(geometry, 'feature geometry is defined and not null');
+ if (geometry.intersectsCoordinate(coordinate)) {
+ return callback.call(opt_this, feature);
+ } else {
+ return undefined;
+ }
+ });
+};
+
+
+/**
+ * Iterate through all features whose bounding box intersects the provided
+ * extent (note that the feature's geometry may not intersect the extent),
+ * calling the callback with each feature. If the callback returns a "truthy"
+ * value, iteration will stop and the function will return the same value.
+ *
+ * If you are interested in features whose geometry intersects an extent, call
+ * the {@link ol.source.Vector#forEachFeatureIntersectingExtent
+ * source.forEachFeatureIntersectingExtent()} method instead.
+ *
+ * When `useSpatialIndex` is set to false, this method will loop through all
+ * features, equivalent to {@link ol.source.Vector#forEachFeature}.
+ *
+ * @param {ol.Extent} extent Extent.
+ * @param {function(this: T, ol.Feature): S} callback Called with each feature
+ * whose bounding box intersects the provided extent.
+ * @param {T=} opt_this The object to use as `this` in the callback.
+ * @return {S|undefined} The return value from the last call to the callback.
+ * @template T,S
+ * @api
+ */
+ol.source.Vector.prototype.forEachFeatureInExtent = function(extent, callback, opt_this) {
+ if (this.featuresRtree_) {
+ return this.featuresRtree_.forEachInExtent(extent, callback, opt_this);
+ } else if (this.featuresCollection_) {
+ return this.featuresCollection_.forEach(callback, opt_this);
+ }
+};
+
+
+/**
+ * Iterate through all features whose geometry intersects the provided extent,
+ * calling the callback with each feature. If the callback returns a "truthy"
+ * value, iteration will stop and the function will return the same value.
+ *
+ * If you only want to test for bounding box intersection, call the
+ * {@link ol.source.Vector#forEachFeatureInExtent
+ * source.forEachFeatureInExtent()} method instead.
+ *
+ * @param {ol.Extent} extent Extent.
+ * @param {function(this: T, ol.Feature): S} callback Called with each feature
+ * whose geometry intersects the provided extent.
+ * @param {T=} opt_this The object to use as `this` in the callback.
+ * @return {S|undefined} The return value from the last call to the callback.
+ * @template T,S
+ * @api
+ */
+ol.source.Vector.prototype.forEachFeatureIntersectingExtent = function(extent, callback, opt_this) {
+ return this.forEachFeatureInExtent(extent,
+ /**
+ * @param {ol.Feature} feature Feature.
+ * @return {S|undefined} The return value from the last call to the callback.
+ * @template S
+ */
+ function(feature) {
+ var geometry = feature.getGeometry();
+ ol.DEBUG && console.assert(geometry,
+ 'feature geometry is defined and not null');
+ if (geometry.intersectsExtent(extent)) {
+ var result = callback.call(opt_this, feature);
+ if (result) {
+ return result;
+ }
+ }
+ });
+};
+
+
+/**
+ * Get the features collection associated with this source. Will be `null`
+ * unless the source was configured with `useSpatialIndex` set to `false`, or
+ * with an {@link ol.Collection} as `features`.
+ * @return {ol.Collection.<ol.Feature>} The collection of features.
+ * @api
+ */
+ol.source.Vector.prototype.getFeaturesCollection = function() {
+ return this.featuresCollection_;
+};
+
+
+/**
+ * Get all features on the source in random order.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.source.Vector.prototype.getFeatures = function() {
+ var features;
+ if (this.featuresCollection_) {
+ features = this.featuresCollection_.getArray();
+ } else if (this.featuresRtree_) {
+ features = this.featuresRtree_.getAll();
+ if (!ol.obj.isEmpty(this.nullGeometryFeatures_)) {
+ ol.array.extend(
+ features, ol.obj.getValues(this.nullGeometryFeatures_));
+ }
+ }
+ return /** @type {Array.<ol.Feature>} */ (features);
+};
+
+
+/**
+ * Get all features whose geometry intersects the provided coordinate.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {Array.<ol.Feature>} Features.
+ * @api stable
+ */
+ol.source.Vector.prototype.getFeaturesAtCoordinate = function(coordinate) {
+ var features = [];
+ this.forEachFeatureAtCoordinateDirect(coordinate, function(feature) {
+ features.push(feature);
+ });
+ return features;
+};
+
+
+/**
+ * Get all features in the provided extent. Note that this returns an array of
+ * all features intersecting the given extent in random order (so it may include
+ * features whose geometries do not intersect the extent).
+ *
+ * This method is not available when the source is configured with
+ * `useSpatialIndex` set to `false`.
+ * @param {ol.Extent} extent Extent.
+ * @return {Array.<ol.Feature>} Features.
+ * @api
+ */
+ol.source.Vector.prototype.getFeaturesInExtent = function(extent) {
+ ol.DEBUG && console.assert(this.featuresRtree_,
+ 'getFeaturesInExtent does not work when useSpatialIndex is set to false');
+ return this.featuresRtree_.getInExtent(extent);
+};
+
+
+/**
+ * Get the closest feature to the provided coordinate.
+ *
+ * This method is not available when the source is configured with
+ * `useSpatialIndex` set to `false`.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {function(ol.Feature):boolean=} opt_filter Feature filter function.
+ * The filter function will receive one argument, the {@link ol.Feature feature}
+ * and it should return a boolean value. By default, no filtering is made.
+ * @return {ol.Feature} Closest feature.
+ * @api stable
+ */
+ol.source.Vector.prototype.getClosestFeatureToCoordinate = function(coordinate, opt_filter) {
+ // Find the closest feature using branch and bound. We start searching an
+ // infinite extent, and find the distance from the first feature found. This
+ // becomes the closest feature. We then compute a smaller extent which any
+ // closer feature must intersect. We continue searching with this smaller
+ // extent, trying to find a closer feature. Every time we find a closer
+ // feature, we update the extent being searched so that any even closer
+ // feature must intersect it. We continue until we run out of features.
+ var x = coordinate[0];
+ var y = coordinate[1];
+ var closestFeature = null;
+ var closestPoint = [NaN, NaN];
+ var minSquaredDistance = Infinity;
+ var extent = [-Infinity, -Infinity, Infinity, Infinity];
+ ol.DEBUG && console.assert(this.featuresRtree_,
+ 'getClosestFeatureToCoordinate does not work with useSpatialIndex set ' +
+ 'to false');
+ var filter = opt_filter ? opt_filter : ol.functions.TRUE;
+ this.featuresRtree_.forEachInExtent(extent,
+ /**
+ * @param {ol.Feature} feature Feature.
+ */
+ function(feature) {
+ if (filter(feature)) {
+ var geometry = feature.getGeometry();
+ ol.DEBUG && console.assert(geometry,
+ 'feature geometry is defined and not null');
+ var previousMinSquaredDistance = minSquaredDistance;
+ minSquaredDistance = geometry.closestPointXY(
+ x, y, closestPoint, minSquaredDistance);
+ if (minSquaredDistance < previousMinSquaredDistance) {
+ closestFeature = feature;
+ // This is sneaky. Reduce the extent that it is currently being
+ // searched while the R-Tree traversal using this same extent object
+ // is still in progress. This is safe because the new extent is
+ // strictly contained by the old extent.
+ var minDistance = Math.sqrt(minSquaredDistance);
+ extent[0] = x - minDistance;
+ extent[1] = y - minDistance;
+ extent[2] = x + minDistance;
+ extent[3] = y + minDistance;
+ }
+ }
+ });
+ return closestFeature;
+};
+
+
+/**
+ * Get the extent of the features currently in the source.
+ *
+ * This method is not available when the source is configured with
+ * `useSpatialIndex` set to `false`.
+ * @return {!ol.Extent} Extent.
+ * @api stable
+ */
+ol.source.Vector.prototype.getExtent = function() {
+ ol.DEBUG && console.assert(this.featuresRtree_,
+ 'getExtent does not work when useSpatialIndex is set to false');
+ return this.featuresRtree_.getExtent();
+};
+
+
+/**
+ * Get a feature by its identifier (the value returned by feature.getId()).
+ * Note that the index treats string and numeric identifiers as the same. So
+ * `source.getFeatureById(2)` will return a feature with id `'2'` or `2`.
+ *
+ * @param {string|number} id Feature identifier.
+ * @return {ol.Feature} The feature (or `null` if not found).
+ * @api stable
+ */
+ol.source.Vector.prototype.getFeatureById = function(id) {
+ var feature = this.idIndex_[id.toString()];
+ return feature !== undefined ? feature : null;
+};
+
+
+/**
+ * Get the format associated with this source.
+ *
+ * @return {ol.format.Feature|undefined} The feature format.
+ * @api
+ */
+ol.source.Vector.prototype.getFormat = function() {
+ return this.format_;
+};
+
+
+/**
+ * @return {boolean} The source can have overlapping geometries.
+ */
+ol.source.Vector.prototype.getOverlaps = function() {
+ return this.overlaps_;
+};
+
+
+/**
+ * Get the url associated with this source.
+ *
+ * @return {string|ol.FeatureUrlFunction|undefined} The url.
+ * @api
+ */
+ol.source.Vector.prototype.getUrl = function() {
+ return this.url_;
+};
+
+
+/**
+ * @param {ol.events.Event} event Event.
+ * @private
+ */
+ol.source.Vector.prototype.handleFeatureChange_ = function(event) {
+ var feature = /** @type {ol.Feature} */ (event.target);
+ var featureKey = ol.getUid(feature).toString();
+ var geometry = feature.getGeometry();
+ if (!geometry) {
+ if (!(featureKey in this.nullGeometryFeatures_)) {
+ if (this.featuresRtree_) {
+ this.featuresRtree_.remove(feature);
+ }
+ this.nullGeometryFeatures_[featureKey] = feature;
+ }
+ } else {
+ var extent = geometry.getExtent();
+ if (featureKey in this.nullGeometryFeatures_) {
+ delete this.nullGeometryFeatures_[featureKey];
+ if (this.featuresRtree_) {
+ this.featuresRtree_.insert(extent, feature);
+ }
+ } else {
+ if (this.featuresRtree_) {
+ this.featuresRtree_.update(extent, feature);
+ }
+ }
+ }
+ var id = feature.getId();
+ var removed;
+ if (id !== undefined) {
+ var sid = id.toString();
+ if (featureKey in this.undefIdIndex_) {
+ delete this.undefIdIndex_[featureKey];
+ this.idIndex_[sid] = feature;
+ } else {
+ if (this.idIndex_[sid] !== feature) {
+ removed = this.removeFromIdIndex_(feature);
+ ol.DEBUG && console.assert(removed,
+ 'Expected feature to be removed from index');
+ this.idIndex_[sid] = feature;
+ }
+ }
+ } else {
+ if (!(featureKey in this.undefIdIndex_)) {
+ removed = this.removeFromIdIndex_(feature);
+ ol.DEBUG && console.assert(removed,
+ 'Expected feature to be removed from index');
+ this.undefIdIndex_[featureKey] = feature;
+ } else {
+ ol.DEBUG && console.assert(this.undefIdIndex_[featureKey] === feature,
+ 'feature keyed under %s in undefIdKeys', featureKey);
+ }
+ }
+ this.changed();
+ this.dispatchEvent(new ol.source.Vector.Event(
+ ol.source.Vector.EventType.CHANGEFEATURE, feature));
+};
+
+
+/**
+ * @return {boolean} Is empty.
+ */
+ol.source.Vector.prototype.isEmpty = function() {
+ return this.featuresRtree_.isEmpty() &&
+ ol.obj.isEmpty(this.nullGeometryFeatures_);
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {ol.proj.Projection} projection Projection.
+ */
+ol.source.Vector.prototype.loadFeatures = function(
+ extent, resolution, projection) {
+ var loadedExtentsRtree = this.loadedExtentsRtree_;
+ var extentsToLoad = this.strategy_(extent, resolution);
+ var i, ii;
+ for (i = 0, ii = extentsToLoad.length; i < ii; ++i) {
+ var extentToLoad = extentsToLoad[i];
+ var alreadyLoaded = loadedExtentsRtree.forEachInExtent(extentToLoad,
+ /**
+ * @param {{extent: ol.Extent}} object Object.
+ * @return {boolean} Contains.
+ */
+ function(object) {
+ return ol.extent.containsExtent(object.extent, extentToLoad);
+ });
+ if (!alreadyLoaded) {
+ this.loader_.call(this, extentToLoad, resolution, projection);
+ loadedExtentsRtree.insert(extentToLoad, {extent: extentToLoad.slice()});
+ }
+ }
+};
+
+
+/**
+ * Remove a single feature from the source. If you want to remove all features
+ * at once, use the {@link ol.source.Vector#clear source.clear()} method
+ * instead.
+ * @param {ol.Feature} feature Feature to remove.
+ * @api stable
+ */
+ol.source.Vector.prototype.removeFeature = function(feature) {
+ var featureKey = ol.getUid(feature).toString();
+ if (featureKey in this.nullGeometryFeatures_) {
+ delete this.nullGeometryFeatures_[featureKey];
+ } else {
+ if (this.featuresRtree_) {
+ this.featuresRtree_.remove(feature);
+ }
+ }
+ this.removeFeatureInternal(feature);
+ this.changed();
+};
+
+
+/**
+ * Remove feature without firing a `change` event.
+ * @param {ol.Feature} feature Feature.
+ * @protected
+ */
+ol.source.Vector.prototype.removeFeatureInternal = function(feature) {
+ var featureKey = ol.getUid(feature).toString();
+ ol.DEBUG && console.assert(featureKey in this.featureChangeKeys_,
+ 'featureKey exists in featureChangeKeys');
+ this.featureChangeKeys_[featureKey].forEach(ol.events.unlistenByKey);
+ delete this.featureChangeKeys_[featureKey];
+ var id = feature.getId();
+ if (id !== undefined) {
+ delete this.idIndex_[id.toString()];
+ } else {
+ delete this.undefIdIndex_[featureKey];
+ }
+ this.dispatchEvent(new ol.source.Vector.Event(
+ ol.source.Vector.EventType.REMOVEFEATURE, feature));
+};
+
+
+/**
+ * Remove a feature from the id index. Called internally when the feature id
+ * may have changed.
+ * @param {ol.Feature} feature The feature.
+ * @return {boolean} Removed the feature from the index.
+ * @private
+ */
+ol.source.Vector.prototype.removeFromIdIndex_ = function(feature) {
+ var removed = false;
+ for (var id in this.idIndex_) {
+ if (this.idIndex_[id] === feature) {
+ delete this.idIndex_[id];
+ removed = true;
+ break;
+ }
+ }
+ return removed;
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.source.Vector} instances are instances of this
+ * type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.source.Vector.Event}
+ * @param {string} type Type.
+ * @param {ol.Feature=} opt_feature Feature.
+ */
+ol.source.Vector.Event = function(type, opt_feature) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The feature being added or removed.
+ * @type {ol.Feature|undefined}
+ * @api stable
+ */
+ this.feature = opt_feature;
+
+};
+ol.inherits(ol.source.Vector.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.source.Vector.EventType = {
+ /**
+ * Triggered when a feature is added to the source.
+ * @event ol.source.Vector.Event#addfeature
+ * @api stable
+ */
+ ADDFEATURE: 'addfeature',
+
+ /**
+ * Triggered when a feature is updated.
+ * @event ol.source.Vector.Event#changefeature
+ * @api
+ */
+ CHANGEFEATURE: 'changefeature',
+
+ /**
+ * Triggered when the clear method is called on the source.
+ * @event ol.source.Vector.Event#clear
+ * @api
+ */
+ CLEAR: 'clear',
+
+ /**
+ * Triggered when a feature is removed from the source.
+ * See {@link ol.source.Vector#clear source.clear()} for exceptions.
+ * @event ol.source.Vector.Event#removefeature
+ * @api stable
+ */
+ REMOVEFEATURE: 'removefeature'
+};
+
+goog.provide('ol.interaction.Draw');
+
+goog.require('ol');
+goog.require('ol.events');
+goog.require('ol.extent');
+goog.require('ol.events.Event');
+goog.require('ol.Feature');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.Object');
+goog.require('ol.coordinate');
+goog.require('ol.functions');
+goog.require('ol.events.condition');
+goog.require('ol.geom.Circle');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.interaction.Pointer');
+goog.require('ol.layer.Vector');
+goog.require('ol.source.Vector');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * Interaction for drawing feature geometries.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @fires ol.interaction.Draw.Event
+ * @param {olx.interaction.DrawOptions} options Options.
+ * @api stable
+ */
+ol.interaction.Draw = function(options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.Draw.handleDownEvent_,
+ handleEvent: ol.interaction.Draw.handleEvent,
+ handleUpEvent: ol.interaction.Draw.handleUpEvent_
+ });
+
+ /**
+ * @type {ol.Pixel}
+ * @private
+ */
+ this.downPx_ = null;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.freehand_ = false;
+
+ /**
+ * Target source for drawn features.
+ * @type {ol.source.Vector}
+ * @private
+ */
+ this.source_ = options.source ? options.source : null;
+
+ /**
+ * Target collection for drawn features.
+ * @type {ol.Collection.<ol.Feature>}
+ * @private
+ */
+ this.features_ = options.features ? options.features : null;
+
+ /**
+ * Pixel distance for snapping.
+ * @type {number}
+ * @private
+ */
+ this.snapTolerance_ = options.snapTolerance ? options.snapTolerance : 12;
+
+ /**
+ * Geometry type.
+ * @type {ol.geom.GeometryType}
+ * @private
+ */
+ this.type_ = options.type;
+
+ /**
+ * Drawing mode (derived from geometry type.
+ * @type {ol.interaction.Draw.Mode}
+ * @private
+ */
+ this.mode_ = ol.interaction.Draw.getMode_(this.type_);
+
+ /**
+ * The number of points that must be drawn before a polygon ring or line
+ * string can be finished. The default is 3 for polygon rings and 2 for
+ * line strings.
+ * @type {number}
+ * @private
+ */
+ this.minPoints_ = options.minPoints ?
+ options.minPoints :
+ (this.mode_ === ol.interaction.Draw.Mode.POLYGON ? 3 : 2);
+
+ /**
+ * The number of points that can be drawn before a polygon ring or line string
+ * is finished. The default is no restriction.
+ * @type {number}
+ * @private
+ */
+ this.maxPoints_ = options.maxPoints ? options.maxPoints : Infinity;
+
+ /**
+ * A function to decide if a potential finish coordinate is permissable
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.finishCondition_ = options.finishCondition ? options.finishCondition : ol.functions.TRUE;
+
+ var geometryFunction = options.geometryFunction;
+ if (!geometryFunction) {
+ if (this.type_ === ol.geom.GeometryType.CIRCLE) {
+ /**
+ * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates
+ * The coordinates.
+ * @param {ol.geom.SimpleGeometry=} opt_geometry Optional geometry.
+ * @return {ol.geom.SimpleGeometry} A geometry.
+ */
+ geometryFunction = function(coordinates, opt_geometry) {
+ var circle = opt_geometry ? /** @type {ol.geom.Circle} */ (opt_geometry) :
+ new ol.geom.Circle([NaN, NaN]);
+ var squaredLength = ol.coordinate.squaredDistance(
+ coordinates[0], coordinates[1]);
+ circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength));
+ return circle;
+ };
+ } else {
+ var Constructor;
+ var mode = this.mode_;
+ if (mode === ol.interaction.Draw.Mode.POINT) {
+ Constructor = ol.geom.Point;
+ } else if (mode === ol.interaction.Draw.Mode.LINE_STRING) {
+ Constructor = ol.geom.LineString;
+ } else if (mode === ol.interaction.Draw.Mode.POLYGON) {
+ Constructor = ol.geom.Polygon;
+ }
+ /**
+ * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates
+ * The coordinates.
+ * @param {ol.geom.SimpleGeometry=} opt_geometry Optional geometry.
+ * @return {ol.geom.SimpleGeometry} A geometry.
+ */
+ geometryFunction = function(coordinates, opt_geometry) {
+ var geometry = opt_geometry;
+ if (geometry) {
+ if (mode === ol.interaction.Draw.Mode.POLYGON) {
+ geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])]);
+ } else {
+ geometry.setCoordinates(coordinates);
+ }
+ } else {
+ geometry = new Constructor(coordinates);
+ }
+ return geometry;
+ };
+ }
+ }
+
+ /**
+ * @type {ol.DrawGeometryFunctionType}
+ * @private
+ */
+ this.geometryFunction_ = geometryFunction;
+
+ /**
+ * Finish coordinate for the feature (first point for polygons, last point for
+ * linestrings).
+ * @type {ol.Coordinate}
+ * @private
+ */
+ this.finishCoordinate_ = null;
+
+ /**
+ * Sketch feature.
+ * @type {ol.Feature}
+ * @private
+ */
+ this.sketchFeature_ = null;
+
+ /**
+ * Sketch point.
+ * @type {ol.Feature}
+ * @private
+ */
+ this.sketchPoint_ = null;
+
+ /**
+ * Sketch coordinates. Used when drawing a line or polygon.
+ * @type {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>}
+ * @private
+ */
+ this.sketchCoords_ = null;
+
+ /**
+ * Sketch line. Used when drawing polygon.
+ * @type {ol.Feature}
+ * @private
+ */
+ this.sketchLine_ = null;
+
+ /**
+ * Sketch line coordinates. Used when drawing a polygon or circle.
+ * @type {Array.<ol.Coordinate>}
+ * @private
+ */
+ this.sketchLineCoords_ = null;
+
+ /**
+ * Squared tolerance for handling up events. If the squared distance
+ * between a down and up event is greater than this tolerance, up events
+ * will not be handled.
+ * @type {number}
+ * @private
+ */
+ this.squaredClickTolerance_ = options.clickTolerance ?
+ options.clickTolerance * options.clickTolerance : 36;
+
+ /**
+ * Draw overlay where our sketch features are drawn.
+ * @type {ol.layer.Vector}
+ * @private
+ */
+ this.overlay_ = new ol.layer.Vector({
+ source: new ol.source.Vector({
+ useSpatialIndex: false,
+ wrapX: options.wrapX ? options.wrapX : false
+ }),
+ style: options.style ? options.style :
+ ol.interaction.Draw.getDefaultStyleFunction()
+ });
+
+ /**
+ * Name of the geometry attribute for newly created features.
+ * @type {string|undefined}
+ * @private
+ */
+ this.geometryName_ = options.geometryName;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.noModifierKeys;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.freehandCondition_;
+ if (options.freehand) {
+ this.freehandCondition_ = ol.events.condition.always;
+ } else {
+ this.freehandCondition_ = options.freehandCondition ?
+ options.freehandCondition : ol.events.condition.shiftKeyOnly;
+ }
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.interaction.Interaction.Property.ACTIVE),
+ this.updateState_, this);
+
+};
+ol.inherits(ol.interaction.Draw, ol.interaction.Pointer);
+
+
+/**
+ * @return {ol.StyleFunction} Styles.
+ */
+ol.interaction.Draw.getDefaultStyleFunction = function() {
+ var styles = ol.style.Style.createDefaultEditing();
+ return function(feature, resolution) {
+ return styles[feature.getGeometry().getType()];
+ };
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Draw.prototype.setMap = function(map) {
+ ol.interaction.Pointer.prototype.setMap.call(this, map);
+ this.updateState_();
+};
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} and may actually
+ * draw or finish the drawing.
+ * @param {ol.MapBrowserEvent} event Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.Draw}
+ * @api
+ */
+ol.interaction.Draw.handleEvent = function(event) {
+ this.freehand_ = this.mode_ !== ol.interaction.Draw.Mode.POINT && this.freehandCondition_(event);
+ var pass = !this.freehand_;
+ if (this.freehand_ &&
+ event.type === ol.MapBrowserEvent.EventType.POINTERDRAG && this.sketchFeature_ !== null) {
+ this.addToDrawing_(event);
+ pass = false;
+ } else if (event.type ===
+ ol.MapBrowserEvent.EventType.POINTERMOVE) {
+ pass = this.handlePointerMove_(event);
+ } else if (event.type === ol.MapBrowserEvent.EventType.DBLCLICK) {
+ pass = false;
+ }
+ return ol.interaction.Pointer.handleEvent.call(this, event) && pass;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} event Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.Draw}
+ * @private
+ */
+ol.interaction.Draw.handleDownEvent_ = function(event) {
+ if (this.freehand_) {
+ this.downPx_ = event.pixel;
+ if (!this.finishCoordinate_) {
+ this.startDrawing_(event);
+ }
+ return true;
+ } else if (this.condition_(event)) {
+ this.downPx_ = event.pixel;
+ return true;
+ } else {
+ return false;
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} event Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.Draw}
+ * @private
+ */
+ol.interaction.Draw.handleUpEvent_ = function(event) {
+ var downPx = this.downPx_;
+ var clickPx = event.pixel;
+ var dx = downPx[0] - clickPx[0];
+ var dy = downPx[1] - clickPx[1];
+ var squaredDistance = dx * dx + dy * dy;
+ var pass = true;
+ var shouldHandle = this.freehand_ ?
+ squaredDistance > this.squaredClickTolerance_ :
+ squaredDistance <= this.squaredClickTolerance_;
+ var circleMode = this.mode_ === ol.interaction.Draw.Mode.CIRCLE;
+ if (shouldHandle) {
+ this.handlePointerMove_(event);
+ if (!this.finishCoordinate_) {
+ this.startDrawing_(event);
+ if (this.mode_ === ol.interaction.Draw.Mode.POINT) {
+ this.finishDrawing();
+ }
+ } else if (this.freehand_ || circleMode) {
+ this.finishDrawing();
+ } else if (this.atFinish_(event)) {
+ if (this.finishCondition_(event)) {
+ this.finishDrawing();
+ }
+ } else {
+ this.addToDrawing_(event);
+ }
+ pass = false;
+ } else if (circleMode) {
+ this.finishCoordinate_ = null;
+ }
+ return pass;
+};
+
+
+/**
+ * Handle move events.
+ * @param {ol.MapBrowserEvent} event A move event.
+ * @return {boolean} Pass the event to other interactions.
+ * @private
+ */
+ol.interaction.Draw.prototype.handlePointerMove_ = function(event) {
+ if (this.finishCoordinate_) {
+ this.modifyDrawing_(event);
+ } else {
+ this.createOrUpdateSketchPoint_(event);
+ }
+ return true;
+};
+
+
+/**
+ * Determine if an event is within the snapping tolerance of the start coord.
+ * @param {ol.MapBrowserEvent} event Event.
+ * @return {boolean} The event is within the snapping tolerance of the start.
+ * @private
+ */
+ol.interaction.Draw.prototype.atFinish_ = function(event) {
+ var at = false;
+ if (this.sketchFeature_) {
+ var potentiallyDone = false;
+ var potentiallyFinishCoordinates = [this.finishCoordinate_];
+ if (this.mode_ === ol.interaction.Draw.Mode.LINE_STRING) {
+ potentiallyDone = this.sketchCoords_.length > this.minPoints_;
+ } else if (this.mode_ === ol.interaction.Draw.Mode.POLYGON) {
+ potentiallyDone = this.sketchCoords_[0].length >
+ this.minPoints_;
+ potentiallyFinishCoordinates = [this.sketchCoords_[0][0],
+ this.sketchCoords_[0][this.sketchCoords_[0].length - 2]];
+ }
+ if (potentiallyDone) {
+ var map = event.map;
+ for (var i = 0, ii = potentiallyFinishCoordinates.length; i < ii; i++) {
+ var finishCoordinate = potentiallyFinishCoordinates[i];
+ var finishPixel = map.getPixelFromCoordinate(finishCoordinate);
+ var pixel = event.pixel;
+ var dx = pixel[0] - finishPixel[0];
+ var dy = pixel[1] - finishPixel[1];
+ var snapTolerance = this.freehand_ ? 1 : this.snapTolerance_;
+ at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance;
+ if (at) {
+ this.finishCoordinate_ = finishCoordinate;
+ break;
+ }
+ }
+ }
+ }
+ return at;
+};
+
+
+/**
+ * @param {ol.MapBrowserEvent} event Event.
+ * @private
+ */
+ol.interaction.Draw.prototype.createOrUpdateSketchPoint_ = function(event) {
+ var coordinates = event.coordinate.slice();
+ if (!this.sketchPoint_) {
+ this.sketchPoint_ = new ol.Feature(new ol.geom.Point(coordinates));
+ this.updateSketchFeatures_();
+ } else {
+ var sketchPointGeom = /** @type {ol.geom.Point} */ (this.sketchPoint_.getGeometry());
+ sketchPointGeom.setCoordinates(coordinates);
+ }
+};
+
+
+/**
+ * Start the drawing.
+ * @param {ol.MapBrowserEvent} event Event.
+ * @private
+ */
+ol.interaction.Draw.prototype.startDrawing_ = function(event) {
+ var start = event.coordinate;
+ this.finishCoordinate_ = start;
+ if (this.mode_ === ol.interaction.Draw.Mode.POINT) {
+ this.sketchCoords_ = start.slice();
+ } else if (this.mode_ === ol.interaction.Draw.Mode.POLYGON) {
+ this.sketchCoords_ = [[start.slice(), start.slice()]];
+ this.sketchLineCoords_ = this.sketchCoords_[0];
+ } else {
+ this.sketchCoords_ = [start.slice(), start.slice()];
+ if (this.mode_ === ol.interaction.Draw.Mode.CIRCLE) {
+ this.sketchLineCoords_ = this.sketchCoords_;
+ }
+ }
+ if (this.sketchLineCoords_) {
+ this.sketchLine_ = new ol.Feature(
+ new ol.geom.LineString(this.sketchLineCoords_));
+ }
+ var geometry = this.geometryFunction_(this.sketchCoords_);
+ ol.DEBUG && console.assert(geometry !== undefined, 'geometry should be defined');
+ this.sketchFeature_ = new ol.Feature();
+ if (this.geometryName_) {
+ this.sketchFeature_.setGeometryName(this.geometryName_);
+ }
+ this.sketchFeature_.setGeometry(geometry);
+ this.updateSketchFeatures_();
+ this.dispatchEvent(new ol.interaction.Draw.Event(
+ ol.interaction.Draw.EventType.DRAWSTART, this.sketchFeature_));
+};
+
+
+/**
+ * Modify the drawing.
+ * @param {ol.MapBrowserEvent} event Event.
+ * @private
+ */
+ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
+ var coordinate = event.coordinate;
+ var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.sketchFeature_.getGeometry());
+ var coordinates, last;
+ if (this.mode_ === ol.interaction.Draw.Mode.POINT) {
+ last = this.sketchCoords_;
+ } else if (this.mode_ === ol.interaction.Draw.Mode.POLYGON) {
+ coordinates = this.sketchCoords_[0];
+ last = coordinates[coordinates.length - 1];
+ if (this.atFinish_(event)) {
+ // snap to finish
+ coordinate = this.finishCoordinate_.slice();
+ }
+ } else {
+ coordinates = this.sketchCoords_;
+ last = coordinates[coordinates.length - 1];
+ }
+ last[0] = coordinate[0];
+ last[1] = coordinate[1];
+ ol.DEBUG && console.assert(this.sketchCoords_, 'sketchCoords_ expected');
+ this.geometryFunction_(
+ /** @type {!ol.Coordinate|!Array.<ol.Coordinate>|!Array.<Array.<ol.Coordinate>>} */ (this.sketchCoords_),
+ geometry);
+ if (this.sketchPoint_) {
+ var sketchPointGeom = /** @type {ol.geom.Point} */ (this.sketchPoint_.getGeometry());
+ sketchPointGeom.setCoordinates(coordinate);
+ }
+ var sketchLineGeom;
+ if (geometry instanceof ol.geom.Polygon &&
+ this.mode_ !== ol.interaction.Draw.Mode.POLYGON) {
+ if (!this.sketchLine_) {
+ this.sketchLine_ = new ol.Feature(new ol.geom.LineString(null));
+ }
+ var ring = geometry.getLinearRing(0);
+ sketchLineGeom = /** @type {ol.geom.LineString} */ (this.sketchLine_.getGeometry());
+ sketchLineGeom.setFlatCoordinates(
+ ring.getLayout(), ring.getFlatCoordinates());
+ } else if (this.sketchLineCoords_) {
+ sketchLineGeom = /** @type {ol.geom.LineString} */ (this.sketchLine_.getGeometry());
+ sketchLineGeom.setCoordinates(this.sketchLineCoords_);
+ }
+ this.updateSketchFeatures_();
+};
+
+
+/**
+ * Add a new coordinate to the drawing.
+ * @param {ol.MapBrowserEvent} event Event.
+ * @private
+ */
+ol.interaction.Draw.prototype.addToDrawing_ = function(event) {
+ var coordinate = event.coordinate;
+ var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.sketchFeature_.getGeometry());
+ var done;
+ var coordinates;
+ if (this.mode_ === ol.interaction.Draw.Mode.LINE_STRING) {
+ this.finishCoordinate_ = coordinate.slice();
+ coordinates = this.sketchCoords_;
+ if (coordinates.length >= this.maxPoints_) {
+ if (this.freehand_) {
+ coordinates.pop();
+ } else {
+ done = true;
+ }
+ }
+ coordinates.push(coordinate.slice());
+ this.geometryFunction_(coordinates, geometry);
+ } else if (this.mode_ === ol.interaction.Draw.Mode.POLYGON) {
+ coordinates = this.sketchCoords_[0];
+ if (coordinates.length >= this.maxPoints_) {
+ if (this.freehand_) {
+ coordinates.pop();
+ } else {
+ done = true;
+ }
+ }
+ coordinates.push(coordinate.slice());
+ if (done) {
+ this.finishCoordinate_ = coordinates[0];
+ }
+ this.geometryFunction_(this.sketchCoords_, geometry);
+ }
+ this.updateSketchFeatures_();
+ if (done) {
+ this.finishDrawing();
+ }
+};
+
+
+/**
+ * Remove last point of the feature currently being drawn.
+ * @api
+ */
+ol.interaction.Draw.prototype.removeLastPoint = function() {
+ var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.sketchFeature_.getGeometry());
+ var coordinates, sketchLineGeom;
+ if (this.mode_ === ol.interaction.Draw.Mode.LINE_STRING) {
+ coordinates = this.sketchCoords_;
+ coordinates.splice(-2, 1);
+ this.geometryFunction_(coordinates, geometry);
+ } else if (this.mode_ === ol.interaction.Draw.Mode.POLYGON) {
+ coordinates = this.sketchCoords_[0];
+ coordinates.splice(-2, 1);
+ sketchLineGeom = /** @type {ol.geom.LineString} */ (this.sketchLine_.getGeometry());
+ sketchLineGeom.setCoordinates(coordinates);
+ this.geometryFunction_(this.sketchCoords_, geometry);
+ }
+
+ if (coordinates.length === 0) {
+ this.finishCoordinate_ = null;
+ }
+
+ this.updateSketchFeatures_();
+};
+
+
+/**
+ * Stop drawing and add the sketch feature to the target layer.
+ * The {@link ol.interaction.Draw.EventType.DRAWEND} event is dispatched before
+ * inserting the feature.
+ * @api
+ */
+ol.interaction.Draw.prototype.finishDrawing = function() {
+ var sketchFeature = this.abortDrawing_();
+ var coordinates = this.sketchCoords_;
+ var geometry = /** @type {ol.geom.SimpleGeometry} */ (sketchFeature.getGeometry());
+ if (this.mode_ === ol.interaction.Draw.Mode.LINE_STRING) {
+ // remove the redundant last point
+ coordinates.pop();
+ this.geometryFunction_(coordinates, geometry);
+ } else if (this.mode_ === ol.interaction.Draw.Mode.POLYGON) {
+ // remove the redundant last point in ring
+ coordinates[0].pop();
+ this.geometryFunction_(coordinates, geometry);
+ coordinates = geometry.getCoordinates();
+ }
+
+ // cast multi-part geometries
+ if (this.type_ === ol.geom.GeometryType.MULTI_POINT) {
+ sketchFeature.setGeometry(new ol.geom.MultiPoint([coordinates]));
+ } else if (this.type_ === ol.geom.GeometryType.MULTI_LINE_STRING) {
+ sketchFeature.setGeometry(new ol.geom.MultiLineString([coordinates]));
+ } else if (this.type_ === ol.geom.GeometryType.MULTI_POLYGON) {
+ sketchFeature.setGeometry(new ol.geom.MultiPolygon([coordinates]));
+ }
+
+ // First dispatch event to allow full set up of feature
+ this.dispatchEvent(new ol.interaction.Draw.Event(
+ ol.interaction.Draw.EventType.DRAWEND, sketchFeature));
+
+ // Then insert feature
+ if (this.features_) {
+ this.features_.push(sketchFeature);
+ }
+ if (this.source_) {
+ this.source_.addFeature(sketchFeature);
+ }
+};
+
+
+/**
+ * Stop drawing without adding the sketch feature to the target layer.
+ * @return {ol.Feature} The sketch feature (or null if none).
+ * @private
+ */
+ol.interaction.Draw.prototype.abortDrawing_ = function() {
+ this.finishCoordinate_ = null;
+ var sketchFeature = this.sketchFeature_;
+ if (sketchFeature) {
+ this.sketchFeature_ = null;
+ this.sketchPoint_ = null;
+ this.sketchLine_ = null;
+ this.overlay_.getSource().clear(true);
+ }
+ return sketchFeature;
+};
+
+
+/**
+ * Extend an existing geometry by adding additional points. This only works
+ * on features with `LineString` geometries, where the interaction will
+ * extend lines by adding points to the end of the coordinates array.
+ * @param {!ol.Feature} feature Feature to be extended.
+ * @api
+ */
+ol.interaction.Draw.prototype.extend = function(feature) {
+ var geometry = feature.getGeometry();
+ ol.DEBUG && console.assert(this.mode_ == ol.interaction.Draw.Mode.LINE_STRING,
+ 'interaction mode must be "line"');
+ ol.DEBUG && console.assert(geometry.getType() == ol.geom.GeometryType.LINE_STRING,
+ 'feature geometry must be a line string');
+ var lineString = /** @type {ol.geom.LineString} */ (geometry);
+ this.sketchFeature_ = feature;
+ this.sketchCoords_ = lineString.getCoordinates();
+ var last = this.sketchCoords_[this.sketchCoords_.length - 1];
+ this.finishCoordinate_ = last.slice();
+ this.sketchCoords_.push(last.slice());
+ this.updateSketchFeatures_();
+ this.dispatchEvent(new ol.interaction.Draw.Event(
+ ol.interaction.Draw.EventType.DRAWSTART, this.sketchFeature_));
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Draw.prototype.shouldStopEvent = ol.functions.FALSE;
+
+
+/**
+ * Redraw the sketch features.
+ * @private
+ */
+ol.interaction.Draw.prototype.updateSketchFeatures_ = function() {
+ var sketchFeatures = [];
+ if (this.sketchFeature_) {
+ sketchFeatures.push(this.sketchFeature_);
+ }
+ if (this.sketchLine_) {
+ sketchFeatures.push(this.sketchLine_);
+ }
+ if (this.sketchPoint_) {
+ sketchFeatures.push(this.sketchPoint_);
+ }
+ var overlaySource = this.overlay_.getSource();
+ overlaySource.clear(true);
+ overlaySource.addFeatures(sketchFeatures);
+};
+
+
+/**
+ * @private
+ */
+ol.interaction.Draw.prototype.updateState_ = function() {
+ var map = this.getMap();
+ var active = this.getActive();
+ if (!map || !active) {
+ this.abortDrawing_();
+ }
+ this.overlay_.setMap(active ? map : null);
+};
+
+
+/**
+ * Create a `geometryFunction` for `type: 'Circle'` that will create a regular
+ * polygon with a user specified number of sides and start angle instead of an
+ * `ol.geom.Circle` geometry.
+ * @param {number=} opt_sides Number of sides of the regular polygon. Default is
+ * 32.
+ * @param {number=} opt_angle Angle of the first point in radians. 0 means East.
+ * Default is the angle defined by the heading from the center of the
+ * regular polygon to the current pointer position.
+ * @return {ol.DrawGeometryFunctionType} Function that draws a
+ * polygon.
+ * @api
+ */
+ol.interaction.Draw.createRegularPolygon = function(opt_sides, opt_angle) {
+ return (
+ /**
+ * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates
+ * @param {ol.geom.SimpleGeometry=} opt_geometry
+ * @return {ol.geom.SimpleGeometry}
+ */
+ function(coordinates, opt_geometry) {
+ var center = coordinates[0];
+ var end = coordinates[1];
+ var radius = Math.sqrt(
+ ol.coordinate.squaredDistance(center, end));
+ var geometry = opt_geometry ? /** @type {ol.geom.Polygon} */ (opt_geometry) :
+ ol.geom.Polygon.fromCircle(new ol.geom.Circle(center), opt_sides);
+ var angle = opt_angle ? opt_angle :
+ Math.atan((end[1] - center[1]) / (end[0] - center[0]));
+ ol.geom.Polygon.makeRegular(geometry, center, radius, angle);
+ return geometry;
+ }
+ );
+};
+
+
+/**
+ * Create a `geometryFunction` that will create a box-shaped polygon (aligned
+ * with the coordinate system axes). Use this with the draw interaction and
+ * `type: 'Circle'` to return a box instead of a circle geometry.
+ * @return {ol.DrawGeometryFunctionType} Function that draws a box-shaped polygon.
+ * @api
+ */
+ol.interaction.Draw.createBox = function() {
+ return (
+ /**
+ * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates
+ * @param {ol.geom.SimpleGeometry=} opt_geometry
+ * @return {ol.geom.SimpleGeometry}
+ */
+ function(coordinates, opt_geometry) {
+ var extent = ol.extent.boundingExtent(coordinates);
+ var geometry = opt_geometry || new ol.geom.Polygon(null);
+ geometry.setCoordinates([[
+ ol.extent.getBottomLeft(extent),
+ ol.extent.getBottomRight(extent),
+ ol.extent.getTopRight(extent),
+ ol.extent.getTopLeft(extent),
+ ol.extent.getBottomLeft(extent)
+ ]]);
+ return geometry;
+ }
+ );
+};
+
+
+/**
+ * Get the drawing mode. The mode for mult-part geometries is the same as for
+ * their single-part cousins.
+ * @param {ol.geom.GeometryType} type Geometry type.
+ * @return {ol.interaction.Draw.Mode} Drawing mode.
+ * @private
+ */
+ol.interaction.Draw.getMode_ = function(type) {
+ var mode;
+ if (type === ol.geom.GeometryType.POINT ||
+ type === ol.geom.GeometryType.MULTI_POINT) {
+ mode = ol.interaction.Draw.Mode.POINT;
+ } else if (type === ol.geom.GeometryType.LINE_STRING ||
+ type === ol.geom.GeometryType.MULTI_LINE_STRING) {
+ mode = ol.interaction.Draw.Mode.LINE_STRING;
+ } else if (type === ol.geom.GeometryType.POLYGON ||
+ type === ol.geom.GeometryType.MULTI_POLYGON) {
+ mode = ol.interaction.Draw.Mode.POLYGON;
+ } else if (type === ol.geom.GeometryType.CIRCLE) {
+ mode = ol.interaction.Draw.Mode.CIRCLE;
+ }
+ return /** @type {!ol.interaction.Draw.Mode} */ (mode);
+};
+
+
+/**
+ * Draw mode. This collapses multi-part geometry types with their single-part
+ * cousins.
+ * @enum {string}
+ */
+ol.interaction.Draw.Mode = {
+ POINT: 'Point',
+ LINE_STRING: 'LineString',
+ POLYGON: 'Polygon',
+ CIRCLE: 'Circle'
+};
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.Draw} instances are instances of
+ * this type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.DrawEvent}
+ * @param {ol.interaction.Draw.EventType} type Type.
+ * @param {ol.Feature} feature The feature drawn.
+ */
+ol.interaction.Draw.Event = function(type, feature) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The feature being drawn.
+ * @type {ol.Feature}
+ * @api stable
+ */
+ this.feature = feature;
+
+};
+ol.inherits(ol.interaction.Draw.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.Draw.EventType = {
+ /**
+ * Triggered upon feature draw start
+ * @event ol.interaction.Draw.Event#drawstart
+ * @api stable
+ */
+ DRAWSTART: 'drawstart',
+ /**
+ * Triggered upon feature draw end
+ * @event ol.interaction.Draw.Event#drawend
+ * @api stable
+ */
+ DRAWEND: 'drawend'
+};
+
+goog.provide('ol.interaction.Extent');
+
+goog.require('ol');
+goog.require('ol.Feature');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.MapBrowserPointerEvent');
+goog.require('ol.coordinate');
+goog.require('ol.events.Event');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.interaction.Pointer');
+goog.require('ol.layer.Vector');
+goog.require('ol.source.Vector');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * Allows the user to draw a vector box by clicking and dragging on the map.
+ * Once drawn, the vector box can be modified by dragging its vertices or edges.
+ * This interaction is only supported for mouse devices.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @fires ol.interaction.Extent.Event
+ * @param {olx.interaction.ExtentOptions=} opt_options Options.
+ * @api
+ */
+ol.interaction.Extent = function(opt_options) {
+
+ /**
+ * Extent of the drawn box
+ * @type {ol.Extent}
+ * @private
+ */
+ this.extent_ = null;
+
+ /**
+ * Handler for pointer move events
+ * @type {function (ol.Coordinate): ol.Extent|null}
+ * @private
+ */
+ this.pointerHandler_ = null;
+
+ /**
+ * Pixel threshold to snap to extent
+ * @type {number}
+ * @private
+ */
+ this.pixelTolerance_ = 10;
+
+ /**
+ * Is the pointer snapped to an extent vertex
+ * @type {boolean}
+ * @private
+ */
+ this.snappedToVertex_ = false;
+
+ /**
+ * Feature for displaying the visible extent
+ * @type {ol.Feature}
+ * @private
+ */
+ this.extentFeature_ = null;
+
+ /**
+ * Feature for displaying the visible pointer
+ * @type {ol.Feature}
+ * @private
+ */
+ this.vertexFeature_ = null;
+
+ if (!opt_options) {
+ opt_options = {};
+ }
+
+ if (opt_options.extent) {
+ this.setExtent(opt_options.extent);
+ }
+
+ /* Inherit ol.interaction.Pointer */
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.Extent.handleDownEvent_,
+ handleDragEvent: ol.interaction.Extent.handleDragEvent_,
+ handleEvent: ol.interaction.Extent.handleEvent_,
+ handleUpEvent: ol.interaction.Extent.handleUpEvent_
+ });
+
+ /**
+ * Layer for the extentFeature
+ * @type {ol.layer.Vector}
+ * @private
+ */
+ this.extentOverlay_ = new ol.layer.Vector({
+ source: new ol.source.Vector({
+ useSpatialIndex: false,
+ wrapX: !!opt_options.wrapX
+ }),
+ style: opt_options.boxStyle ? opt_options.boxStyle : ol.interaction.Extent.getDefaultExtentStyleFunction_(),
+ updateWhileAnimating: true,
+ updateWhileInteracting: true
+ });
+
+ /**
+ * Layer for the vertexFeature
+ * @type {ol.layer.Vector}
+ * @private
+ */
+ this.vertexOverlay_ = new ol.layer.Vector({
+ source: new ol.source.Vector({
+ useSpatialIndex: false,
+ wrapX: !!opt_options.wrapX
+ }),
+ style: opt_options.pointerStyle ? opt_options.pointerStyle : ol.interaction.Extent.getDefaultPointerStyleFunction_(),
+ updateWhileAnimating: true,
+ updateWhileInteracting: true
+ });
+};
+
+ol.inherits(ol.interaction.Extent, ol.interaction.Pointer);
+
+/**
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Event.
+ * @return {boolean} Propagate event?
+ * @this {ol.interaction.Extent}
+ * @private
+ */
+ol.interaction.Extent.handleEvent_ = function(mapBrowserEvent) {
+ if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) {
+ return true;
+ }
+ //display pointer (if not dragging)
+ if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE && !this.handlingDownUpSequence) {
+ this.handlePointerMove_(mapBrowserEvent);
+ }
+ //call pointer to determine up/down/drag
+ ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent);
+ //return false to stop propagation
+ return false;
+};
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Event handled?
+ * @this {ol.interaction.Extent}
+ * @private
+ */
+ol.interaction.Extent.handleDownEvent_ = function(mapBrowserEvent) {
+ var pixel = mapBrowserEvent.pixel;
+ var map = mapBrowserEvent.map;
+
+ var extent = this.getExtent();
+ var vertex = this.snapToVertex_(pixel, map);
+
+ //find the extent corner opposite the passed corner
+ var getOpposingPoint = function(point) {
+ var x_ = null;
+ var y_ = null;
+ if (point[0] == extent[0]) {
+ x_ = extent[2];
+ } else if (point[0] == extent[2]) {
+ x_ = extent[0];
+ }
+ if (point[1] == extent[1]) {
+ y_ = extent[3];
+ } else if (point[1] == extent[3]) {
+ y_ = extent[1];
+ }
+ if (x_ !== null && y_ !== null) {
+ return [x_, y_];
+ }
+ return null;
+ };
+ if (vertex && extent) {
+ var x = (vertex[0] == extent[0] || vertex[0] == extent[2]) ? vertex[0] : null;
+ var y = (vertex[1] == extent[1] || vertex[1] == extent[3]) ? vertex[1] : null;
+
+ //snap to point
+ if (x !== null && y !== null) {
+ this.pointerHandler_ = ol.interaction.Extent.getPointHandler_(getOpposingPoint(vertex));
+ //snap to edge
+ } else if (x !== null) {
+ this.pointerHandler_ = ol.interaction.Extent.getEdgeHandler_(
+ getOpposingPoint([x, extent[1]]),
+ getOpposingPoint([x, extent[3]])
+ );
+ } else if (y !== null) {
+ this.pointerHandler_ = ol.interaction.Extent.getEdgeHandler_(
+ getOpposingPoint([extent[0], y]),
+ getOpposingPoint([extent[2], y])
+ );
+ }
+ //no snap - new bbox
+ } else {
+ vertex = map.getCoordinateFromPixel(pixel);
+ this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]);
+ this.pointerHandler_ = ol.interaction.Extent.getPointHandler_(vertex);
+ }
+ return true; //event handled; start downup sequence
+};
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Event handled?
+ * @this {ol.interaction.Extent}
+ * @private
+ */
+ol.interaction.Extent.handleDragEvent_ = function(mapBrowserEvent) {
+ if (this.pointerHandler_) {
+ var pixelCoordinate = mapBrowserEvent.coordinate;
+ this.setExtent(this.pointerHandler_(pixelCoordinate));
+ this.createOrUpdatePointerFeature_(pixelCoordinate);
+ }
+ return true;
+};
+
+/**
+ * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.Extent}
+ * @private
+ */
+ol.interaction.Extent.handleUpEvent_ = function(mapBrowserEvent) {
+ this.pointerHandler_ = null;
+ //If bbox is zero area, set to null;
+ var extent = this.getExtent();
+ if (!extent || ol.extent.getArea(extent) === 0) {
+ this.setExtent(null);
+ }
+ return false; //Stop handling downup sequence
+};
+
+/**
+ * Returns the default style for the drawn bbox
+ *
+ * @return {ol.StyleFunction} Default Extent style
+ * @private
+ */
+ol.interaction.Extent.getDefaultExtentStyleFunction_ = function() {
+ var style = ol.style.Style.createDefaultEditing();
+ return function(feature, resolution) {
+ return style[ol.geom.GeometryType.POLYGON];
+ };
+};
+
+/**
+ * Returns the default style for the pointer
+ *
+ * @return {ol.StyleFunction} Default pointer style
+ * @private
+ */
+ol.interaction.Extent.getDefaultPointerStyleFunction_ = function() {
+ var style = ol.style.Style.createDefaultEditing();
+ return function(feature, resolution) {
+ return style[ol.geom.GeometryType.POINT];
+ };
+};
+
+/**
+ * @param {ol.Coordinate} fixedPoint corner that will be unchanged in the new extent
+ * @returns {function (ol.Coordinate): ol.Extent} event handler
+ * @private
+ */
+ol.interaction.Extent.getPointHandler_ = function(fixedPoint) {
+ return function(point) {
+ return ol.extent.boundingExtent([fixedPoint, point]);
+ };
+};
+
+/**
+ * @param {ol.Coordinate} fixedP1 first corner that will be unchanged in the new extent
+ * @param {ol.Coordinate} fixedP2 second corner that will be unchanged in the new extent
+ * @returns {function (ol.Coordinate): ol.Extent|null} event handler
+ * @private
+ */
+ol.interaction.Extent.getEdgeHandler_ = function(fixedP1, fixedP2) {
+ if (fixedP1[0] == fixedP2[0]) {
+ return function(point) {
+ return ol.extent.boundingExtent([fixedP1, [point[0], fixedP2[1]]]);
+ };
+ } else if (fixedP1[1] == fixedP2[1]) {
+ return function(point) {
+ return ol.extent.boundingExtent([fixedP1, [fixedP2[0], point[1]]]);
+ };
+ } else {
+ return null;
+ }
+};
+
+/**
+ * @param {ol.Extent} extent extent
+ * @returns {Array<Array<ol.Coordinate>>} extent line segments
+ * @private
+ */
+ol.interaction.Extent.getSegments_ = function(extent) {
+ return [
+ [[extent[0], extent[1]], [extent[0], extent[3]]],
+ [[extent[0], extent[3]], [extent[2], extent[3]]],
+ [[extent[2], extent[3]], [extent[2], extent[1]]],
+ [[extent[2], extent[1]], [extent[0], extent[1]]]
+ ];
+};
+
+/**
+ * @param {ol.Pixel} pixel cursor location
+ * @param {ol.Map} map map
+ * @returns {ol.Coordinate|null} snapped vertex on extent
+ * @private
+ */
+ol.interaction.Extent.prototype.snapToVertex_ = function(pixel, map) {
+ var pixelCoordinate = map.getCoordinateFromPixel(pixel);
+ var sortByDistance = function(a, b) {
+ return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a) -
+ ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b);
+ };
+ var extent = this.getExtent();
+ if (extent) {
+ //convert extents to line segments and find the segment closest to pixelCoordinate
+ var segments = ol.interaction.Extent.getSegments_(extent);
+ segments.sort(sortByDistance);
+ var closestSegment = segments[0];
+
+ var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate,
+ closestSegment));
+ var vertexPixel = map.getPixelFromCoordinate(vertex);
+
+ //if the distance is within tolerance, snap to the segment
+ if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <=
+ this.pixelTolerance_) {
+
+ //test if we should further snap to a vertex
+ var pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
+ var pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
+ var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1);
+ var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2);
+ var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
+ this.snappedToVertex_ = dist <= this.pixelTolerance_;
+ if (this.snappedToVertex_) {
+ vertex = squaredDist1 > squaredDist2 ?
+ closestSegment[1] : closestSegment[0];
+ }
+ return vertex;
+ }
+ }
+ return null;
+};
+
+/**
+ * @param {ol.MapBrowserEvent} mapBrowserEvent pointer move event
+ * @private
+ */
+ol.interaction.Extent.prototype.handlePointerMove_ = function(mapBrowserEvent) {
+ var pixel = mapBrowserEvent.pixel;
+ var map = mapBrowserEvent.map;
+
+ var vertex = this.snapToVertex_(pixel, map);
+ if (!vertex) {
+ vertex = map.getCoordinateFromPixel(pixel);
+ }
+ this.createOrUpdatePointerFeature_(vertex);
+};
+
+/**
+ * @param {ol.Extent} extent extent
+ * @returns {ol.Feature} extent as featrue
+ * @private
+ */
+ol.interaction.Extent.prototype.createOrUpdateExtentFeature_ = function(extent) {
+ var extentFeature = this.extentFeature_;
+
+ if (!extentFeature) {
+ if (!extent) {
+ extentFeature = new ol.Feature({});
+ } else {
+ extentFeature = new ol.Feature(ol.geom.Polygon.fromExtent(extent));
+ }
+ this.extentFeature_ = extentFeature;
+ this.extentOverlay_.getSource().addFeature(extentFeature);
+ } else {
+ if (!extent) {
+ extentFeature.setGeometry(undefined);
+ } else {
+ extentFeature.setGeometry(ol.geom.Polygon.fromExtent(extent));
+ }
+ }
+ return extentFeature;
+};
+
+
+/**
+ * @param {ol.Coordinate} vertex location of feature
+ * @returns {ol.Feature} vertex as feature
+ * @private
+ */
+ol.interaction.Extent.prototype.createOrUpdatePointerFeature_ = function(vertex) {
+ var vertexFeature = this.vertexFeature_;
+ if (!vertexFeature) {
+ vertexFeature = new ol.Feature(new ol.geom.Point(vertex));
+ this.vertexFeature_ = vertexFeature;
+ this.vertexOverlay_.getSource().addFeature(vertexFeature);
+ } else {
+ var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry());
+ geometry.setCoordinates(vertex);
+ }
+ return vertexFeature;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Extent.prototype.setMap = function(map) {
+ this.extentOverlay_.setMap(map);
+ this.vertexOverlay_.setMap(map);
+ ol.interaction.Pointer.prototype.setMap.call(this, map);
+};
+
+/**
+ * Returns the current drawn extent in the view projection
+ *
+ * @return {ol.Extent} Drawn extent in the view projection.
+ * @api
+ */
+ol.interaction.Extent.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+/**
+ * Manually sets the drawn extent, using the view projection.
+ *
+ * @param {ol.Extent} extent Extent
+ * @api
+ */
+ol.interaction.Extent.prototype.setExtent = function(extent) {
+ //Null extent means no bbox
+ this.extent_ = extent ? extent : null;
+ this.createOrUpdateExtentFeature_(extent);
+ this.dispatchEvent(new ol.interaction.Extent.Event(this.extent_));
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.Extent} instances are instances of
+ * this type.
+ *
+ * @constructor
+ * @param {ol.Extent} extent the new extent
+ * @extends {ol.events.Event}
+ */
+ol.interaction.Extent.Event = function(extent) {
+ ol.events.Event.call(this, ol.interaction.Extent.EventType.EXTENTCHANGED);
+
+ /**
+ * The current extent.
+ * @type {ol.Extent}
+ * @api
+ */
+ this.extent_ = extent;
+};
+ol.inherits(ol.interaction.Extent.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.Extent.EventType = {
+ /**
+ * Triggered after the extent is changed
+ * @event ol.interaction.Extent.Event
+ * @api
+ */
+ EXTENTCHANGED: 'extentchanged'
+};
+
+goog.provide('ol.interaction.Modify');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.Feature');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.MapBrowserPointerEvent');
+goog.require('ol.View');
+goog.require('ol.array');
+goog.require('ol.coordinate');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.EventType');
+goog.require('ol.events.condition');
+goog.require('ol.extent');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.geom.Point');
+goog.require('ol.interaction.Pointer');
+goog.require('ol.layer.Vector');
+goog.require('ol.source.Vector');
+goog.require('ol.structs.RBush');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * Interaction for modifying feature geometries.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.ModifyOptions} options Options.
+ * @fires ol.interaction.Modify.Event
+ * @api
+ */
+ol.interaction.Modify = function(options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.Modify.handleDownEvent_,
+ handleDragEvent: ol.interaction.Modify.handleDragEvent_,
+ handleEvent: ol.interaction.Modify.handleEvent,
+ handleUpEvent: ol.interaction.Modify.handleUpEvent_
+ });
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.primaryAction;
+
+
+ /**
+ * @private
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Browser event.
+ * @return {boolean} Combined condition result.
+ */
+ this.defaultDeleteCondition_ = function(mapBrowserEvent) {
+ return ol.events.condition.noModifierKeys(mapBrowserEvent) &&
+ ol.events.condition.singleClick(mapBrowserEvent);
+ };
+
+ /**
+ * @type {ol.EventsConditionType}
+ * @private
+ */
+ this.deleteCondition_ = options.deleteCondition ?
+ options.deleteCondition : this.defaultDeleteCondition_;
+
+ /**
+ * Editing vertex.
+ * @type {ol.Feature}
+ * @private
+ */
+ this.vertexFeature_ = null;
+
+ /**
+ * Segments intersecting {@link this.vertexFeature_} by segment uid.
+ * @type {Object.<string, boolean>}
+ * @private
+ */
+ this.vertexSegments_ = null;
+
+ /**
+ * @type {ol.Pixel}
+ * @private
+ */
+ this.lastPixel_ = [0, 0];
+
+ /**
+ * Tracks if the next `singleclick` event should be ignored to prevent
+ * accidental deletion right after vertex creation.
+ * @type {boolean}
+ * @private
+ */
+ this.ignoreNextSingleClick_ = false;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.modified_ = false;
+
+ /**
+ * Segment RTree for each layer
+ * @type {ol.structs.RBush.<ol.ModifySegmentDataType>}
+ * @private
+ */
+ this.rBush_ = new ol.structs.RBush();
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.pixelTolerance_ = options.pixelTolerance !== undefined ?
+ options.pixelTolerance : 10;
+
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.snappedToVertex_ = false;
+
+ /**
+ * Indicate whether the interaction is currently changing a feature's
+ * coordinates.
+ * @type {boolean}
+ * @private
+ */
+ this.changingFeature_ = false;
+
+ /**
+ * @type {Array}
+ * @private
+ */
+ this.dragSegments_ = [];
+
+ /**
+ * Draw overlay where sketch features are drawn.
+ * @type {ol.layer.Vector}
+ * @private
+ */
+ this.overlay_ = new ol.layer.Vector({
+ source: new ol.source.Vector({
+ useSpatialIndex: false,
+ wrapX: !!options.wrapX
+ }),
+ style: options.style ? options.style :
+ ol.interaction.Modify.getDefaultStyleFunction(),
+ updateWhileAnimating: true,
+ updateWhileInteracting: true
+ });
+
+ /**
+ * @const
+ * @private
+ * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)>}
+ */
+ this.SEGMENT_WRITERS_ = {
+ 'Point': this.writePointGeometry_,
+ 'LineString': this.writeLineStringGeometry_,
+ 'LinearRing': this.writeLineStringGeometry_,
+ 'Polygon': this.writePolygonGeometry_,
+ 'MultiPoint': this.writeMultiPointGeometry_,
+ 'MultiLineString': this.writeMultiLineStringGeometry_,
+ 'MultiPolygon': this.writeMultiPolygonGeometry_,
+ 'GeometryCollection': this.writeGeometryCollectionGeometry_
+ };
+
+ /**
+ * @type {ol.Collection.<ol.Feature>}
+ * @private
+ */
+ this.features_ = options.features;
+
+ this.features_.forEach(this.addFeature_, this);
+ ol.events.listen(this.features_, ol.Collection.EventType.ADD,
+ this.handleFeatureAdd_, this);
+ ol.events.listen(this.features_, ol.Collection.EventType.REMOVE,
+ this.handleFeatureRemove_, this);
+
+ /**
+ * @type {ol.MapBrowserPointerEvent}
+ * @private
+ */
+ this.lastPointerEvent_ = null;
+
+};
+ol.inherits(ol.interaction.Modify, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.interaction.Modify.prototype.addFeature_ = function(feature) {
+ var geometry = feature.getGeometry();
+ if (geometry && geometry.getType() in this.SEGMENT_WRITERS_) {
+ this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry);
+ }
+ var map = this.getMap();
+ if (map && map.isRendered()) {
+ this.handlePointerAtPixel_(this.lastPixel_, map);
+ }
+ ol.events.listen(feature, ol.events.EventType.CHANGE,
+ this.handleFeatureChange_, this);
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} evt Map browser event
+ * @private
+ */
+ol.interaction.Modify.prototype.willModifyFeatures_ = function(evt) {
+ if (!this.modified_) {
+ this.modified_ = true;
+ this.dispatchEvent(new ol.interaction.Modify.Event(
+ ol.interaction.Modify.EventType.MODIFYSTART, this.features_, evt));
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.interaction.Modify.prototype.removeFeature_ = function(feature) {
+ this.removeFeatureSegmentData_(feature);
+ // Remove the vertex feature if the collection of canditate features
+ // is empty.
+ if (this.vertexFeature_ && this.features_.getLength() === 0) {
+ this.overlay_.getSource().removeFeature(this.vertexFeature_);
+ this.vertexFeature_ = null;
+ }
+ ol.events.unlisten(feature, ol.events.EventType.CHANGE,
+ this.handleFeatureChange_, this);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.interaction.Modify.prototype.removeFeatureSegmentData_ = function(feature) {
+ var rBush = this.rBush_;
+ var /** @type {Array.<ol.ModifySegmentDataType>} */ nodesToRemove = [];
+ rBush.forEach(
+ /**
+ * @param {ol.ModifySegmentDataType} node RTree node.
+ */
+ function(node) {
+ if (feature === node.feature) {
+ nodesToRemove.push(node);
+ }
+ });
+ for (var i = nodesToRemove.length - 1; i >= 0; --i) {
+ rBush.remove(nodesToRemove[i]);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Modify.prototype.setActive = function(active) {
+ if (this.vertexFeature_ && !active) {
+ this.overlay_.getSource().removeFeature(this.vertexFeature_);
+ this.vertexFeature_ = null;
+ }
+ ol.interaction.Pointer.prototype.setActive.call(this, active);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Modify.prototype.setMap = function(map) {
+ this.overlay_.setMap(map);
+ ol.interaction.Pointer.prototype.setMap.call(this, map);
+};
+
+
+/**
+ * @param {ol.Collection.Event} evt Event.
+ * @private
+ */
+ol.interaction.Modify.prototype.handleFeatureAdd_ = function(evt) {
+ this.addFeature_(/** @type {ol.Feature} */ (evt.element));
+};
+
+
+/**
+ * @param {ol.events.Event} evt Event.
+ * @private
+ */
+ol.interaction.Modify.prototype.handleFeatureChange_ = function(evt) {
+ if (!this.changingFeature_) {
+ var feature = /** @type {ol.Feature} */ (evt.target);
+ this.removeFeature_(feature);
+ this.addFeature_(feature);
+ }
+};
+
+
+/**
+ * @param {ol.Collection.Event} evt Event.
+ * @private
+ */
+ol.interaction.Modify.prototype.handleFeatureRemove_ = function(evt) {
+ var feature = /** @type {ol.Feature} */ (evt.element);
+ this.removeFeature_(feature);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.Point} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writePointGeometry_ = function(feature, geometry) {
+ var coordinates = geometry.getCoordinates();
+ var segmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ feature: feature,
+ geometry: geometry,
+ segment: [coordinates, coordinates]
+ });
+ this.rBush_.insert(geometry.getExtent(), segmentData);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.MultiPoint} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writeMultiPointGeometry_ = function(feature, geometry) {
+ var points = geometry.getCoordinates();
+ var coordinates, i, ii, segmentData;
+ for (i = 0, ii = points.length; i < ii; ++i) {
+ coordinates = points[i];
+ segmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ feature: feature,
+ geometry: geometry,
+ depth: [i],
+ index: i,
+ segment: [coordinates, coordinates]
+ });
+ this.rBush_.insert(geometry.getExtent(), segmentData);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.LineString} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writeLineStringGeometry_ = function(feature, geometry) {
+ var coordinates = geometry.getCoordinates();
+ var i, ii, segment, segmentData;
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ feature: feature,
+ geometry: geometry,
+ index: i,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.MultiLineString} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) {
+ var lines = geometry.getCoordinates();
+ var coordinates, i, ii, j, jj, segment, segmentData;
+ for (j = 0, jj = lines.length; j < jj; ++j) {
+ coordinates = lines[j];
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ feature: feature,
+ geometry: geometry,
+ depth: [j],
+ index: i,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.Polygon} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writePolygonGeometry_ = function(feature, geometry) {
+ var rings = geometry.getCoordinates();
+ var coordinates, i, ii, j, jj, segment, segmentData;
+ for (j = 0, jj = rings.length; j < jj; ++j) {
+ coordinates = rings[j];
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ feature: feature,
+ geometry: geometry,
+ depth: [j],
+ index: i,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.MultiPolygon} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) {
+ var polygons = geometry.getCoordinates();
+ var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData;
+ for (k = 0, kk = polygons.length; k < kk; ++k) {
+ rings = polygons[k];
+ for (j = 0, jj = rings.length; j < jj; ++j) {
+ coordinates = rings[j];
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ feature: feature,
+ geometry: geometry,
+ depth: [j, k],
+ index: i,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.GeometryCollection} geometry Geometry.
+ * @private
+ */
+ol.interaction.Modify.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) {
+ var i, geometries = geometry.getGeometriesArray();
+ for (i = 0; i < geometries.length; ++i) {
+ this.SEGMENT_WRITERS_[geometries[i].getType()].call(
+ this, feature, geometries[i]);
+ }
+};
+
+
+/**
+ * @param {ol.Coordinate} coordinates Coordinates.
+ * @return {ol.Feature} Vertex feature.
+ * @private
+ */
+ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ = function(coordinates) {
+ var vertexFeature = this.vertexFeature_;
+ if (!vertexFeature) {
+ vertexFeature = new ol.Feature(new ol.geom.Point(coordinates));
+ this.vertexFeature_ = vertexFeature;
+ this.overlay_.getSource().addFeature(vertexFeature);
+ } else {
+ var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry());
+ geometry.setCoordinates(coordinates);
+ }
+ return vertexFeature;
+};
+
+
+/**
+ * @param {ol.ModifySegmentDataType} a The first segment data.
+ * @param {ol.ModifySegmentDataType} b The second segment data.
+ * @return {number} The difference in indexes.
+ * @private
+ */
+ol.interaction.Modify.compareIndexes_ = function(a, b) {
+ return a.index - b.index;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} evt Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.Modify}
+ * @private
+ */
+ol.interaction.Modify.handleDownEvent_ = function(evt) {
+ if (!this.condition_(evt)) {
+ return false;
+ }
+ this.handlePointerAtPixel_(evt.pixel, evt.map);
+ this.dragSegments_.length = 0;
+ this.modified_ = false;
+ var vertexFeature = this.vertexFeature_;
+ if (vertexFeature) {
+ var insertVertices = [];
+ var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry());
+ var vertex = geometry.getCoordinates();
+ var vertexExtent = ol.extent.boundingExtent([vertex]);
+ var segmentDataMatches = this.rBush_.getInExtent(vertexExtent);
+ var componentSegments = {};
+ segmentDataMatches.sort(ol.interaction.Modify.compareIndexes_);
+ for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
+ var segmentDataMatch = segmentDataMatches[i];
+ var segment = segmentDataMatch.segment;
+ var uid = ol.getUid(segmentDataMatch.feature);
+ var depth = segmentDataMatch.depth;
+ if (depth) {
+ uid += '-' + depth.join('-'); // separate feature components
+ }
+ if (!componentSegments[uid]) {
+ componentSegments[uid] = new Array(2);
+ }
+ if (ol.coordinate.equals(segment[0], vertex) &&
+ !componentSegments[uid][0]) {
+ this.dragSegments_.push([segmentDataMatch, 0]);
+ componentSegments[uid][0] = segmentDataMatch;
+ } else if (ol.coordinate.equals(segment[1], vertex) &&
+ !componentSegments[uid][1]) {
+
+ // prevent dragging closed linestrings by the connecting node
+ if ((segmentDataMatch.geometry.getType() ===
+ ol.geom.GeometryType.LINE_STRING ||
+ segmentDataMatch.geometry.getType() ===
+ ol.geom.GeometryType.MULTI_LINE_STRING) &&
+ componentSegments[uid][0] &&
+ componentSegments[uid][0].index === 0) {
+ continue;
+ }
+
+ this.dragSegments_.push([segmentDataMatch, 1]);
+ componentSegments[uid][1] = segmentDataMatch;
+ } else if (ol.getUid(segment) in this.vertexSegments_ &&
+ (!componentSegments[uid][0] && !componentSegments[uid][1])) {
+ insertVertices.push([segmentDataMatch, vertex]);
+ }
+ }
+ if (insertVertices.length) {
+ this.willModifyFeatures_(evt);
+ }
+ for (var j = insertVertices.length - 1; j >= 0; --j) {
+ this.insertVertex_.apply(this, insertVertices[j]);
+ }
+ }
+ return !!this.vertexFeature_;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} evt Event.
+ * @this {ol.interaction.Modify}
+ * @private
+ */
+ol.interaction.Modify.handleDragEvent_ = function(evt) {
+ this.ignoreNextSingleClick_ = false;
+ this.willModifyFeatures_(evt);
+
+ var vertex = evt.coordinate;
+ for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
+ var dragSegment = this.dragSegments_[i];
+ var segmentData = dragSegment[0];
+ var depth = segmentData.depth;
+ var geometry = segmentData.geometry;
+ var coordinates = geometry.getCoordinates();
+ var segment = segmentData.segment;
+ var index = dragSegment[1];
+
+ while (vertex.length < geometry.getStride()) {
+ vertex.push(segment[index][vertex.length]);
+ }
+
+ switch (geometry.getType()) {
+ case ol.geom.GeometryType.POINT:
+ coordinates = vertex;
+ segment[0] = segment[1] = vertex;
+ break;
+ case ol.geom.GeometryType.MULTI_POINT:
+ coordinates[segmentData.index] = vertex;
+ segment[0] = segment[1] = vertex;
+ break;
+ case ol.geom.GeometryType.LINE_STRING:
+ coordinates[segmentData.index + index] = vertex;
+ segment[index] = vertex;
+ break;
+ case ol.geom.GeometryType.MULTI_LINE_STRING:
+ coordinates[depth[0]][segmentData.index + index] = vertex;
+ segment[index] = vertex;
+ break;
+ case ol.geom.GeometryType.POLYGON:
+ coordinates[depth[0]][segmentData.index + index] = vertex;
+ segment[index] = vertex;
+ break;
+ case ol.geom.GeometryType.MULTI_POLYGON:
+ coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex;
+ segment[index] = vertex;
+ break;
+ default:
+ // pass
+ }
+
+ this.setGeometryCoordinates_(geometry, coordinates);
+ }
+ this.createOrUpdateVertexFeature_(vertex);
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} evt Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.Modify}
+ * @private
+ */
+ol.interaction.Modify.handleUpEvent_ = function(evt) {
+ var segmentData;
+ for (var i = this.dragSegments_.length - 1; i >= 0; --i) {
+ segmentData = this.dragSegments_[i][0];
+ this.rBush_.update(ol.extent.boundingExtent(segmentData.segment),
+ segmentData);
+ }
+ if (this.modified_) {
+ this.dispatchEvent(new ol.interaction.Modify.Event(
+ ol.interaction.Modify.EventType.MODIFYEND, this.features_, evt));
+ this.modified_ = false;
+ }
+ return false;
+};
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} and may modify the
+ * geometry.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.Modify}
+ * @api
+ */
+ol.interaction.Modify.handleEvent = function(mapBrowserEvent) {
+ if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) {
+ return true;
+ }
+ this.lastPointerEvent_ = mapBrowserEvent;
+
+ var handled;
+ if (!mapBrowserEvent.map.getView().getHints()[ol.View.Hint.INTERACTING] &&
+ mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE &&
+ !this.handlingDownUpSequence) {
+ this.handlePointerMove_(mapBrowserEvent);
+ }
+ if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) {
+ if (mapBrowserEvent.type != ol.MapBrowserEvent.EventType.SINGLECLICK ||
+ !this.ignoreNextSingleClick_) {
+ handled = this.removePoint();
+ } else {
+ handled = true;
+ }
+ }
+
+ if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.SINGLECLICK) {
+ this.ignoreNextSingleClick_ = false;
+ }
+
+ return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) &&
+ !handled;
+};
+
+
+/**
+ * @param {ol.MapBrowserEvent} evt Event.
+ * @private
+ */
+ol.interaction.Modify.prototype.handlePointerMove_ = function(evt) {
+ this.lastPixel_ = evt.pixel;
+ this.handlePointerAtPixel_(evt.pixel, evt.map);
+};
+
+
+/**
+ * @param {ol.Pixel} pixel Pixel
+ * @param {ol.Map} map Map.
+ * @private
+ */
+ol.interaction.Modify.prototype.handlePointerAtPixel_ = function(pixel, map) {
+ var pixelCoordinate = map.getCoordinateFromPixel(pixel);
+ var sortByDistance = function(a, b) {
+ return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) -
+ ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment);
+ };
+
+ var box = ol.extent.buffer(
+ ol.extent.createOrUpdateFromCoordinate(pixelCoordinate),
+ map.getView().getResolution() * this.pixelTolerance_);
+
+ var rBush = this.rBush_;
+ var nodes = rBush.getInExtent(box);
+ if (nodes.length > 0) {
+ nodes.sort(sortByDistance);
+ var node = nodes[0];
+ var closestSegment = node.segment;
+ var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate,
+ closestSegment));
+ var vertexPixel = map.getPixelFromCoordinate(vertex);
+ if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <=
+ this.pixelTolerance_) {
+ var pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
+ var pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
+ var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1);
+ var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2);
+ var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
+ this.snappedToVertex_ = dist <= this.pixelTolerance_;
+ if (this.snappedToVertex_) {
+ vertex = squaredDist1 > squaredDist2 ?
+ closestSegment[1] : closestSegment[0];
+ }
+ this.createOrUpdateVertexFeature_(vertex);
+ var vertexSegments = {};
+ vertexSegments[ol.getUid(closestSegment)] = true;
+ var segment;
+ for (var i = 1, ii = nodes.length; i < ii; ++i) {
+ segment = nodes[i].segment;
+ if ((ol.coordinate.equals(closestSegment[0], segment[0]) &&
+ ol.coordinate.equals(closestSegment[1], segment[1]) ||
+ (ol.coordinate.equals(closestSegment[0], segment[1]) &&
+ ol.coordinate.equals(closestSegment[1], segment[0])))) {
+ vertexSegments[ol.getUid(segment)] = true;
+ } else {
+ break;
+ }
+ }
+ this.vertexSegments_ = vertexSegments;
+ return;
+ }
+ }
+ if (this.vertexFeature_) {
+ this.overlay_.getSource().removeFeature(this.vertexFeature_);
+ this.vertexFeature_ = null;
+ }
+};
+
+
+/**
+ * @param {ol.ModifySegmentDataType} segmentData Segment data.
+ * @param {ol.Coordinate} vertex Vertex.
+ * @private
+ */
+ol.interaction.Modify.prototype.insertVertex_ = function(segmentData, vertex) {
+ var segment = segmentData.segment;
+ var feature = segmentData.feature;
+ var geometry = segmentData.geometry;
+ var depth = segmentData.depth;
+ var index = /** @type {number} */ (segmentData.index);
+ var coordinates;
+
+ while (vertex.length < geometry.getStride()) {
+ vertex.push(0);
+ }
+
+ switch (geometry.getType()) {
+ case ol.geom.GeometryType.MULTI_LINE_STRING:
+ coordinates = geometry.getCoordinates();
+ coordinates[depth[0]].splice(index + 1, 0, vertex);
+ break;
+ case ol.geom.GeometryType.POLYGON:
+ coordinates = geometry.getCoordinates();
+ coordinates[depth[0]].splice(index + 1, 0, vertex);
+ break;
+ case ol.geom.GeometryType.MULTI_POLYGON:
+ coordinates = geometry.getCoordinates();
+ coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex);
+ break;
+ case ol.geom.GeometryType.LINE_STRING:
+ coordinates = geometry.getCoordinates();
+ coordinates.splice(index + 1, 0, vertex);
+ break;
+ default:
+ return;
+ }
+
+ this.setGeometryCoordinates_(geometry, coordinates);
+ var rTree = this.rBush_;
+ rTree.remove(segmentData);
+ this.updateSegmentIndices_(geometry, index, depth, 1);
+ var newSegmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ segment: [segment[0], vertex],
+ feature: feature,
+ geometry: geometry,
+ depth: depth,
+ index: index
+ });
+ rTree.insert(ol.extent.boundingExtent(newSegmentData.segment),
+ newSegmentData);
+ this.dragSegments_.push([newSegmentData, 1]);
+
+ var newSegmentData2 = /** @type {ol.ModifySegmentDataType} */ ({
+ segment: [vertex, segment[1]],
+ feature: feature,
+ geometry: geometry,
+ depth: depth,
+ index: index + 1
+ });
+ rTree.insert(ol.extent.boundingExtent(newSegmentData2.segment),
+ newSegmentData2);
+ this.dragSegments_.push([newSegmentData2, 0]);
+ this.ignoreNextSingleClick_ = true;
+};
+
+/**
+ * Removes the vertex currently being pointed.
+ * @return {boolean} True when a vertex was removed.
+ * @api
+ */
+ol.interaction.Modify.prototype.removePoint = function() {
+ if (this.lastPointerEvent_ && this.lastPointerEvent_.type != ol.MapBrowserEvent.EventType.POINTERDRAG) {
+ var evt = this.lastPointerEvent_;
+ this.willModifyFeatures_(evt);
+ this.removeVertex_();
+ this.dispatchEvent(new ol.interaction.Modify.Event(
+ ol.interaction.Modify.EventType.MODIFYEND, this.features_, evt));
+ this.modified_ = false;
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Removes a vertex from all matching features.
+ * @return {boolean} True when a vertex was removed.
+ * @private
+ */
+ol.interaction.Modify.prototype.removeVertex_ = function() {
+ var dragSegments = this.dragSegments_;
+ var segmentsByFeature = {};
+ var deleted = false;
+ var component, coordinates, dragSegment, geometry, i, index, left;
+ var newIndex, right, segmentData, uid;
+ for (i = dragSegments.length - 1; i >= 0; --i) {
+ dragSegment = dragSegments[i];
+ segmentData = dragSegment[0];
+ uid = ol.getUid(segmentData.feature);
+ if (segmentData.depth) {
+ // separate feature components
+ uid += '-' + segmentData.depth.join('-');
+ }
+ if (!(uid in segmentsByFeature)) {
+ segmentsByFeature[uid] = {};
+ }
+ if (dragSegment[1] === 0) {
+ segmentsByFeature[uid].right = segmentData;
+ segmentsByFeature[uid].index = segmentData.index;
+ } else if (dragSegment[1] == 1) {
+ segmentsByFeature[uid].left = segmentData;
+ segmentsByFeature[uid].index = segmentData.index + 1;
+ }
+
+ }
+ for (uid in segmentsByFeature) {
+ right = segmentsByFeature[uid].right;
+ left = segmentsByFeature[uid].left;
+ index = segmentsByFeature[uid].index;
+ newIndex = index - 1;
+ if (left !== undefined) {
+ segmentData = left;
+ } else {
+ segmentData = right;
+ }
+ if (newIndex < 0) {
+ newIndex = 0;
+ }
+ geometry = segmentData.geometry;
+ coordinates = geometry.getCoordinates();
+ component = coordinates;
+ deleted = false;
+ switch (geometry.getType()) {
+ case ol.geom.GeometryType.MULTI_LINE_STRING:
+ if (coordinates[segmentData.depth[0]].length > 2) {
+ coordinates[segmentData.depth[0]].splice(index, 1);
+ deleted = true;
+ }
+ break;
+ case ol.geom.GeometryType.LINE_STRING:
+ if (coordinates.length > 2) {
+ coordinates.splice(index, 1);
+ deleted = true;
+ }
+ break;
+ case ol.geom.GeometryType.MULTI_POLYGON:
+ component = component[segmentData.depth[1]];
+ /* falls through */
+ case ol.geom.GeometryType.POLYGON:
+ component = component[segmentData.depth[0]];
+ if (component.length > 4) {
+ if (index == component.length - 1) {
+ index = 0;
+ }
+ component.splice(index, 1);
+ deleted = true;
+ if (index === 0) {
+ // close the ring again
+ component.pop();
+ component.push(component[0]);
+ newIndex = component.length - 1;
+ }
+ }
+ break;
+ default:
+ // pass
+ }
+
+ if (deleted) {
+ this.setGeometryCoordinates_(geometry, coordinates);
+ var segments = [];
+ if (left !== undefined) {
+ this.rBush_.remove(left);
+ segments.push(left.segment[0]);
+ }
+ if (right !== undefined) {
+ this.rBush_.remove(right);
+ segments.push(right.segment[1]);
+ }
+ if (left !== undefined && right !== undefined) {
+ ol.DEBUG && console.assert(newIndex >= 0, 'newIndex should be larger than 0');
+
+ var newSegmentData = /** @type {ol.ModifySegmentDataType} */ ({
+ depth: segmentData.depth,
+ feature: segmentData.feature,
+ geometry: segmentData.geometry,
+ index: newIndex,
+ segment: segments
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(newSegmentData.segment),
+ newSegmentData);
+ }
+ this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
+ if (this.vertexFeature_) {
+ this.overlay_.getSource().removeFeature(this.vertexFeature_);
+ this.vertexFeature_ = null;
+ }
+ }
+
+ }
+ return deleted;
+};
+
+
+/**
+ * @param {ol.geom.SimpleGeometry} geometry Geometry.
+ * @param {Array} coordinates Coordinates.
+ * @private
+ */
+ol.interaction.Modify.prototype.setGeometryCoordinates_ = function(geometry, coordinates) {
+ this.changingFeature_ = true;
+ geometry.setCoordinates(coordinates);
+ this.changingFeature_ = false;
+};
+
+
+/**
+ * @param {ol.geom.SimpleGeometry} geometry Geometry.
+ * @param {number} index Index.
+ * @param {Array.<number>|undefined} depth Depth.
+ * @param {number} delta Delta (1 or -1).
+ * @private
+ */
+ol.interaction.Modify.prototype.updateSegmentIndices_ = function(
+ geometry, index, depth, delta) {
+ this.rBush_.forEachInExtent(geometry.getExtent(), function(segmentDataMatch) {
+ if (segmentDataMatch.geometry === geometry &&
+ (depth === undefined || segmentDataMatch.depth === undefined ||
+ ol.array.equals(segmentDataMatch.depth, depth)) &&
+ segmentDataMatch.index > index) {
+ segmentDataMatch.index += delta;
+ }
+ });
+};
+
+
+/**
+ * @return {ol.StyleFunction} Styles.
+ */
+ol.interaction.Modify.getDefaultStyleFunction = function() {
+ var style = ol.style.Style.createDefaultEditing();
+ return function(feature, resolution) {
+ return style[ol.geom.GeometryType.POINT];
+ };
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.Modify} instances are instances of
+ * this type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.ModifyEvent}
+ * @param {ol.interaction.Modify.EventType} type Type.
+ * @param {ol.Collection.<ol.Feature>} features The features modified.
+ * @param {ol.MapBrowserPointerEvent} mapBrowserPointerEvent Associated
+ * {@link ol.MapBrowserPointerEvent}.
+ */
+ol.interaction.Modify.Event = function(type, features, mapBrowserPointerEvent) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The features being modified.
+ * @type {ol.Collection.<ol.Feature>}
+ * @api
+ */
+ this.features = features;
+
+ /**
+ * Associated {@link ol.MapBrowserEvent}.
+ * @type {ol.MapBrowserEvent}
+ * @api
+ */
+ this.mapBrowserEvent = mapBrowserPointerEvent;
+};
+ol.inherits(ol.interaction.Modify.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.Modify.EventType = {
+ /**
+ * Triggered upon feature modification start
+ * @event ol.interaction.Modify.Event#modifystart
+ * @api
+ */
+ MODIFYSTART: 'modifystart',
+ /**
+ * Triggered upon feature modification end
+ * @event ol.interaction.Modify.Event#modifyend
+ * @api
+ */
+ MODIFYEND: 'modifyend'
+};
+
+goog.provide('ol.interaction.Select');
+
+goog.require('ol');
+goog.require('ol.functions');
+goog.require('ol.Collection');
+goog.require('ol.array');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.condition');
+goog.require('ol.geom.GeometryType');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.layer.Vector');
+goog.require('ol.obj');
+goog.require('ol.source.Vector');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * Interaction for selecting vector features. By default, selected features are
+ * styled differently, so this interaction can be used for visual highlighting,
+ * as well as selecting features for other actions, such as modification or
+ * output. There are three ways of controlling which features are selected:
+ * using the browser event as defined by the `condition` and optionally the
+ * `toggle`, `add`/`remove`, and `multi` options; a `layers` filter; and a
+ * further feature filter using the `filter` option.
+ *
+ * Selected features are added to an internal unmanaged layer.
+ *
+ * @constructor
+ * @extends {ol.interaction.Interaction}
+ * @param {olx.interaction.SelectOptions=} opt_options Options.
+ * @fires ol.interaction.Select.Event
+ * @api stable
+ */
+ol.interaction.Select = function(opt_options) {
+
+ ol.interaction.Interaction.call(this, {
+ handleEvent: ol.interaction.Select.handleEvent
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.condition_ = options.condition ?
+ options.condition : ol.events.condition.singleClick;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.addCondition_ = options.addCondition ?
+ options.addCondition : ol.events.condition.never;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.removeCondition_ = options.removeCondition ?
+ options.removeCondition : ol.events.condition.never;
+
+ /**
+ * @private
+ * @type {ol.EventsConditionType}
+ */
+ this.toggleCondition_ = options.toggleCondition ?
+ options.toggleCondition : ol.events.condition.shiftKeyOnly;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.multi_ = options.multi ? options.multi : false;
+
+ /**
+ * @private
+ * @type {ol.SelectFilterFunction}
+ */
+ this.filter_ = options.filter ? options.filter :
+ ol.functions.TRUE;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
+
+ var featureOverlay = new ol.layer.Vector({
+ source: new ol.source.Vector({
+ useSpatialIndex: false,
+ features: options.features,
+ wrapX: options.wrapX
+ }),
+ style: options.style ? options.style :
+ ol.interaction.Select.getDefaultStyleFunction(),
+ updateWhileAnimating: true,
+ updateWhileInteracting: true
+ });
+
+ /**
+ * @private
+ * @type {ol.layer.Vector}
+ */
+ this.featureOverlay_ = featureOverlay;
+
+ /** @type {function(ol.layer.Layer): boolean} */
+ var layerFilter;
+ if (options.layers) {
+ if (typeof options.layers === 'function') {
+ layerFilter = options.layers;
+ } else {
+ var layers = options.layers;
+ layerFilter = function(layer) {
+ return ol.array.includes(layers, layer);
+ };
+ }
+ } else {
+ layerFilter = ol.functions.TRUE;
+ }
+
+ /**
+ * @private
+ * @type {function(ol.layer.Layer): boolean}
+ */
+ this.layerFilter_ = layerFilter;
+
+ /**
+ * An association between selected feature (key)
+ * and layer (value)
+ * @private
+ * @type {Object.<number, ol.layer.Layer>}
+ */
+ this.featureLayerAssociation_ = {};
+
+ var features = this.featureOverlay_.getSource().getFeaturesCollection();
+ ol.events.listen(features, ol.Collection.EventType.ADD,
+ this.addFeature_, this);
+ ol.events.listen(features, ol.Collection.EventType.REMOVE,
+ this.removeFeature_, this);
+
+};
+ol.inherits(ol.interaction.Select, ol.interaction.Interaction);
+
+
+/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {ol.layer.Layer} layer Layer.
+ * @private
+ */
+ol.interaction.Select.prototype.addFeatureLayerAssociation_ = function(feature, layer) {
+ var key = ol.getUid(feature);
+ this.featureLayerAssociation_[key] = layer;
+};
+
+
+/**
+ * Get the selected features.
+ * @return {ol.Collection.<ol.Feature>} Features collection.
+ * @api stable
+ */
+ol.interaction.Select.prototype.getFeatures = function() {
+ return this.featureOverlay_.getSource().getFeaturesCollection();
+};
+
+
+/**
+ * Returns the Hit-detection tolerance.
+ * @returns {number} Hit tolerance in pixels.
+ * @api
+ */
+ol.interaction.Select.prototype.getHitTolerance = function() {
+ return this.hitTolerance_;
+};
+
+
+/**
+ * Returns the associated {@link ol.layer.Vector vectorlayer} of
+ * the (last) selected feature. Note that this will not work with any
+ * programmatic method like pushing features to
+ * {@link ol.interaction.Select#getFeatures collection}.
+ * @param {ol.Feature|ol.render.Feature} feature Feature
+ * @return {ol.layer.Vector} Layer.
+ * @api
+ */
+ol.interaction.Select.prototype.getLayer = function(feature) {
+ var key = ol.getUid(feature);
+ return /** @type {ol.layer.Vector} */ (this.featureLayerAssociation_[key]);
+};
+
+
+/**
+ * Handles the {@link ol.MapBrowserEvent map browser event} and may change the
+ * selected state of features.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
+ * @return {boolean} `false` to stop event propagation.
+ * @this {ol.interaction.Select}
+ * @api
+ */
+ol.interaction.Select.handleEvent = function(mapBrowserEvent) {
+ if (!this.condition_(mapBrowserEvent)) {
+ return true;
+ }
+ var add = this.addCondition_(mapBrowserEvent);
+ var remove = this.removeCondition_(mapBrowserEvent);
+ var toggle = this.toggleCondition_(mapBrowserEvent);
+ var set = !add && !remove && !toggle;
+ var map = mapBrowserEvent.map;
+ var features = this.featureOverlay_.getSource().getFeaturesCollection();
+ var deselected = [];
+ var selected = [];
+ if (set) {
+ // Replace the currently selected feature(s) with the feature(s) at the
+ // pixel, or clear the selected feature(s) if there is no feature at
+ // the pixel.
+ ol.obj.clear(this.featureLayerAssociation_);
+ map.forEachFeatureAtPixel(mapBrowserEvent.pixel,
+ (/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {ol.layer.Layer} layer Layer.
+ * @return {boolean|undefined} Continue to iterate over the features.
+ */
+ function(feature, layer) {
+ if (this.filter_(feature, layer)) {
+ selected.push(feature);
+ this.addFeatureLayerAssociation_(feature, layer);
+ return !this.multi_;
+ }
+ }).bind(this), {
+ layerFilter: this.layerFilter_,
+ hitTolerance: this.hitTolerance_
+ });
+ var i;
+ for (i = features.getLength() - 1; i >= 0; --i) {
+ var feature = features.item(i);
+ var index = selected.indexOf(feature);
+ if (index > -1) {
+ // feature is already selected
+ selected.splice(index, 1);
+ } else {
+ features.remove(feature);
+ deselected.push(feature);
+ }
+ }
+ if (selected.length !== 0) {
+ features.extend(selected);
+ }
+ } else {
+ // Modify the currently selected feature(s).
+ map.forEachFeatureAtPixel(mapBrowserEvent.pixel,
+ (/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @param {ol.layer.Layer} layer Layer.
+ * @return {boolean|undefined} Continue to iterate over the features.
+ */
+ function(feature, layer) {
+ if (this.filter_(feature, layer)) {
+ if ((add || toggle) &&
+ !ol.array.includes(features.getArray(), feature)) {
+ selected.push(feature);
+ this.addFeatureLayerAssociation_(feature, layer);
+ } else if ((remove || toggle) &&
+ ol.array.includes(features.getArray(), feature)) {
+ deselected.push(feature);
+ this.removeFeatureLayerAssociation_(feature);
+ }
+ return !this.multi_;
+ }
+ }).bind(this), {
+ layerFilter: this.layerFilter_,
+ hitTolerance: this.hitTolerance_
+ });
+ var j;
+ for (j = deselected.length - 1; j >= 0; --j) {
+ features.remove(deselected[j]);
+ }
+ features.extend(selected);
+ }
+ if (selected.length > 0 || deselected.length > 0) {
+ this.dispatchEvent(
+ new ol.interaction.Select.Event(ol.interaction.Select.EventType.SELECT,
+ selected, deselected, mapBrowserEvent));
+ }
+ return ol.events.condition.pointerMove(mapBrowserEvent);
+};
+
+
+/**
+ * Hit-detection tolerance. Pixels inside the radius around the given position
+ * will be checked for features. This only works for the canvas renderer and
+ * not for WebGL.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @api
+ */
+ol.interaction.Select.prototype.setHitTolerance = function(hitTolerance) {
+ this.hitTolerance_ = hitTolerance;
+};
+
+
+/**
+ * Remove the interaction from its current map, if any, and attach it to a new
+ * map, if any. Pass `null` to just remove the interaction from the current map.
+ * @param {ol.Map} map Map.
+ * @api stable
+ */
+ol.interaction.Select.prototype.setMap = function(map) {
+ var currentMap = this.getMap();
+ var selectedFeatures =
+ this.featureOverlay_.getSource().getFeaturesCollection();
+ if (currentMap) {
+ selectedFeatures.forEach(currentMap.unskipFeature, currentMap);
+ }
+ ol.interaction.Interaction.prototype.setMap.call(this, map);
+ this.featureOverlay_.setMap(map);
+ if (map) {
+ selectedFeatures.forEach(map.skipFeature, map);
+ }
+};
+
+
+/**
+ * @return {ol.StyleFunction} Styles.
+ */
+ol.interaction.Select.getDefaultStyleFunction = function() {
+ var styles = ol.style.Style.createDefaultEditing();
+ ol.array.extend(styles[ol.geom.GeometryType.POLYGON],
+ styles[ol.geom.GeometryType.LINE_STRING]);
+ ol.array.extend(styles[ol.geom.GeometryType.GEOMETRY_COLLECTION],
+ styles[ol.geom.GeometryType.LINE_STRING]);
+
+ return function(feature, resolution) {
+ if (!feature.getGeometry()) {
+ return null;
+ }
+ return styles[feature.getGeometry().getType()];
+ };
+};
+
+
+/**
+ * @param {ol.Collection.Event} evt Event.
+ * @private
+ */
+ol.interaction.Select.prototype.addFeature_ = function(evt) {
+ var map = this.getMap();
+ if (map) {
+ map.skipFeature(/** @type {ol.Feature} */ (evt.element));
+ }
+};
+
+
+/**
+ * @param {ol.Collection.Event} evt Event.
+ * @private
+ */
+ol.interaction.Select.prototype.removeFeature_ = function(evt) {
+ var map = this.getMap();
+ if (map) {
+ map.unskipFeature(/** @type {ol.Feature} */ (evt.element));
+ }
+};
+
+
+/**
+ * @param {ol.Feature|ol.render.Feature} feature Feature.
+ * @private
+ */
+ol.interaction.Select.prototype.removeFeatureLayerAssociation_ = function(feature) {
+ var key = ol.getUid(feature);
+ delete this.featureLayerAssociation_[key];
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.Select} instances are instances of
+ * this type.
+ *
+ * @param {ol.interaction.Select.EventType} type The event type.
+ * @param {Array.<ol.Feature>} selected Selected features.
+ * @param {Array.<ol.Feature>} deselected Deselected features.
+ * @param {ol.MapBrowserEvent} mapBrowserEvent Associated
+ * {@link ol.MapBrowserEvent}.
+ * @implements {oli.SelectEvent}
+ * @extends {ol.events.Event}
+ * @constructor
+ */
+ol.interaction.Select.Event = function(type, selected, deselected, mapBrowserEvent) {
+ ol.events.Event.call(this, type);
+
+ /**
+ * Selected features array.
+ * @type {Array.<ol.Feature>}
+ * @api
+ */
+ this.selected = selected;
+
+ /**
+ * Deselected features array.
+ * @type {Array.<ol.Feature>}
+ * @api
+ */
+ this.deselected = deselected;
+
+ /**
+ * Associated {@link ol.MapBrowserEvent}.
+ * @type {ol.MapBrowserEvent}
+ * @api
+ */
+ this.mapBrowserEvent = mapBrowserEvent;
+};
+ol.inherits(ol.interaction.Select.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.Select.EventType = {
+ /**
+ * Triggered when feature(s) has been (de)selected.
+ * @event ol.interaction.Select.Event#select
+ * @api
+ */
+ SELECT: 'select'
+};
+
+goog.provide('ol.interaction.Snap');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.Object');
+goog.require('ol.Observable');
+goog.require('ol.coordinate');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.interaction.Pointer');
+goog.require('ol.functions');
+goog.require('ol.obj');
+goog.require('ol.source.Vector');
+goog.require('ol.structs.RBush');
+
+
+/**
+ * @classdesc
+ * Handles snapping of vector features while modifying or drawing them. The
+ * features can come from a {@link ol.source.Vector} or {@link ol.Collection}
+ * Any interaction object that allows the user to interact
+ * with the features using the mouse can benefit from the snapping, as long
+ * as it is added before.
+ *
+ * The snap interaction modifies map browser event `coordinate` and `pixel`
+ * properties to force the snap to occur to any interaction that them.
+ *
+ * Example:
+ *
+ * var snap = new ol.interaction.Snap({
+ * source: source
+ * });
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @param {olx.interaction.SnapOptions=} opt_options Options.
+ * @api
+ */
+ol.interaction.Snap = function(opt_options) {
+
+ ol.interaction.Pointer.call(this, {
+ handleEvent: ol.interaction.Snap.handleEvent_,
+ handleDownEvent: ol.functions.TRUE,
+ handleUpEvent: ol.interaction.Snap.handleUpEvent_
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @type {ol.source.Vector}
+ * @private
+ */
+ this.source_ = options.source ? options.source : null;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.vertex_ = options.vertex !== undefined ? options.vertex : true;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.edge_ = options.edge !== undefined ? options.edge : true;
+
+ /**
+ * @type {ol.Collection.<ol.Feature>}
+ * @private
+ */
+ this.features_ = options.features ? options.features : null;
+
+ /**
+ * @type {Array.<ol.EventsKey>}
+ * @private
+ */
+ this.featuresListenerKeys_ = [];
+
+ /**
+ * @type {Object.<number, ol.EventsKey>}
+ * @private
+ */
+ this.geometryChangeListenerKeys_ = {};
+
+ /**
+ * @type {Object.<number, ol.EventsKey>}
+ * @private
+ */
+ this.geometryModifyListenerKeys_ = {};
+
+ /**
+ * Extents are preserved so indexed segment can be quickly removed
+ * when its feature geometry changes
+ * @type {Object.<number, ol.Extent>}
+ * @private
+ */
+ this.indexedFeaturesExtents_ = {};
+
+ /**
+ * If a feature geometry changes while a pointer drag|move event occurs, the
+ * feature doesn't get updated right away. It will be at the next 'pointerup'
+ * event fired.
+ * @type {Object.<number, ol.Feature>}
+ * @private
+ */
+ this.pendingFeatures_ = {};
+
+ /**
+ * Used for distance sorting in sortByDistance_
+ * @type {ol.Coordinate}
+ * @private
+ */
+ this.pixelCoordinate_ = null;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.pixelTolerance_ = options.pixelTolerance !== undefined ?
+ options.pixelTolerance : 10;
+
+ /**
+ * @type {function(ol.SnapSegmentDataType, ol.SnapSegmentDataType): number}
+ * @private
+ */
+ this.sortByDistance_ = ol.interaction.Snap.sortByDistance.bind(this);
+
+
+ /**
+ * Segment RTree for each layer
+ * @type {ol.structs.RBush.<ol.SnapSegmentDataType>}
+ * @private
+ */
+ this.rBush_ = new ol.structs.RBush();
+
+
+ /**
+ * @const
+ * @private
+ * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)>}
+ */
+ this.SEGMENT_WRITERS_ = {
+ 'Point': this.writePointGeometry_,
+ 'LineString': this.writeLineStringGeometry_,
+ 'LinearRing': this.writeLineStringGeometry_,
+ 'Polygon': this.writePolygonGeometry_,
+ 'MultiPoint': this.writeMultiPointGeometry_,
+ 'MultiLineString': this.writeMultiLineStringGeometry_,
+ 'MultiPolygon': this.writeMultiPolygonGeometry_,
+ 'GeometryCollection': this.writeGeometryCollectionGeometry_
+ };
+};
+ol.inherits(ol.interaction.Snap, ol.interaction.Pointer);
+
+
+/**
+ * Add a feature to the collection of features that we may snap to.
+ * @param {ol.Feature} feature Feature.
+ * @param {boolean=} opt_listen Whether to listen to the geometry change or not
+ * Defaults to `true`.
+ * @api
+ */
+ol.interaction.Snap.prototype.addFeature = function(feature, opt_listen) {
+ var listen = opt_listen !== undefined ? opt_listen : true;
+ var feature_uid = ol.getUid(feature);
+ var geometry = feature.getGeometry();
+ if (geometry) {
+ var segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()];
+ if (segmentWriter) {
+ this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(
+ ol.extent.createEmpty());
+ segmentWriter.call(this, feature, geometry);
+
+ if (listen) {
+ this.geometryModifyListenerKeys_[feature_uid] = ol.events.listen(
+ geometry,
+ ol.events.EventType.CHANGE,
+ this.handleGeometryModify_.bind(this, feature),
+ this);
+ }
+ }
+ }
+
+ if (listen) {
+ this.geometryChangeListenerKeys_[feature_uid] = ol.events.listen(
+ feature,
+ ol.Object.getChangeEventType(feature.getGeometryName()),
+ this.handleGeometryChange_, this);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.interaction.Snap.prototype.forEachFeatureAdd_ = function(feature) {
+ this.addFeature(feature);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature.
+ * @private
+ */
+ol.interaction.Snap.prototype.forEachFeatureRemove_ = function(feature) {
+ this.removeFeature(feature);
+};
+
+
+/**
+ * @return {ol.Collection.<ol.Feature>|Array.<ol.Feature>} Features.
+ * @private
+ */
+ol.interaction.Snap.prototype.getFeatures_ = function() {
+ var features;
+ if (this.features_) {
+ features = this.features_;
+ } else if (this.source_) {
+ features = this.source_.getFeatures();
+ }
+ return /** @type {!Array.<ol.Feature>|!ol.Collection.<ol.Feature>} */ (features);
+};
+
+
+/**
+ * @param {ol.source.Vector.Event|ol.Collection.Event} evt Event.
+ * @private
+ */
+ol.interaction.Snap.prototype.handleFeatureAdd_ = function(evt) {
+ var feature;
+ if (evt instanceof ol.source.Vector.Event) {
+ feature = evt.feature;
+ } else if (evt instanceof ol.Collection.Event) {
+ feature = evt.element;
+ }
+ this.addFeature(/** @type {ol.Feature} */ (feature));
+};
+
+
+/**
+ * @param {ol.source.Vector.Event|ol.Collection.Event} evt Event.
+ * @private
+ */
+ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) {
+ var feature;
+ if (evt instanceof ol.source.Vector.Event) {
+ feature = evt.feature;
+ } else if (evt instanceof ol.Collection.Event) {
+ feature = evt.element;
+ }
+ this.removeFeature(/** @type {ol.Feature} */ (feature));
+};
+
+
+/**
+ * @param {ol.events.Event} evt Event.
+ * @private
+ */
+ol.interaction.Snap.prototype.handleGeometryChange_ = function(evt) {
+ var feature = /** @type {ol.Feature} */ (evt.target);
+ this.removeFeature(feature, true);
+ this.addFeature(feature, true);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature which geometry was modified.
+ * @param {ol.events.Event} evt Event.
+ * @private
+ */
+ol.interaction.Snap.prototype.handleGeometryModify_ = function(feature, evt) {
+ if (this.handlingDownUpSequence) {
+ var uid = ol.getUid(feature);
+ if (!(uid in this.pendingFeatures_)) {
+ this.pendingFeatures_[uid] = feature;
+ }
+ } else {
+ this.updateFeature_(feature);
+ }
+};
+
+
+/**
+ * Remove a feature from the collection of features that we may snap to.
+ * @param {ol.Feature} feature Feature
+ * @param {boolean=} opt_unlisten Whether to unlisten to the geometry change
+ * or not. Defaults to `true`.
+ * @api
+ */
+ol.interaction.Snap.prototype.removeFeature = function(feature, opt_unlisten) {
+ var unlisten = opt_unlisten !== undefined ? opt_unlisten : true;
+ var feature_uid = ol.getUid(feature);
+ var extent = this.indexedFeaturesExtents_[feature_uid];
+ if (extent) {
+ var rBush = this.rBush_;
+ var i, nodesToRemove = [];
+ rBush.forEachInExtent(extent, function(node) {
+ if (feature === node.feature) {
+ nodesToRemove.push(node);
+ }
+ });
+ for (i = nodesToRemove.length - 1; i >= 0; --i) {
+ rBush.remove(nodesToRemove[i]);
+ }
+ if (unlisten) {
+ ol.Observable.unByKey(this.geometryModifyListenerKeys_[feature_uid]);
+ delete this.geometryModifyListenerKeys_[feature_uid];
+ }
+ }
+
+ if (unlisten) {
+ ol.Observable.unByKey(this.geometryChangeListenerKeys_[feature_uid]);
+ delete this.geometryChangeListenerKeys_[feature_uid];
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Snap.prototype.setMap = function(map) {
+ var currentMap = this.getMap();
+ var keys = this.featuresListenerKeys_;
+ var features = this.getFeatures_();
+
+ if (currentMap) {
+ keys.forEach(ol.Observable.unByKey);
+ keys.length = 0;
+ features.forEach(this.forEachFeatureRemove_, this);
+ }
+ ol.interaction.Pointer.prototype.setMap.call(this, map);
+
+ if (map) {
+ if (this.features_) {
+ keys.push(
+ ol.events.listen(this.features_, ol.Collection.EventType.ADD,
+ this.handleFeatureAdd_, this),
+ ol.events.listen(this.features_, ol.Collection.EventType.REMOVE,
+ this.handleFeatureRemove_, this)
+ );
+ } else if (this.source_) {
+ keys.push(
+ ol.events.listen(this.source_, ol.source.Vector.EventType.ADDFEATURE,
+ this.handleFeatureAdd_, this),
+ ol.events.listen(this.source_, ol.source.Vector.EventType.REMOVEFEATURE,
+ this.handleFeatureRemove_, this)
+ );
+ }
+ features.forEach(this.forEachFeatureAdd_, this);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.interaction.Snap.prototype.shouldStopEvent = ol.functions.FALSE;
+
+
+/**
+ * @param {ol.Pixel} pixel Pixel
+ * @param {ol.Coordinate} pixelCoordinate Coordinate
+ * @param {ol.Map} map Map.
+ * @return {ol.SnapResultType} Snap result
+ */
+ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) {
+
+ var lowerLeft = map.getCoordinateFromPixel(
+ [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]);
+ var upperRight = map.getCoordinateFromPixel(
+ [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]);
+ var box = ol.extent.boundingExtent([lowerLeft, upperRight]);
+
+ var segments = this.rBush_.getInExtent(box);
+ var snappedToVertex = false;
+ var snapped = false;
+ var vertex = null;
+ var vertexPixel = null;
+ var dist, pixel1, pixel2, squaredDist1, squaredDist2;
+ if (segments.length > 0) {
+ this.pixelCoordinate_ = pixelCoordinate;
+ segments.sort(this.sortByDistance_);
+ var closestSegment = segments[0].segment;
+ if (this.vertex_ && !this.edge_) {
+ pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
+ pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
+ squaredDist1 = ol.coordinate.squaredDistance(pixel, pixel1);
+ squaredDist2 = ol.coordinate.squaredDistance(pixel, pixel2);
+ dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
+ snappedToVertex = dist <= this.pixelTolerance_;
+ if (snappedToVertex) {
+ snapped = true;
+ vertex = squaredDist1 > squaredDist2 ?
+ closestSegment[1] : closestSegment[0];
+ vertexPixel = map.getPixelFromCoordinate(vertex);
+ }
+ } else if (this.edge_) {
+ vertex = (ol.coordinate.closestOnSegment(pixelCoordinate,
+ closestSegment));
+ vertexPixel = map.getPixelFromCoordinate(vertex);
+ if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <=
+ this.pixelTolerance_) {
+ snapped = true;
+ if (this.vertex_) {
+ pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
+ pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
+ squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1);
+ squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2);
+ dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
+ snappedToVertex = dist <= this.pixelTolerance_;
+ if (snappedToVertex) {
+ vertex = squaredDist1 > squaredDist2 ?
+ closestSegment[1] : closestSegment[0];
+ vertexPixel = map.getPixelFromCoordinate(vertex);
+ }
+ }
+ }
+ }
+ if (snapped) {
+ vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])];
+ }
+ }
+ return /** @type {ol.SnapResultType} */ ({
+ snapped: snapped,
+ vertex: vertex,
+ vertexPixel: vertexPixel
+ });
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @private
+ */
+ol.interaction.Snap.prototype.updateFeature_ = function(feature) {
+ this.removeFeature(feature, false);
+ this.addFeature(feature, false);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.GeometryCollection} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) {
+ var i, geometries = geometry.getGeometriesArray();
+ for (i = 0; i < geometries.length; ++i) {
+ this.SEGMENT_WRITERS_[geometries[i].getType()].call(
+ this, feature, geometries[i]);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.LineString} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writeLineStringGeometry_ = function(feature, geometry) {
+ var coordinates = geometry.getCoordinates();
+ var i, ii, segment, segmentData;
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.SnapSegmentDataType} */ ({
+ feature: feature,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.MultiLineString} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) {
+ var lines = geometry.getCoordinates();
+ var coordinates, i, ii, j, jj, segment, segmentData;
+ for (j = 0, jj = lines.length; j < jj; ++j) {
+ coordinates = lines[j];
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.SnapSegmentDataType} */ ({
+ feature: feature,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.MultiPoint} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writeMultiPointGeometry_ = function(feature, geometry) {
+ var points = geometry.getCoordinates();
+ var coordinates, i, ii, segmentData;
+ for (i = 0, ii = points.length; i < ii; ++i) {
+ coordinates = points[i];
+ segmentData = /** @type {ol.SnapSegmentDataType} */ ({
+ feature: feature,
+ segment: [coordinates, coordinates]
+ });
+ this.rBush_.insert(geometry.getExtent(), segmentData);
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.MultiPolygon} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) {
+ var polygons = geometry.getCoordinates();
+ var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData;
+ for (k = 0, kk = polygons.length; k < kk; ++k) {
+ rings = polygons[k];
+ for (j = 0, jj = rings.length; j < jj; ++j) {
+ coordinates = rings[j];
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.SnapSegmentDataType} */ ({
+ feature: feature,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.Point} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writePointGeometry_ = function(feature, geometry) {
+ var coordinates = geometry.getCoordinates();
+ var segmentData = /** @type {ol.SnapSegmentDataType} */ ({
+ feature: feature,
+ segment: [coordinates, coordinates]
+ });
+ this.rBush_.insert(geometry.getExtent(), segmentData);
+};
+
+
+/**
+ * @param {ol.Feature} feature Feature
+ * @param {ol.geom.Polygon} geometry Geometry.
+ * @private
+ */
+ol.interaction.Snap.prototype.writePolygonGeometry_ = function(feature, geometry) {
+ var rings = geometry.getCoordinates();
+ var coordinates, i, ii, j, jj, segment, segmentData;
+ for (j = 0, jj = rings.length; j < jj; ++j) {
+ coordinates = rings[j];
+ for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
+ segment = coordinates.slice(i, i + 2);
+ segmentData = /** @type {ol.SnapSegmentDataType} */ ({
+ feature: feature,
+ segment: segment
+ });
+ this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
+ }
+ }
+};
+
+
+/**
+ * Handle all pointer events events.
+ * @param {ol.MapBrowserEvent} evt A move event.
+ * @return {boolean} Pass the event to other interactions.
+ * @this {ol.interaction.Snap}
+ * @private
+ */
+ol.interaction.Snap.handleEvent_ = function(evt) {
+ var result = this.snapTo(evt.pixel, evt.coordinate, evt.map);
+ if (result.snapped) {
+ evt.coordinate = result.vertex.slice(0, 2);
+ evt.pixel = result.vertexPixel;
+ }
+ return ol.interaction.Pointer.handleEvent.call(this, evt);
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} evt Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.Snap}
+ * @private
+ */
+ol.interaction.Snap.handleUpEvent_ = function(evt) {
+ var featuresToUpdate = ol.obj.getValues(this.pendingFeatures_);
+ if (featuresToUpdate.length) {
+ featuresToUpdate.forEach(this.updateFeature_, this);
+ this.pendingFeatures_ = {};
+ }
+ return false;
+};
+
+
+/**
+ * Sort segments by distance, helper function
+ * @param {ol.SnapSegmentDataType} a The first segment data.
+ * @param {ol.SnapSegmentDataType} b The second segment data.
+ * @return {number} The difference in distance.
+ * @this {ol.interaction.Snap}
+ */
+ol.interaction.Snap.sortByDistance = function(a, b) {
+ return ol.coordinate.squaredDistanceToSegment(
+ this.pixelCoordinate_, a.segment) -
+ ol.coordinate.squaredDistanceToSegment(
+ this.pixelCoordinate_, b.segment);
+};
+
+goog.provide('ol.interaction.Translate');
+
+goog.require('ol');
+goog.require('ol.Collection');
+goog.require('ol.events.Event');
+goog.require('ol.functions');
+goog.require('ol.array');
+goog.require('ol.interaction.Pointer');
+
+
+/**
+ * @classdesc
+ * Interaction for translating (moving) features.
+ *
+ * @constructor
+ * @extends {ol.interaction.Pointer}
+ * @fires ol.interaction.Translate.Event
+ * @param {olx.interaction.TranslateOptions=} opt_options Options.
+ * @api
+ */
+ol.interaction.Translate = function(opt_options) {
+ ol.interaction.Pointer.call(this, {
+ handleDownEvent: ol.interaction.Translate.handleDownEvent_,
+ handleDragEvent: ol.interaction.Translate.handleDragEvent_,
+ handleMoveEvent: ol.interaction.Translate.handleMoveEvent_,
+ handleUpEvent: ol.interaction.Translate.handleUpEvent_
+ });
+
+ var options = opt_options ? opt_options : {};
+
+ /**
+ * @type {string|undefined}
+ * @private
+ */
+ this.previousCursor_ = undefined;
+
+
+ /**
+ * The last position we translated to.
+ * @type {ol.Coordinate}
+ * @private
+ */
+ this.lastCoordinate_ = null;
+
+
+ /**
+ * @type {ol.Collection.<ol.Feature>}
+ * @private
+ */
+ this.features_ = options.features !== undefined ? options.features : null;
+
+ /** @type {function(ol.layer.Layer): boolean} */
+ var layerFilter;
+ if (options.layers) {
+ if (typeof options.layers === 'function') {
+ layerFilter = options.layers;
+ } else {
+ var layers = options.layers;
+ layerFilter = function(layer) {
+ return ol.array.includes(layers, layer);
+ };
+ }
+ } else {
+ layerFilter = ol.functions.TRUE;
+ }
+
+ /**
+ * @private
+ * @type {function(ol.layer.Layer): boolean}
+ */
+ this.layerFilter_ = layerFilter;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
+
+ /**
+ * @type {ol.Feature}
+ * @private
+ */
+ this.lastFeature_ = null;
+};
+ol.inherits(ol.interaction.Translate, ol.interaction.Pointer);
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} event Event.
+ * @return {boolean} Start drag sequence?
+ * @this {ol.interaction.Translate}
+ * @private
+ */
+ol.interaction.Translate.handleDownEvent_ = function(event) {
+ this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
+ if (!this.lastCoordinate_ && this.lastFeature_) {
+ this.lastCoordinate_ = event.coordinate;
+ ol.interaction.Translate.handleMoveEvent_.call(this, event);
+
+ var features = this.features_ || new ol.Collection([this.lastFeature_]);
+
+ this.dispatchEvent(
+ new ol.interaction.Translate.Event(
+ ol.interaction.Translate.EventType.TRANSLATESTART, features,
+ event.coordinate));
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} event Event.
+ * @return {boolean} Stop drag sequence?
+ * @this {ol.interaction.Translate}
+ * @private
+ */
+ol.interaction.Translate.handleUpEvent_ = function(event) {
+ if (this.lastCoordinate_) {
+ this.lastCoordinate_ = null;
+ ol.interaction.Translate.handleMoveEvent_.call(this, event);
+
+ var features = this.features_ || new ol.Collection([this.lastFeature_]);
+
+ this.dispatchEvent(
+ new ol.interaction.Translate.Event(
+ ol.interaction.Translate.EventType.TRANSLATEEND, features,
+ event.coordinate));
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * @param {ol.MapBrowserPointerEvent} event Event.
+ * @this {ol.interaction.Translate}
+ * @private
+ */
+ol.interaction.Translate.handleDragEvent_ = function(event) {
+ if (this.lastCoordinate_) {
+ var newCoordinate = event.coordinate;
+ var deltaX = newCoordinate[0] - this.lastCoordinate_[0];
+ var deltaY = newCoordinate[1] - this.lastCoordinate_[1];
+
+ var features = this.features_ || new ol.Collection([this.lastFeature_]);
+
+ features.forEach(function(feature) {
+ var geom = feature.getGeometry();
+ geom.translate(deltaX, deltaY);
+ feature.setGeometry(geom);
+ });
+
+ this.lastCoordinate_ = newCoordinate;
+ this.dispatchEvent(
+ new ol.interaction.Translate.Event(
+ ol.interaction.Translate.EventType.TRANSLATING, features,
+ newCoordinate));
+ }
+};
+
+
+/**
+ * @param {ol.MapBrowserEvent} event Event.
+ * @this {ol.interaction.Translate}
+ * @private
+ */
+ol.interaction.Translate.handleMoveEvent_ = function(event) {
+ var elem = event.map.getTargetElement();
+
+ // Change the cursor to grab/grabbing if hovering any of the features managed
+ // by the interaction
+ if (this.featuresAtPixel_(event.pixel, event.map)) {
+ this.previousCursor_ = elem.style.cursor;
+ // WebKit browsers don't support the grab icons without a prefix
+ elem.style.cursor = this.lastCoordinate_ ?
+ '-webkit-grabbing' : '-webkit-grab';
+
+ // Thankfully, attempting to set the standard ones will silently fail,
+ // keeping the prefixed icons
+ elem.style.cursor = this.lastCoordinate_ ? 'grabbing' : 'grab';
+ } else {
+ elem.style.cursor = this.previousCursor_ !== undefined ?
+ this.previousCursor_ : '';
+ this.previousCursor_ = undefined;
+ }
+};
+
+
+/**
+ * Tests to see if the given coordinates intersects any of our selected
+ * features.
+ * @param {ol.Pixel} pixel Pixel coordinate to test for intersection.
+ * @param {ol.Map} map Map to test the intersection on.
+ * @return {ol.Feature} Returns the feature found at the specified pixel
+ * coordinates.
+ * @private
+ */
+ol.interaction.Translate.prototype.featuresAtPixel_ = function(pixel, map) {
+ return map.forEachFeatureAtPixel(pixel,
+ function(feature) {
+ if (!this.features_ ||
+ ol.array.includes(this.features_.getArray(), feature)) {
+ return feature;
+ }
+ }.bind(this), {
+ layerFilter: this.layerFilter_,
+ hitTolerance: this.hitTolerance_
+ });
+};
+
+
+/**
+ * Returns the Hit-detection tolerance.
+ * @returns {number} Hit tolerance in pixels.
+ * @api
+ */
+ol.interaction.Translate.prototype.getHitTolerance = function() {
+ return this.hitTolerance_;
+};
+
+
+/**
+ * Hit-detection tolerance. Pixels inside the radius around the given position
+ * will be checked for features. This only works for the canvas renderer and
+ * not for WebGL.
+ * @param {number} hitTolerance Hit tolerance in pixels.
+ * @api
+ */
+ol.interaction.Translate.prototype.setHitTolerance = function(hitTolerance) {
+ this.hitTolerance_ = hitTolerance;
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.interaction.Translate} instances are instances of
+ * this type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.interaction.TranslateEvent}
+ * @param {ol.interaction.Translate.EventType} type Type.
+ * @param {ol.Collection.<ol.Feature>} features The features translated.
+ * @param {ol.Coordinate} coordinate The event coordinate.
+ */
+ol.interaction.Translate.Event = function(type, features, coordinate) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The features being translated.
+ * @type {ol.Collection.<ol.Feature>}
+ * @api
+ */
+ this.features = features;
+
+ /**
+ * The coordinate of the drag event.
+ * @const
+ * @type {ol.Coordinate}
+ * @api
+ */
+ this.coordinate = coordinate;
+};
+ol.inherits(ol.interaction.Translate.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.interaction.Translate.EventType = {
+ /**
+ * Triggered upon feature translation start.
+ * @event ol.interaction.Translate.Event#translatestart
+ * @api
+ */
+ TRANSLATESTART: 'translatestart',
+ /**
+ * Triggered upon feature translation.
+ * @event ol.interaction.Translate.Event#translating
+ * @api
+ */
+ TRANSLATING: 'translating',
+ /**
+ * Triggered upon feature translation end.
+ * @event ol.interaction.Translate.Event#translateend
+ * @api
+ */
+ TRANSLATEEND: 'translateend'
+};
+
+goog.provide('ol.layer.Heatmap');
+
+goog.require('ol.events');
+goog.require('ol');
+goog.require('ol.Object');
+goog.require('ol.dom');
+goog.require('ol.layer.Vector');
+goog.require('ol.math');
+goog.require('ol.obj');
+goog.require('ol.render.Event');
+goog.require('ol.style.Icon');
+goog.require('ol.style.Style');
+
+
+/**
+ * @classdesc
+ * Layer for rendering vector data as a heatmap.
+ * Note that any property set in the options is set as a {@link ol.Object}
+ * property on the layer object; for example, setting `title: 'My Title'` in the
+ * options means that `title` is observable, and has get/set accessors.
+ *
+ * @constructor
+ * @extends {ol.layer.Vector}
+ * @fires ol.render.Event
+ * @param {olx.layer.HeatmapOptions=} opt_options Options.
+ * @api
+ */
+ol.layer.Heatmap = function(opt_options) {
+ var options = opt_options ? opt_options : {};
+
+ var baseOptions = ol.obj.assign({}, options);
+
+ delete baseOptions.gradient;
+ delete baseOptions.radius;
+ delete baseOptions.blur;
+ delete baseOptions.shadow;
+ delete baseOptions.weight;
+ ol.layer.Vector.call(this, /** @type {olx.layer.VectorOptions} */ (baseOptions));
+
+ /**
+ * @private
+ * @type {Uint8ClampedArray}
+ */
+ this.gradient_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.shadow_ = options.shadow !== undefined ? options.shadow : 250;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.circleImage_ = undefined;
+
+ /**
+ * @private
+ * @type {Array.<Array.<ol.style.Style>>}
+ */
+ this.styleCache_ = null;
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.layer.Heatmap.Property.GRADIENT),
+ this.handleGradientChanged_, this);
+
+ this.setGradient(options.gradient ?
+ options.gradient : ol.layer.Heatmap.DEFAULT_GRADIENT);
+
+ this.setBlur(options.blur !== undefined ? options.blur : 15);
+
+ this.setRadius(options.radius !== undefined ? options.radius : 8);
+
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.layer.Heatmap.Property.BLUR),
+ this.handleStyleChanged_, this);
+ ol.events.listen(this,
+ ol.Object.getChangeEventType(ol.layer.Heatmap.Property.RADIUS),
+ this.handleStyleChanged_, this);
+
+ this.handleStyleChanged_();
+
+ var weight = options.weight ? options.weight : 'weight';
+ var weightFunction;
+ if (typeof weight === 'string') {
+ weightFunction = function(feature) {
+ return feature.get(weight);
+ };
+ } else {
+ weightFunction = weight;
+ }
+ ol.DEBUG && console.assert(typeof weightFunction === 'function',
+ 'weightFunction should be a function');
+
+ this.setStyle(function(feature, resolution) {
+ ol.DEBUG && console.assert(this.styleCache_, 'this.styleCache_ expected');
+ ol.DEBUG && console.assert(this.circleImage_ !== undefined,
+ 'this.circleImage_ should be defined');
+ var weight = weightFunction(feature);
+ var opacity = weight !== undefined ? ol.math.clamp(weight, 0, 1) : 1;
+ // cast to 8 bits
+ var index = (255 * opacity) | 0;
+ var style = this.styleCache_[index];
+ if (!style) {
+ style = [
+ new ol.style.Style({
+ image: new ol.style.Icon({
+ opacity: opacity,
+ src: this.circleImage_
+ })
+ })
+ ];
+ this.styleCache_[index] = style;
+ }
+ return style;
+ }.bind(this));
+
+ // For performance reasons, don't sort the features before rendering.
+ // The render order is not relevant for a heatmap representation.
+ this.setRenderOrder(null);
+
+ ol.events.listen(this, ol.render.Event.Type.RENDER, this.handleRender_, this);
+
+};
+ol.inherits(ol.layer.Heatmap, ol.layer.Vector);
+
+
+/**
+ * @const
+ * @type {Array.<string>}
+ */
+ol.layer.Heatmap.DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00'];
+
+
+/**
+ * @param {Array.<string>} colors A list of colored.
+ * @return {Uint8ClampedArray} An array.
+ * @private
+ */
+ol.layer.Heatmap.createGradient_ = function(colors) {
+ var width = 1;
+ var height = 256;
+ var context = ol.dom.createCanvasContext2D(width, height);
+
+ var gradient = context.createLinearGradient(0, 0, width, height);
+ var step = 1 / (colors.length - 1);
+ for (var i = 0, ii = colors.length; i < ii; ++i) {
+ gradient.addColorStop(i * step, colors[i]);
+ }
+
+ context.fillStyle = gradient;
+ context.fillRect(0, 0, width, height);
+
+ return context.getImageData(0, 0, width, height).data;
+};
+
+
+/**
+ * @return {string} Data URL for a circle.
+ * @private
+ */
+ol.layer.Heatmap.prototype.createCircle_ = function() {
+ var radius = this.getRadius();
+ var blur = this.getBlur();
+ ol.DEBUG && console.assert(radius !== undefined && blur !== undefined,
+ 'radius and blur should be defined');
+ var halfSize = radius + blur + 1;
+ var size = 2 * halfSize;
+ var context = ol.dom.createCanvasContext2D(size, size);
+ context.shadowOffsetX = context.shadowOffsetY = this.shadow_;
+ context.shadowBlur = blur;
+ context.shadowColor = '#000';
+ context.beginPath();
+ var center = halfSize - this.shadow_;
+ context.arc(center, center, radius, 0, Math.PI * 2, true);
+ context.fill();
+ return context.canvas.toDataURL();
+};
+
+
+/**
+ * Return the blur size in pixels.
+ * @return {number} Blur size in pixels.
+ * @api
+ * @observable
+ */
+ol.layer.Heatmap.prototype.getBlur = function() {
+ return /** @type {number} */ (this.get(ol.layer.Heatmap.Property.BLUR));
+};
+
+
+/**
+ * Return the gradient colors as array of strings.
+ * @return {Array.<string>} Colors.
+ * @api
+ * @observable
+ */
+ol.layer.Heatmap.prototype.getGradient = function() {
+ return /** @type {Array.<string>} */ (
+ this.get(ol.layer.Heatmap.Property.GRADIENT));
+};
+
+
+/**
+ * Return the size of the radius in pixels.
+ * @return {number} Radius size in pixel.
+ * @api
+ * @observable
+ */
+ol.layer.Heatmap.prototype.getRadius = function() {
+ return /** @type {number} */ (this.get(ol.layer.Heatmap.Property.RADIUS));
+};
+
+
+/**
+ * @private
+ */
+ol.layer.Heatmap.prototype.handleGradientChanged_ = function() {
+ this.gradient_ = ol.layer.Heatmap.createGradient_(this.getGradient());
+};
+
+
+/**
+ * @private
+ */
+ol.layer.Heatmap.prototype.handleStyleChanged_ = function() {
+ this.circleImage_ = this.createCircle_();
+ this.styleCache_ = new Array(256);
+ this.changed();
+};
+
+
+/**
+ * @param {ol.render.Event} event Post compose event
+ * @private
+ */
+ol.layer.Heatmap.prototype.handleRender_ = function(event) {
+ ol.DEBUG && console.assert(event.type == ol.render.Event.Type.RENDER,
+ 'event.type should be RENDER');
+ ol.DEBUG && console.assert(this.gradient_, 'this.gradient_ expected');
+ var context = event.context;
+ var canvas = context.canvas;
+ var image = context.getImageData(0, 0, canvas.width, canvas.height);
+ var view8 = image.data;
+ var i, ii, alpha;
+ for (i = 0, ii = view8.length; i < ii; i += 4) {
+ alpha = view8[i + 3] * 4;
+ if (alpha) {
+ view8[i] = this.gradient_[alpha];
+ view8[i + 1] = this.gradient_[alpha + 1];
+ view8[i + 2] = this.gradient_[alpha + 2];
+ }
+ }
+ context.putImageData(image, 0, 0);
+};
+
+
+/**
+ * Set the blur size in pixels.
+ * @param {number} blur Blur size in pixels.
+ * @api
+ * @observable
+ */
+ol.layer.Heatmap.prototype.setBlur = function(blur) {
+ this.set(ol.layer.Heatmap.Property.BLUR, blur);
+};
+
+
+/**
+ * Set the gradient colors as array of strings.
+ * @param {Array.<string>} colors Gradient.
+ * @api
+ * @observable
+ */
+ol.layer.Heatmap.prototype.setGradient = function(colors) {
+ this.set(ol.layer.Heatmap.Property.GRADIENT, colors);
+};
+
+
+/**
+ * Set the size of the radius in pixels.
+ * @param {number} radius Radius size in pixel.
+ * @api
+ * @observable
+ */
+ol.layer.Heatmap.prototype.setRadius = function(radius) {
+ this.set(ol.layer.Heatmap.Property.RADIUS, radius);
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.layer.Heatmap.Property = {
+ BLUR: 'blur',
+ GRADIENT: 'gradient',
+ RADIUS: 'radius'
+};
+
+goog.provide('ol.net');
+
+goog.require('ol');
+
+
+/**
+ * Simple JSONP helper. Supports error callbacks and a custom callback param.
+ * The error callback will be called when no JSONP is executed after 10 seconds.
+ *
+ * @param {string} url Request url. A 'callback' query parameter will be
+ * appended.
+ * @param {Function} callback Callback on success.
+ * @param {function()=} opt_errback Callback on error.
+ * @param {string=} opt_callbackParam Custom query parameter for the JSONP
+ * callback. Default is 'callback'.
+ */
+ol.net.jsonp = function(url, callback, opt_errback, opt_callbackParam) {
+ var script = document.createElement('script');
+ var key = 'olc_' + ol.getUid(callback);
+ function cleanup() {
+ delete window[key];
+ script.parentNode.removeChild(script);
+ }
+ script.async = true;
+ script.src = url + (url.indexOf('?') == -1 ? '?' : '&') +
+ (opt_callbackParam || 'callback') + '=' + key;
+ var timer = setTimeout(function() {
+ cleanup();
+ if (opt_errback) {
+ opt_errback();
+ }
+ }, 10000);
+ window[key] = function(data) {
+ clearTimeout(timer);
+ cleanup();
+ callback(data);
+ };
+ document.getElementsByTagName('head')[0].appendChild(script);
+};
+
+goog.provide('ol.render');
+
+goog.require('ol.has');
+goog.require('ol.transform');
+goog.require('ol.render.canvas.Immediate');
+
+
+/**
+ * Binds a Canvas Immediate API to a canvas context, to allow drawing geometries
+ * to the context's canvas.
+ *
+ * The units for geometry coordinates are css pixels relative to the top left
+ * corner of the canvas element.
+ * ```js
+ * var canvas = document.createElement('canvas');
+ * var render = ol.render.toContext(canvas.getContext('2d'),
+ * { size: [100, 100] });
+ * render.setFillStrokeStyle(new ol.style.Fill({ color: blue }));
+ * render.drawPolygon(
+ * new ol.geom.Polygon([[[0, 0], [100, 100], [100, 0], [0, 0]]]));
+ * ```
+ *
+ * @param {CanvasRenderingContext2D} context Canvas context.
+ * @param {olx.render.ToContextOptions=} opt_options Options.
+ * @return {ol.render.canvas.Immediate} Canvas Immediate.
+ * @api
+ */
+ol.render.toContext = function(context, opt_options) {
+ var canvas = context.canvas;
+ var options = opt_options ? opt_options : {};
+ var pixelRatio = options.pixelRatio || ol.has.DEVICE_PIXEL_RATIO;
+ var size = options.size;
+ if (size) {
+ canvas.width = size[0] * pixelRatio;
+ canvas.height = size[1] * pixelRatio;
+ canvas.style.width = size[0] + 'px';
+ canvas.style.height = size[1] + 'px';
+ }
+ var extent = [0, 0, canvas.width, canvas.height];
+ var transform = ol.transform.scale(ol.transform.create(), pixelRatio, pixelRatio);
+ return new ol.render.canvas.Immediate(context, pixelRatio, extent, transform,
+ 0);
+};
+
+goog.provide('ol.reproj.Tile');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.math');
+goog.require('ol.reproj');
+goog.require('ol.reproj.Triangulation');
+
+
+/**
+ * @classdesc
+ * Class encapsulating single reprojected tile.
+ * See {@link ol.source.TileImage}.
+ *
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.proj.Projection} sourceProj Source projection.
+ * @param {ol.tilegrid.TileGrid} sourceTileGrid Source tile grid.
+ * @param {ol.proj.Projection} targetProj Target projection.
+ * @param {ol.tilegrid.TileGrid} targetTileGrid Target tile grid.
+ * @param {ol.TileCoord} tileCoord Coordinate of the tile.
+ * @param {ol.TileCoord} wrappedTileCoord Coordinate of the tile wrapped in X.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {number} gutter Gutter of the source tiles.
+ * @param {ol.ReprojTileFunctionType} getTileFunction
+ * Function returning source tiles (z, x, y, pixelRatio).
+ * @param {number=} opt_errorThreshold Acceptable reprojection error (in px).
+ * @param {boolean=} opt_renderEdges Render reprojection edges.
+ */
+ol.reproj.Tile = function(sourceProj, sourceTileGrid,
+ targetProj, targetTileGrid, tileCoord, wrappedTileCoord,
+ pixelRatio, gutter, getTileFunction,
+ opt_errorThreshold,
+ opt_renderEdges) {
+ ol.Tile.call(this, tileCoord, ol.Tile.State.IDLE);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelRatio_ = pixelRatio;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.gutter_ = gutter;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = null;
+
+ /**
+ * @private
+ * @type {ol.tilegrid.TileGrid}
+ */
+ this.sourceTileGrid_ = sourceTileGrid;
+
+ /**
+ * @private
+ * @type {ol.tilegrid.TileGrid}
+ */
+ this.targetTileGrid_ = targetTileGrid;
+
+ /**
+ * @private
+ * @type {ol.TileCoord}
+ */
+ this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord;
+
+ /**
+ * @private
+ * @type {!Array.<ol.Tile>}
+ */
+ this.sourceTiles_ = [];
+
+ /**
+ * @private
+ * @type {Array.<ol.EventsKey>}
+ */
+ this.sourcesListenerKeys_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.sourceZ_ = 0;
+
+ var targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_);
+ var maxTargetExtent = this.targetTileGrid_.getExtent();
+ var maxSourceExtent = this.sourceTileGrid_.getExtent();
+
+ var limitedTargetExtent = maxTargetExtent ?
+ ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent;
+
+ if (ol.extent.getArea(limitedTargetExtent) === 0) {
+ // Tile is completely outside range -> EMPTY
+ // TODO: is it actually correct that the source even creates the tile ?
+ this.state = ol.Tile.State.EMPTY;
+ return;
+ }
+
+ var sourceProjExtent = sourceProj.getExtent();
+ if (sourceProjExtent) {
+ if (!maxSourceExtent) {
+ maxSourceExtent = sourceProjExtent;
+ } else {
+ maxSourceExtent = ol.extent.getIntersection(
+ maxSourceExtent, sourceProjExtent);
+ }
+ }
+
+ var targetResolution = targetTileGrid.getResolution(
+ this.wrappedTileCoord_[0]);
+
+ var targetCenter = ol.extent.getCenter(limitedTargetExtent);
+ var sourceResolution = ol.reproj.calculateSourceResolution(
+ sourceProj, targetProj, targetCenter, targetResolution);
+
+ if (!isFinite(sourceResolution) || sourceResolution <= 0) {
+ // invalid sourceResolution -> EMPTY
+ // probably edges of the projections when no extent is defined
+ this.state = ol.Tile.State.EMPTY;
+ return;
+ }
+
+ var errorThresholdInPixels = opt_errorThreshold !== undefined ?
+ opt_errorThreshold : ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD;
+
+ /**
+ * @private
+ * @type {!ol.reproj.Triangulation}
+ */
+ this.triangulation_ = new ol.reproj.Triangulation(
+ sourceProj, targetProj, limitedTargetExtent, maxSourceExtent,
+ sourceResolution * errorThresholdInPixels);
+
+ if (this.triangulation_.getTriangles().length === 0) {
+ // no valid triangles -> EMPTY
+ this.state = ol.Tile.State.EMPTY;
+ return;
+ }
+
+ this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution);
+ var sourceExtent = this.triangulation_.calculateSourceExtent();
+
+ if (maxSourceExtent) {
+ if (sourceProj.canWrapX()) {
+ sourceExtent[1] = ol.math.clamp(
+ sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]);
+ sourceExtent[3] = ol.math.clamp(
+ sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]);
+ } else {
+ sourceExtent = ol.extent.getIntersection(sourceExtent, maxSourceExtent);
+ }
+ }
+
+ if (!ol.extent.getArea(sourceExtent)) {
+ this.state = ol.Tile.State.EMPTY;
+ } else {
+ var sourceRange = sourceTileGrid.getTileRangeForExtentAndZ(
+ sourceExtent, this.sourceZ_);
+
+ var tilesRequired = sourceRange.getWidth() * sourceRange.getHeight();
+ if (ol.DEBUG && !(tilesRequired < ol.RASTER_REPROJECTION_MAX_SOURCE_TILES)) {
+ console.assert(false, 'reasonable number of tiles is required');
+ this.state = ol.Tile.State.ERROR;
+ return;
+ }
+ for (var srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) {
+ for (var srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) {
+ var tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio);
+ if (tile) {
+ this.sourceTiles_.push(tile);
+ }
+ }
+ }
+
+ if (this.sourceTiles_.length === 0) {
+ this.state = ol.Tile.State.EMPTY;
+ }
+ }
+};
+ol.inherits(ol.reproj.Tile, ol.Tile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.reproj.Tile.prototype.disposeInternal = function() {
+ if (this.state == ol.Tile.State.LOADING) {
+ this.unlistenSources_();
+ }
+ ol.Tile.prototype.disposeInternal.call(this);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.reproj.Tile.prototype.getImage = function() {
+ return this.canvas_;
+};
+
+
+/**
+ * @private
+ */
+ol.reproj.Tile.prototype.reproject_ = function() {
+ var sources = [];
+ this.sourceTiles_.forEach(function(tile, i, arr) {
+ if (tile && tile.getState() == ol.Tile.State.LOADED) {
+ sources.push({
+ extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord),
+ image: tile.getImage()
+ });
+ }
+ }, this);
+ this.sourceTiles_.length = 0;
+
+ if (sources.length === 0) {
+ this.state = ol.Tile.State.ERROR;
+ } else {
+ var z = this.wrappedTileCoord_[0];
+ var size = this.targetTileGrid_.getTileSize(z);
+ var width = typeof size === 'number' ? size : size[0];
+ var height = typeof size === 'number' ? size : size[1];
+ var targetResolution = this.targetTileGrid_.getResolution(z);
+ var sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_);
+
+ var targetExtent = this.targetTileGrid_.getTileCoordExtent(
+ this.wrappedTileCoord_);
+ this.canvas_ = ol.reproj.render(width, height, this.pixelRatio_,
+ sourceResolution, this.sourceTileGrid_.getExtent(),
+ targetResolution, targetExtent, this.triangulation_, sources,
+ this.gutter_, this.renderEdges_);
+
+ this.state = ol.Tile.State.LOADED;
+ }
+ this.changed();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.reproj.Tile.prototype.load = function() {
+ if (this.state == ol.Tile.State.IDLE) {
+ this.state = ol.Tile.State.LOADING;
+ this.changed();
+
+ var leftToLoad = 0;
+
+ ol.DEBUG && console.assert(!this.sourcesListenerKeys_,
+ 'this.sourcesListenerKeys_ should be null');
+
+ this.sourcesListenerKeys_ = [];
+ this.sourceTiles_.forEach(function(tile, i, arr) {
+ var state = tile.getState();
+ if (state == ol.Tile.State.IDLE || state == ol.Tile.State.LOADING) {
+ leftToLoad++;
+
+ var sourceListenKey;
+ sourceListenKey = ol.events.listen(tile, ol.events.EventType.CHANGE,
+ function(e) {
+ var state = tile.getState();
+ if (state == ol.Tile.State.LOADED ||
+ state == ol.Tile.State.ERROR ||
+ state == ol.Tile.State.EMPTY) {
+ ol.events.unlistenByKey(sourceListenKey);
+ leftToLoad--;
+ ol.DEBUG && console.assert(leftToLoad >= 0,
+ 'leftToLoad should not be negative');
+ if (leftToLoad === 0) {
+ this.unlistenSources_();
+ this.reproject_();
+ }
+ }
+ }, this);
+ this.sourcesListenerKeys_.push(sourceListenKey);
+ }
+ }, this);
+
+ this.sourceTiles_.forEach(function(tile, i, arr) {
+ var state = tile.getState();
+ if (state == ol.Tile.State.IDLE) {
+ tile.load();
+ }
+ });
+
+ if (leftToLoad === 0) {
+ setTimeout(this.reproject_.bind(this), 0);
+ }
+ }
+};
+
+
+/**
+ * @private
+ */
+ol.reproj.Tile.prototype.unlistenSources_ = function() {
+ this.sourcesListenerKeys_.forEach(ol.events.unlistenByKey);
+ this.sourcesListenerKeys_ = null;
+};
+
+goog.provide('ol.TileUrlFunction');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.math');
+goog.require('ol.tilecoord');
+
+
+/**
+ * @param {string} template Template.
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @return {ol.TileUrlFunctionType} Tile URL function.
+ */
+ol.TileUrlFunction.createFromTemplate = function(template, tileGrid) {
+ var zRegEx = /\{z\}/g;
+ var xRegEx = /\{x\}/g;
+ var yRegEx = /\{y\}/g;
+ var dashYRegEx = /\{-y\}/g;
+ return (
+ /**
+ * @param {ol.TileCoord} tileCoord Tile Coordinate.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string|undefined} Tile URL.
+ */
+ function(tileCoord, pixelRatio, projection) {
+ if (!tileCoord) {
+ return undefined;
+ } else {
+ return template.replace(zRegEx, tileCoord[0].toString())
+ .replace(xRegEx, tileCoord[1].toString())
+ .replace(yRegEx, function() {
+ var y = -tileCoord[2] - 1;
+ return y.toString();
+ })
+ .replace(dashYRegEx, function() {
+ var z = tileCoord[0];
+ var range = tileGrid.getFullTileRange(z);
+ ol.asserts.assert(range, 55); // The {-y} placeholder requires a tile grid with extent
+ var y = range.getHeight() + tileCoord[2];
+ return y.toString();
+ });
+ }
+ });
+};
+
+
+/**
+ * @param {Array.<string>} templates Templates.
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @return {ol.TileUrlFunctionType} Tile URL function.
+ */
+ol.TileUrlFunction.createFromTemplates = function(templates, tileGrid) {
+ var len = templates.length;
+ var tileUrlFunctions = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ tileUrlFunctions[i] = ol.TileUrlFunction.createFromTemplate(
+ templates[i], tileGrid);
+ }
+ return ol.TileUrlFunction.createFromTileUrlFunctions(tileUrlFunctions);
+};
+
+
+/**
+ * @param {Array.<ol.TileUrlFunctionType>} tileUrlFunctions Tile URL Functions.
+ * @return {ol.TileUrlFunctionType} Tile URL function.
+ */
+ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
+ ol.DEBUG && console.assert(tileUrlFunctions.length > 0,
+ 'Length of tile url functions should be greater than 0');
+ if (tileUrlFunctions.length === 1) {
+ return tileUrlFunctions[0];
+ }
+ return (
+ /**
+ * @param {ol.TileCoord} tileCoord Tile Coordinate.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string|undefined} Tile URL.
+ */
+ function(tileCoord, pixelRatio, projection) {
+ if (!tileCoord) {
+ return undefined;
+ } else {
+ var h = ol.tilecoord.hash(tileCoord);
+ var index = ol.math.modulo(h, tileUrlFunctions.length);
+ return tileUrlFunctions[index](tileCoord, pixelRatio, projection);
+ }
+ });
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string|undefined} Tile URL.
+ */
+ol.TileUrlFunction.nullTileUrlFunction = function(tileCoord, pixelRatio, projection) {
+ return undefined;
+};
+
+
+/**
+ * @param {string} url URL.
+ * @return {Array.<string>} Array of urls.
+ */
+ol.TileUrlFunction.expandUrl = function(url) {
+ var urls = [];
+ var match = /\{([a-z])-([a-z])\}/.exec(url);
+ if (match) {
+ // char range
+ var startCharCode = match[1].charCodeAt(0);
+ var stopCharCode = match[2].charCodeAt(0);
+ var charCode;
+ for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) {
+ urls.push(url.replace(match[0], String.fromCharCode(charCode)));
+ }
+ return urls;
+ }
+ match = match = /\{(\d+)-(\d+)\}/.exec(url);
+ if (match) {
+ // number range
+ var stop = parseInt(match[2], 10);
+ for (var i = parseInt(match[1], 10); i <= stop; i++) {
+ urls.push(url.replace(match[0], i.toString()));
+ }
+ return urls;
+ }
+ urls.push(url);
+ return urls;
+};
+
+goog.provide('ol.TileCache');
+
+goog.require('ol');
+goog.require('ol.structs.LRUCache');
+
+
+/**
+ * @constructor
+ * @extends {ol.structs.LRUCache.<ol.Tile>}
+ * @param {number=} opt_highWaterMark High water mark.
+ * @struct
+ */
+ol.TileCache = function(opt_highWaterMark) {
+
+ ol.structs.LRUCache.call(this);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.highWaterMark_ = opt_highWaterMark !== undefined ? opt_highWaterMark : 2048;
+
+};
+ol.inherits(ol.TileCache, ol.structs.LRUCache);
+
+
+/**
+ * @return {boolean} Can expire cache.
+ */
+ol.TileCache.prototype.canExpireCache = function() {
+ return this.getCount() > this.highWaterMark_;
+};
+
+
+/**
+ * @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
+ */
+ol.TileCache.prototype.expireCache = function(usedTiles) {
+ var tile, zKey;
+ while (this.canExpireCache()) {
+ tile = this.peekLast();
+ zKey = tile.tileCoord[0].toString();
+ if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) {
+ break;
+ } else {
+ this.pop().dispose();
+ }
+ }
+};
+
+goog.provide('ol.source.Tile');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.TileCache');
+goog.require('ol.events.Event');
+goog.require('ol.proj');
+goog.require('ol.size');
+goog.require('ol.source.Source');
+goog.require('ol.tilecoord');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * Abstract base class; normally only used for creating subclasses and not
+ * instantiated in apps.
+ * Base class for sources providing images divided into a tile grid.
+ *
+ * @constructor
+ * @extends {ol.source.Source}
+ * @param {ol.SourceTileOptions} options Tile source options.
+ * @api
+ */
+ol.source.Tile = function(options) {
+
+ ol.source.Source.call(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ logo: options.logo,
+ projection: options.projection,
+ state: options.state,
+ wrapX: options.wrapX
+ });
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.opaque_ = options.opaque !== undefined ? options.opaque : false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.tilePixelRatio_ = options.tilePixelRatio !== undefined ?
+ options.tilePixelRatio : 1;
+
+ /**
+ * @protected
+ * @type {ol.tilegrid.TileGrid}
+ */
+ this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null;
+
+ /**
+ * @protected
+ * @type {ol.TileCache}
+ */
+ this.tileCache = new ol.TileCache(options.cacheSize);
+
+ /**
+ * @protected
+ * @type {ol.Size}
+ */
+ this.tmpSize = [0, 0];
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.key_ = '';
+
+};
+ol.inherits(ol.source.Tile, ol.source.Source);
+
+
+/**
+ * @return {boolean} Can expire cache.
+ */
+ol.source.Tile.prototype.canExpireCache = function() {
+ return this.tileCache.canExpireCache();
+};
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
+ */
+ol.source.Tile.prototype.expireCache = function(projection, usedTiles) {
+ var tileCache = this.getTileCacheForProjection(projection);
+ if (tileCache) {
+ tileCache.expireCache(usedTiles);
+ }
+};
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {number} z Zoom level.
+ * @param {ol.TileRange} tileRange Tile range.
+ * @param {function(ol.Tile):(boolean|undefined)} callback Called with each
+ * loaded tile. If the callback returns `false`, the tile will not be
+ * considered loaded.
+ * @return {boolean} The tile range is fully covered with loaded tiles.
+ */
+ol.source.Tile.prototype.forEachLoadedTile = function(projection, z, tileRange, callback) {
+ var tileCache = this.getTileCacheForProjection(projection);
+ if (!tileCache) {
+ return false;
+ }
+
+ var covered = true;
+ var tile, tileCoordKey, loaded;
+ for (var x = tileRange.minX; x <= tileRange.maxX; ++x) {
+ for (var y = tileRange.minY; y <= tileRange.maxY; ++y) {
+ tileCoordKey = this.getKeyZXY(z, x, y);
+ loaded = false;
+ if (tileCache.containsKey(tileCoordKey)) {
+ tile = /** @type {!ol.Tile} */ (tileCache.get(tileCoordKey));
+ loaded = tile.getState() === ol.Tile.State.LOADED;
+ if (loaded) {
+ loaded = (callback(tile) !== false);
+ }
+ }
+ if (!loaded) {
+ covered = false;
+ }
+ }
+ }
+ return covered;
+};
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {number} Gutter.
+ */
+ol.source.Tile.prototype.getGutter = function(projection) {
+ return 0;
+};
+
+
+/**
+ * Return the key to be used for all tiles in the source.
+ * @return {string} The key for all tiles.
+ * @protected
+ */
+ol.source.Tile.prototype.getKey = function() {
+ return this.key_;
+};
+
+
+/**
+ * Set the value to be used as the key for all tiles in the source.
+ * @param {string} key The key for tiles.
+ * @protected
+ */
+ol.source.Tile.prototype.setKey = function(key) {
+ if (this.key_ !== key) {
+ this.key_ = key;
+ this.changed();
+ }
+};
+
+
+/**
+ * @param {number} z Z.
+ * @param {number} x X.
+ * @param {number} y Y.
+ * @return {string} Key.
+ * @protected
+ */
+ol.source.Tile.prototype.getKeyZXY = ol.tilecoord.getKeyZXY;
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {boolean} Opaque.
+ */
+ol.source.Tile.prototype.getOpaque = function(projection) {
+ return this.opaque_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.Tile.prototype.getResolutions = function() {
+ return this.tileGrid.getResolutions();
+};
+
+
+/**
+ * @abstract
+ * @param {number} z Tile coordinate z.
+ * @param {number} x Tile coordinate x.
+ * @param {number} y Tile coordinate y.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {!ol.Tile} Tile.
+ */
+ol.source.Tile.prototype.getTile = function(z, x, y, pixelRatio, projection) {};
+
+
+/**
+ * Return the tile grid of the tile source.
+ * @return {ol.tilegrid.TileGrid} Tile grid.
+ * @api stable
+ */
+ol.source.Tile.prototype.getTileGrid = function() {
+ return this.tileGrid;
+};
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {!ol.tilegrid.TileGrid} Tile grid.
+ */
+ol.source.Tile.prototype.getTileGridForProjection = function(projection) {
+ if (!this.tileGrid) {
+ return ol.tilegrid.getForProjection(projection);
+ } else {
+ return this.tileGrid;
+ }
+};
+
+
+/**
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {ol.TileCache} Tile cache.
+ * @protected
+ */
+ol.source.Tile.prototype.getTileCacheForProjection = function(projection) {
+ var thisProj = this.getProjection();
+ if (thisProj && !ol.proj.equivalent(thisProj, projection)) {
+ return null;
+ } else {
+ return this.tileCache;
+ }
+};
+
+
+/**
+ * Get the tile pixel ratio for this source. Subclasses may override this
+ * method, which is meant to return a supported pixel ratio that matches the
+ * provided `opt_pixelRatio` as close as possible. When no `opt_pixelRatio` is
+ * provided, it is meant to return `this.tilePixelRatio_`.
+ * @param {number=} opt_pixelRatio Pixel ratio.
+ * @return {number} Tile pixel ratio.
+ */
+ol.source.Tile.prototype.getTilePixelRatio = function(opt_pixelRatio) {
+ return this.tilePixelRatio_;
+};
+
+
+/**
+ * @param {number} z Z.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {ol.Size} Tile size.
+ */
+ol.source.Tile.prototype.getTilePixelSize = function(z, pixelRatio, projection) {
+ var tileGrid = this.getTileGridForProjection(projection);
+ var tilePixelRatio = this.getTilePixelRatio(pixelRatio);
+ var tileSize = ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize);
+ if (tilePixelRatio == 1) {
+ return tileSize;
+ } else {
+ return ol.size.scale(tileSize, tilePixelRatio, this.tmpSize);
+ }
+};
+
+
+/**
+ * Returns a tile coordinate wrapped around the x-axis. When the tile coordinate
+ * is outside the resolution and extent range of the tile grid, `null` will be
+ * returned.
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.proj.Projection=} opt_projection Projection.
+ * @return {ol.TileCoord} Tile coordinate to be passed to the tileUrlFunction or
+ * null if no tile URL should be created for the passed `tileCoord`.
+ */
+ol.source.Tile.prototype.getTileCoordForTileUrlFunction = function(tileCoord, opt_projection) {
+ var projection = opt_projection !== undefined ?
+ opt_projection : this.getProjection();
+ var tileGrid = this.getTileGridForProjection(projection);
+ if (this.getWrapX() && projection.isGlobal()) {
+ tileCoord = ol.tilegrid.wrapX(tileGrid, tileCoord, projection);
+ }
+ return ol.tilecoord.withinExtentAndZ(tileCoord, tileGrid) ? tileCoord : null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.Tile.prototype.refresh = function() {
+ this.tileCache.clear();
+ this.changed();
+};
+
+
+/**
+ * Marks a tile coord as being used, without triggering a load.
+ * @param {number} z Tile coordinate z.
+ * @param {number} x Tile coordinate x.
+ * @param {number} y Tile coordinate y.
+ * @param {ol.proj.Projection} projection Projection.
+ */
+ol.source.Tile.prototype.useTile = ol.nullFunction;
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.source.Tile} instances are instances of this
+ * type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.source.Tile.Event}
+ * @param {string} type Type.
+ * @param {ol.Tile} tile The tile.
+ */
+ol.source.Tile.Event = function(type, tile) {
+
+ ol.events.Event.call(this, type);
+
+ /**
+ * The tile related to the event.
+ * @type {ol.Tile}
+ * @api
+ */
+ this.tile = tile;
+
+};
+ol.inherits(ol.source.Tile.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.source.Tile.EventType = {
+
+ /**
+ * Triggered when a tile starts loading.
+ * @event ol.source.Tile.Event#tileloadstart
+ * @api stable
+ */
+ TILELOADSTART: 'tileloadstart',
+
+ /**
+ * Triggered when a tile finishes loading.
+ * @event ol.source.Tile.Event#tileloadend
+ * @api stable
+ */
+ TILELOADEND: 'tileloadend',
+
+ /**
+ * Triggered if tile loading results in an error.
+ * @event ol.source.Tile.Event#tileloaderror
+ * @api stable
+ */
+ TILELOADERROR: 'tileloaderror'
+
+};
+
+goog.provide('ol.source.UrlTile');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.TileUrlFunction');
+goog.require('ol.source.Tile');
+
+
+/**
+ * @classdesc
+ * Base class for sources providing tiles divided into a tile grid over http.
+ *
+ * @constructor
+ * @fires ol.source.Tile.Event
+ * @extends {ol.source.Tile}
+ * @param {ol.SourceUrlTileOptions} options Image tile options.
+ */
+ol.source.UrlTile = function(options) {
+
+ ol.source.Tile.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ extent: options.extent,
+ logo: options.logo,
+ opaque: options.opaque,
+ projection: options.projection,
+ state: options.state,
+ tileGrid: options.tileGrid,
+ tilePixelRatio: options.tilePixelRatio,
+ wrapX: options.wrapX
+ });
+
+ /**
+ * @protected
+ * @type {ol.TileLoadFunctionType}
+ */
+ this.tileLoadFunction = options.tileLoadFunction;
+
+ /**
+ * @protected
+ * @type {ol.TileUrlFunctionType}
+ */
+ this.tileUrlFunction = this.fixedTileUrlFunction ?
+ this.fixedTileUrlFunction.bind(this) :
+ ol.TileUrlFunction.nullTileUrlFunction;
+
+ /**
+ * @protected
+ * @type {!Array.<string>|null}
+ */
+ this.urls = null;
+
+ if (options.urls) {
+ this.setUrls(options.urls);
+ } else if (options.url) {
+ this.setUrl(options.url);
+ }
+ if (options.tileUrlFunction) {
+ this.setTileUrlFunction(options.tileUrlFunction);
+ }
+
+};
+ol.inherits(ol.source.UrlTile, ol.source.Tile);
+
+
+/**
+ * @type {ol.TileUrlFunctionType|undefined}
+ * @protected
+ */
+ol.source.UrlTile.prototype.fixedTileUrlFunction;
+
+/**
+ * Return the tile load function of the source.
+ * @return {ol.TileLoadFunctionType} TileLoadFunction
+ * @api
+ */
+ol.source.UrlTile.prototype.getTileLoadFunction = function() {
+ return this.tileLoadFunction;
+};
+
+
+/**
+ * Return the tile URL function of the source.
+ * @return {ol.TileUrlFunctionType} TileUrlFunction
+ * @api
+ */
+ol.source.UrlTile.prototype.getTileUrlFunction = function() {
+ return this.tileUrlFunction;
+};
+
+
+/**
+ * Return the URLs used for this source.
+ * When a tileUrlFunction is used instead of url or urls,
+ * null will be returned.
+ * @return {!Array.<string>|null} URLs.
+ * @api
+ */
+ol.source.UrlTile.prototype.getUrls = function() {
+ return this.urls;
+};
+
+
+/**
+ * Handle tile change events.
+ * @param {ol.events.Event} event Event.
+ * @protected
+ */
+ol.source.UrlTile.prototype.handleTileChange = function(event) {
+ var tile = /** @type {ol.Tile} */ (event.target);
+ switch (tile.getState()) {
+ case ol.Tile.State.LOADING:
+ this.dispatchEvent(
+ new ol.source.Tile.Event(ol.source.Tile.EventType.TILELOADSTART, tile));
+ break;
+ case ol.Tile.State.LOADED:
+ this.dispatchEvent(
+ new ol.source.Tile.Event(ol.source.Tile.EventType.TILELOADEND, tile));
+ break;
+ case ol.Tile.State.ERROR:
+ this.dispatchEvent(
+ new ol.source.Tile.Event(ol.source.Tile.EventType.TILELOADERROR, tile));
+ break;
+ default:
+ // pass
+ }
+};
+
+
+/**
+ * Set the tile load function of the source.
+ * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
+ * @api
+ */
+ol.source.UrlTile.prototype.setTileLoadFunction = function(tileLoadFunction) {
+ this.tileCache.clear();
+ this.tileLoadFunction = tileLoadFunction;
+ this.changed();
+};
+
+
+/**
+ * Set the tile URL function of the source.
+ * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function.
+ * @param {string=} opt_key Optional new tile key for the source.
+ * @api
+ */
+ol.source.UrlTile.prototype.setTileUrlFunction = function(tileUrlFunction, opt_key) {
+ this.tileUrlFunction = tileUrlFunction;
+ if (typeof opt_key !== 'undefined') {
+ this.setKey(opt_key);
+ } else {
+ this.changed();
+ }
+};
+
+
+/**
+ * Set the URL to use for requests.
+ * @param {string} url URL.
+ * @api stable
+ */
+ol.source.UrlTile.prototype.setUrl = function(url) {
+ var urls = this.urls = ol.TileUrlFunction.expandUrl(url);
+ this.setTileUrlFunction(this.fixedTileUrlFunction ?
+ this.fixedTileUrlFunction.bind(this) :
+ ol.TileUrlFunction.createFromTemplates(urls, this.tileGrid), url);
+};
+
+
+/**
+ * Set the URLs to use for requests.
+ * @param {Array.<string>} urls URLs.
+ * @api stable
+ */
+ol.source.UrlTile.prototype.setUrls = function(urls) {
+ this.urls = urls;
+ var key = urls.join('\n');
+ this.setTileUrlFunction(this.fixedTileUrlFunction ?
+ this.fixedTileUrlFunction.bind(this) :
+ ol.TileUrlFunction.createFromTemplates(urls, this.tileGrid), key);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.UrlTile.prototype.useTile = function(z, x, y) {
+ var tileCoordKey = this.getKeyZXY(z, x, y);
+ if (this.tileCache.containsKey(tileCoordKey)) {
+ this.tileCache.get(tileCoordKey);
+ }
+};
+
+goog.provide('ol.source.TileImage');
+
+goog.require('ol');
+goog.require('ol.ImageTile');
+goog.require('ol.Tile');
+goog.require('ol.TileCache');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.proj');
+goog.require('ol.reproj.Tile');
+goog.require('ol.source.UrlTile');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * Base class for sources providing images divided into a tile grid.
+ *
+ * @constructor
+ * @fires ol.source.Tile.Event
+ * @extends {ol.source.UrlTile}
+ * @param {olx.source.TileImageOptions} options Image tile options.
+ * @api
+ */
+ol.source.TileImage = function(options) {
+
+ ol.source.UrlTile.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ extent: options.extent,
+ logo: options.logo,
+ opaque: options.opaque,
+ projection: options.projection,
+ state: options.state,
+ tileGrid: options.tileGrid,
+ tileLoadFunction: options.tileLoadFunction ?
+ options.tileLoadFunction : ol.source.TileImage.defaultTileLoadFunction,
+ tilePixelRatio: options.tilePixelRatio,
+ tileUrlFunction: options.tileUrlFunction,
+ url: options.url,
+ urls: options.urls,
+ wrapX: options.wrapX
+ });
+
+ /**
+ * @protected
+ * @type {?string}
+ */
+ this.crossOrigin =
+ options.crossOrigin !== undefined ? options.crossOrigin : null;
+
+ /**
+ * @protected
+ * @type {function(new: ol.ImageTile, ol.TileCoord, ol.Tile.State, string,
+ * ?string, ol.TileLoadFunctionType)}
+ */
+ this.tileClass = options.tileClass !== undefined ?
+ options.tileClass : ol.ImageTile;
+
+ /**
+ * @protected
+ * @type {Object.<string, ol.TileCache>}
+ */
+ this.tileCacheForProjection = {};
+
+ /**
+ * @protected
+ * @type {Object.<string, ol.tilegrid.TileGrid>}
+ */
+ this.tileGridForProjection = {};
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.renderReprojectionEdges_ = false;
+};
+ol.inherits(ol.source.TileImage, ol.source.UrlTile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.canExpireCache = function() {
+ if (!ol.ENABLE_RASTER_REPROJECTION) {
+ return ol.source.UrlTile.prototype.canExpireCache.call(this);
+ }
+ if (this.tileCache.canExpireCache()) {
+ return true;
+ } else {
+ for (var key in this.tileCacheForProjection) {
+ if (this.tileCacheForProjection[key].canExpireCache()) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.expireCache = function(projection, usedTiles) {
+ if (!ol.ENABLE_RASTER_REPROJECTION) {
+ ol.source.UrlTile.prototype.expireCache.call(this, projection, usedTiles);
+ return;
+ }
+ var usedTileCache = this.getTileCacheForProjection(projection);
+
+ this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {});
+ for (var id in this.tileCacheForProjection) {
+ var tileCache = this.tileCacheForProjection[id];
+ tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {});
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.getGutter = function(projection) {
+ if (ol.ENABLE_RASTER_REPROJECTION &&
+ this.getProjection() && projection &&
+ !ol.proj.equivalent(this.getProjection(), projection)) {
+ return 0;
+ } else {
+ return this.getGutterInternal();
+ }
+};
+
+
+/**
+ * @protected
+ * @return {number} Gutter.
+ */
+ol.source.TileImage.prototype.getGutterInternal = function() {
+ return 0;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.getOpaque = function(projection) {
+ if (ol.ENABLE_RASTER_REPROJECTION &&
+ this.getProjection() && projection &&
+ !ol.proj.equivalent(this.getProjection(), projection)) {
+ return false;
+ } else {
+ return ol.source.UrlTile.prototype.getOpaque.call(this, projection);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.getTileGridForProjection = function(projection) {
+ if (!ol.ENABLE_RASTER_REPROJECTION) {
+ return ol.source.UrlTile.prototype.getTileGridForProjection.call(this, projection);
+ }
+ var thisProj = this.getProjection();
+ if (this.tileGrid &&
+ (!thisProj || ol.proj.equivalent(thisProj, projection))) {
+ return this.tileGrid;
+ } else {
+ var projKey = ol.getUid(projection).toString();
+ if (!(projKey in this.tileGridForProjection)) {
+ this.tileGridForProjection[projKey] =
+ ol.tilegrid.getForProjection(projection);
+ }
+ return /** @type {!ol.tilegrid.TileGrid} */ (this.tileGridForProjection[projKey]);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.getTileCacheForProjection = function(projection) {
+ if (!ol.ENABLE_RASTER_REPROJECTION) {
+ return ol.source.UrlTile.prototype.getTileCacheForProjection.call(this, projection);
+ }
+ var thisProj = this.getProjection();
+ if (!thisProj || ol.proj.equivalent(thisProj, projection)) {
+ return this.tileCache;
+ } else {
+ var projKey = ol.getUid(projection).toString();
+ if (!(projKey in this.tileCacheForProjection)) {
+ this.tileCacheForProjection[projKey] = new ol.TileCache();
+ }
+ return this.tileCacheForProjection[projKey];
+ }
+};
+
+
+/**
+ * @param {number} z Tile coordinate z.
+ * @param {number} x Tile coordinate x.
+ * @param {number} y Tile coordinate y.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {string} key The key set on the tile.
+ * @return {!ol.Tile} Tile.
+ * @private
+ */
+ol.source.TileImage.prototype.createTile_ = function(z, x, y, pixelRatio, projection, key) {
+ var tileCoord = [z, x, y];
+ var urlTileCoord = this.getTileCoordForTileUrlFunction(
+ tileCoord, projection);
+ var tileUrl = urlTileCoord ?
+ this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined;
+ var tile = new this.tileClass(
+ tileCoord,
+ tileUrl !== undefined ? ol.Tile.State.IDLE : ol.Tile.State.EMPTY,
+ tileUrl !== undefined ? tileUrl : '',
+ this.crossOrigin,
+ this.tileLoadFunction);
+ tile.key = key;
+ ol.events.listen(tile, ol.events.EventType.CHANGE,
+ this.handleTileChange, this);
+ return tile;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection) {
+ if (!ol.ENABLE_RASTER_REPROJECTION ||
+ !this.getProjection() ||
+ !projection ||
+ ol.proj.equivalent(this.getProjection(), projection)) {
+ return this.getTileInternal(z, x, y, pixelRatio, /** @type {!ol.proj.Projection} */ (projection));
+ } else {
+ var cache = this.getTileCacheForProjection(projection);
+ var tileCoord = [z, x, y];
+ var tile;
+ var tileCoordKey = this.getKeyZXY.apply(this, tileCoord);
+ if (cache.containsKey(tileCoordKey)) {
+ tile = /** @type {!ol.Tile} */ (cache.get(tileCoordKey));
+ }
+ var key = this.getKey();
+ if (tile && tile.key == key) {
+ return tile;
+ } else {
+ var sourceProjection = /** @type {!ol.proj.Projection} */ (this.getProjection());
+ var sourceTileGrid = this.getTileGridForProjection(sourceProjection);
+ var targetTileGrid = this.getTileGridForProjection(projection);
+ var wrappedTileCoord =
+ this.getTileCoordForTileUrlFunction(tileCoord, projection);
+ var newTile = new ol.reproj.Tile(
+ sourceProjection, sourceTileGrid,
+ projection, targetTileGrid,
+ tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio),
+ this.getGutterInternal(),
+ function(z, x, y, pixelRatio) {
+ return this.getTileInternal(z, x, y, pixelRatio, sourceProjection);
+ }.bind(this), this.reprojectionErrorThreshold_,
+ this.renderReprojectionEdges_);
+ newTile.key = key;
+
+ if (tile) {
+ newTile.interimTile = tile;
+ cache.replace(tileCoordKey, newTile);
+ } else {
+ cache.set(tileCoordKey, newTile);
+ }
+ return newTile;
+ }
+ }
+};
+
+
+/**
+ * @param {number} z Tile coordinate z.
+ * @param {number} x Tile coordinate x.
+ * @param {number} y Tile coordinate y.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {!ol.proj.Projection} projection Projection.
+ * @return {!ol.Tile} Tile.
+ * @protected
+ */
+ol.source.TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, projection) {
+ var tile = null;
+ var tileCoordKey = this.getKeyZXY(z, x, y);
+ var key = this.getKey();
+ if (!this.tileCache.containsKey(tileCoordKey)) {
+ tile = this.createTile_(z, x, y, pixelRatio, projection, key);
+ this.tileCache.set(tileCoordKey, tile);
+ } else {
+ tile = this.tileCache.get(tileCoordKey);
+ if (tile.key != key) {
+ // The source's params changed. If the tile has an interim tile and if we
+ // can use it then we use it. Otherwise we create a new tile. In both
+ // cases we attempt to assign an interim tile to the new tile.
+ var interimTile = tile;
+ tile = this.createTile_(z, x, y, pixelRatio, projection, key);
+
+ //make the new tile the head of the list,
+ if (interimTile.getState() == ol.Tile.State.IDLE) {
+ //the old tile hasn't begun loading yet, and is now outdated, so we can simply discard it
+ tile.interimTile = interimTile.interimTile;
+ } else {
+ tile.interimTile = interimTile;
+ }
+ tile.refreshInterimChain();
+ this.tileCache.replace(tileCoordKey, tile);
+ }
+ }
+ return tile;
+};
+
+
+/**
+ * Sets whether to render reprojection edges or not (usually for debugging).
+ * @param {boolean} render Render the edges.
+ * @api
+ */
+ol.source.TileImage.prototype.setRenderReprojectionEdges = function(render) {
+ if (!ol.ENABLE_RASTER_REPROJECTION ||
+ this.renderReprojectionEdges_ == render) {
+ return;
+ }
+ this.renderReprojectionEdges_ = render;
+ for (var id in this.tileCacheForProjection) {
+ this.tileCacheForProjection[id].clear();
+ }
+ this.changed();
+};
+
+
+/**
+ * Sets the tile grid to use when reprojecting the tiles to the given
+ * projection instead of the default tile grid for the projection.
+ *
+ * This can be useful when the default tile grid cannot be created
+ * (e.g. projection has no extent defined) or
+ * for optimization reasons (custom tile size, resolutions, ...).
+ *
+ * @param {ol.ProjectionLike} projection Projection.
+ * @param {ol.tilegrid.TileGrid} tilegrid Tile grid to use for the projection.
+ * @api
+ */
+ol.source.TileImage.prototype.setTileGridForProjection = function(projection, tilegrid) {
+ if (ol.ENABLE_RASTER_REPROJECTION) {
+ var proj = ol.proj.get(projection);
+ if (proj) {
+ var projKey = ol.getUid(proj).toString();
+ if (!(projKey in this.tileGridForProjection)) {
+ this.tileGridForProjection[projKey] = tilegrid;
+ }
+ }
+ }
+};
+
+
+/**
+ * @param {ol.ImageTile} imageTile Image tile.
+ * @param {string} src Source.
+ */
+ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) {
+ imageTile.getImage().src = src;
+};
+
+goog.provide('ol.source.BingMaps');
+
+goog.require('ol');
+goog.require('ol.Attribution');
+goog.require('ol.TileUrlFunction');
+goog.require('ol.extent');
+goog.require('ol.net');
+goog.require('ol.proj');
+goog.require('ol.source.State');
+goog.require('ol.source.TileImage');
+goog.require('ol.tilecoord');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * Layer source for Bing Maps tile data.
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.BingMapsOptions} options Bing Maps options.
+ * @api stable
+ */
+ol.source.BingMaps = function(options) {
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.hidpi_ = options.hidpi !== undefined ? options.hidpi : false;
+
+ ol.source.TileImage.call(this, {
+ cacheSize: options.cacheSize,
+ crossOrigin: 'anonymous',
+ opaque: true,
+ projection: ol.proj.get('EPSG:3857'),
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ state: ol.source.State.LOADING,
+ tileLoadFunction: options.tileLoadFunction,
+ tilePixelRatio: this.hidpi_ ? 2 : 1,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.culture_ = options.culture !== undefined ? options.culture : 'en-us';
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.apiKey_ = options.key;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.imagerySet_ = options.imagerySet;
+
+ var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' +
+ this.imagerySet_ +
+ '?uriScheme=https&include=ImageryProviders&key=' + this.apiKey_;
+
+ ol.net.jsonp(url, this.handleImageryMetadataResponse.bind(this), undefined,
+ 'jsonp');
+
+};
+ol.inherits(ol.source.BingMaps, ol.source.TileImage);
+
+
+/**
+ * The attribution containing a link to the Microsoft® Bing™ Maps Platform APIs’
+ * Terms Of Use.
+ * @const
+ * @type {ol.Attribution}
+ * @api
+ */
+ol.source.BingMaps.TOS_ATTRIBUTION = new ol.Attribution({
+ html: '<a class="ol-attribution-bing-tos" ' +
+ 'href="http://www.microsoft.com/maps/product/terms.html">' +
+ 'Terms of Use</a>'
+});
+
+
+/**
+ * Get the api key used for this source.
+ *
+ * @return {string} The api key.
+ * @api
+ */
+ol.source.BingMaps.prototype.getApiKey = function() {
+ return this.apiKey_;
+};
+
+
+/**
+ * Get the imagery set associated with this source.
+ *
+ * @return {string} The imagery set.
+ * @api
+ */
+ol.source.BingMaps.prototype.getImagerySet = function() {
+ return this.imagerySet_;
+};
+
+
+/**
+ * @param {BingMapsImageryMetadataResponse} response Response.
+ */
+ol.source.BingMaps.prototype.handleImageryMetadataResponse = function(response) {
+
+ if (response.statusCode != 200 ||
+ response.statusDescription != 'OK' ||
+ response.authenticationResultCode != 'ValidCredentials' ||
+ response.resourceSets.length != 1 ||
+ response.resourceSets[0].resources.length != 1) {
+ this.setState(ol.source.State.ERROR);
+ return;
+ }
+
+ var brandLogoUri = response.brandLogoUri;
+ if (brandLogoUri.indexOf('https') == -1) {
+ brandLogoUri = brandLogoUri.replace('http', 'https');
+ }
+ //var copyright = response.copyright; // FIXME do we need to display this?
+ var resource = response.resourceSets[0].resources[0];
+ ol.DEBUG && console.assert(resource.imageWidth == resource.imageHeight,
+ 'resource has imageWidth equal to imageHeight, i.e. is square');
+ var maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_;
+
+ var sourceProjection = this.getProjection();
+ var extent = ol.tilegrid.extentFromProjection(sourceProjection);
+ var tileSize = resource.imageWidth == resource.imageHeight ?
+ resource.imageWidth : [resource.imageWidth, resource.imageHeight];
+ var tileGrid = ol.tilegrid.createXYZ({
+ extent: extent,
+ minZoom: resource.zoomMin,
+ maxZoom: maxZoom,
+ tileSize: tileSize / this.getTilePixelRatio()
+ });
+ this.tileGrid = tileGrid;
+
+ var culture = this.culture_;
+ var hidpi = this.hidpi_;
+ this.tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions(
+ resource.imageUrlSubdomains.map(function(subdomain) {
+ var quadKeyTileCoord = [0, 0, 0];
+ var imageUrl = resource.imageUrl
+ .replace('{subdomain}', subdomain)
+ .replace('{culture}', culture);
+ return (
+ /**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string|undefined} Tile URL.
+ */
+ function(tileCoord, pixelRatio, projection) {
+ ol.DEBUG && console.assert(ol.proj.equivalent(
+ projection, sourceProjection),
+ 'projections are equivalent');
+ if (!tileCoord) {
+ return undefined;
+ } else {
+ ol.tilecoord.createOrUpdate(tileCoord[0], tileCoord[1],
+ -tileCoord[2] - 1, quadKeyTileCoord);
+ var url = imageUrl;
+ if (hidpi) {
+ url += '&dpi=d1&device=mobile';
+ }
+ return url.replace('{quadkey}', ol.tilecoord.quadKey(
+ quadKeyTileCoord));
+ }
+ });
+ }));
+
+ if (resource.imageryProviders) {
+ var transform = ol.proj.getTransformFromProjections(
+ ol.proj.get('EPSG:4326'), this.getProjection());
+
+ var attributions = resource.imageryProviders.map(function(imageryProvider) {
+ var html = imageryProvider.attribution;
+ /** @type {Object.<string, Array.<ol.TileRange>>} */
+ var tileRanges = {};
+ imageryProvider.coverageAreas.forEach(function(coverageArea) {
+ var minZ = coverageArea.zoomMin;
+ var maxZ = Math.min(coverageArea.zoomMax, maxZoom);
+ var bbox = coverageArea.bbox;
+ var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]];
+ var extent = ol.extent.applyTransform(epsg4326Extent, transform);
+ var tileRange, z, zKey;
+ for (z = minZ; z <= maxZ; ++z) {
+ zKey = z.toString();
+ tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
+ if (zKey in tileRanges) {
+ tileRanges[zKey].push(tileRange);
+ } else {
+ tileRanges[zKey] = [tileRange];
+ }
+ }
+ });
+ return new ol.Attribution({html: html, tileRanges: tileRanges});
+ });
+ attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION);
+ this.setAttributions(attributions);
+ }
+
+ this.setLogo(brandLogoUri);
+
+ this.setState(ol.source.State.READY);
+
+};
+
+goog.provide('ol.source.XYZ');
+
+goog.require('ol');
+goog.require('ol.source.TileImage');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * Layer source for tile data with URLs in a set XYZ format that are
+ * defined in a URL template. By default, this follows the widely-used
+ * Google grid where `x` 0 and `y` 0 are in the top left. Grids like
+ * TMS where `x` 0 and `y` 0 are in the bottom left can be used by
+ * using the `{-y}` placeholder in the URL template, so long as the
+ * source does not have a custom tile grid. In this case,
+ * {@link ol.source.TileImage} can be used with a `tileUrlFunction`
+ * such as:
+ *
+ * tileUrlFunction: function(coordinate) {
+ * return 'http://mapserver.com/' + coordinate[0] + '/' +
+ * coordinate[1] + '/' + coordinate[2] + '.png';
+ * }
+ *
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.XYZOptions=} opt_options XYZ options.
+ * @api stable
+ */
+ol.source.XYZ = function(opt_options) {
+ var options = opt_options || {};
+ var projection = options.projection !== undefined ?
+ options.projection : 'EPSG:3857';
+
+ var tileGrid = options.tileGrid !== undefined ? options.tileGrid :
+ ol.tilegrid.createXYZ({
+ extent: ol.tilegrid.extentFromProjection(projection),
+ maxZoom: options.maxZoom,
+ minZoom: options.minZoom,
+ tileSize: options.tileSize
+ });
+
+ ol.source.TileImage.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ logo: options.logo,
+ opaque: options.opaque,
+ projection: projection,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileGrid: tileGrid,
+ tileLoadFunction: options.tileLoadFunction,
+ tilePixelRatio: options.tilePixelRatio,
+ tileUrlFunction: options.tileUrlFunction,
+ url: options.url,
+ urls: options.urls,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+};
+ol.inherits(ol.source.XYZ, ol.source.TileImage);
+
+goog.provide('ol.source.CartoDB');
+
+goog.require('ol');
+goog.require('ol.obj');
+goog.require('ol.source.State');
+goog.require('ol.source.XYZ');
+
+
+/**
+ * @classdesc
+ * Layer source for the CartoDB tiles.
+ *
+ * @constructor
+ * @extends {ol.source.XYZ}
+ * @param {olx.source.CartoDBOptions} options CartoDB options.
+ * @api
+ */
+ol.source.CartoDB = function(options) {
+
+ /**
+ * @type {string}
+ * @private
+ */
+ this.account_ = options.account;
+
+ /**
+ * @type {string}
+ * @private
+ */
+ this.mapId_ = options.map || '';
+
+ /**
+ * @type {!Object}
+ * @private
+ */
+ this.config_ = options.config || {};
+
+ /**
+ * @type {!Object.<string, CartoDBLayerInfo>}
+ * @private
+ */
+ this.templateCache_ = {};
+
+ ol.source.XYZ.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ logo: options.logo,
+ maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18,
+ minZoom: options.minZoom,
+ projection: options.projection,
+ state: ol.source.State.LOADING,
+ wrapX: options.wrapX
+ });
+ this.initializeMap_();
+};
+ol.inherits(ol.source.CartoDB, ol.source.XYZ);
+
+
+/**
+ * Returns the current config.
+ * @return {!Object} The current configuration.
+ * @api
+ */
+ol.source.CartoDB.prototype.getConfig = function() {
+ return this.config_;
+};
+
+
+/**
+ * Updates the carto db config.
+ * @param {Object} config a key-value lookup. Values will replace current values
+ * in the config.
+ * @api
+ */
+ol.source.CartoDB.prototype.updateConfig = function(config) {
+ ol.obj.assign(this.config_, config);
+ this.initializeMap_();
+};
+
+
+/**
+ * Sets the CartoDB config
+ * @param {Object} config In the case of anonymous maps, a CartoDB configuration
+ * object.
+ * If using named maps, a key-value lookup with the template parameters.
+ * @api
+ */
+ol.source.CartoDB.prototype.setConfig = function(config) {
+ this.config_ = config || {};
+ this.initializeMap_();
+};
+
+
+/**
+ * Issue a request to initialize the CartoDB map.
+ * @private
+ */
+ol.source.CartoDB.prototype.initializeMap_ = function() {
+ var paramHash = JSON.stringify(this.config_);
+ if (this.templateCache_[paramHash]) {
+ this.applyTemplate_(this.templateCache_[paramHash]);
+ return;
+ }
+ var mapUrl = 'https://' + this.account_ + '.cartodb.com/api/v1/map';
+
+ if (this.mapId_) {
+ mapUrl += '/named/' + this.mapId_;
+ }
+
+ var client = new XMLHttpRequest();
+ client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash));
+ client.addEventListener('error', this.handleInitError_.bind(this));
+ client.open('POST', mapUrl);
+ client.setRequestHeader('Content-type', 'application/json');
+ client.send(JSON.stringify(this.config_));
+};
+
+
+/**
+ * Handle map initialization response.
+ * @param {string} paramHash a hash representing the parameter set that was used
+ * for the request
+ * @param {Event} event Event.
+ * @private
+ */
+ol.source.CartoDB.prototype.handleInitResponse_ = function(paramHash, event) {
+ var client = /** @type {XMLHttpRequest} */ (event.target);
+ // status will be 0 for file:// urls
+ if (!client.status || client.status >= 200 && client.status < 300) {
+ var response;
+ try {
+ response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText));
+ } catch (err) {
+ this.setState(ol.source.State.ERROR);
+ return;
+ }
+ this.applyTemplate_(response);
+ this.templateCache_[paramHash] = response;
+ this.setState(ol.source.State.READY);
+ } else {
+ this.setState(ol.source.State.ERROR);
+ }
+};
+
+
+/**
+ * @private
+ * @param {Event} event Event.
+ */
+ol.source.CartoDB.prototype.handleInitError_ = function(event) {
+ this.setState(ol.source.State.ERROR);
+};
+
+
+/**
+ * Apply the new tile urls returned by carto db
+ * @param {CartoDBLayerInfo} data Result of carto db call.
+ * @private
+ */
+ol.source.CartoDB.prototype.applyTemplate_ = function(data) {
+ var tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ +
+ '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png';
+ this.setUrl(tilesUrl);
+};
+
+// FIXME keep cluster cache by resolution ?
+// FIXME distance not respected because of the centroid
+
+goog.provide('ol.source.Cluster');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.Feature');
+goog.require('ol.coordinate');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.geom.Point');
+goog.require('ol.source.Vector');
+
+
+/**
+ * @classdesc
+ * Layer source to cluster vector data. Works out of the box with point
+ * geometries. For other geometry types, or if not all geometries should be
+ * considered for clustering, a custom `geometryFunction` can be defined.
+ *
+ * @constructor
+ * @param {olx.source.ClusterOptions} options Constructor options.
+ * @extends {ol.source.Vector}
+ * @api
+ */
+ol.source.Cluster = function(options) {
+ ol.source.Vector.call(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ logo: options.logo,
+ projection: options.projection,
+ wrapX: options.wrapX
+ });
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.resolution_ = undefined;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.distance_ = options.distance !== undefined ? options.distance : 20;
+
+ /**
+ * @type {Array.<ol.Feature>}
+ * @private
+ */
+ this.features_ = [];
+
+ /**
+ * @param {ol.Feature} feature Feature.
+ * @return {ol.geom.Point} Cluster calculation point.
+ */
+ this.geometryFunction_ = options.geometryFunction || function(feature) {
+ var geometry = /** @type {ol.geom.Point} */ (feature.getGeometry());
+ ol.asserts.assert(geometry instanceof ol.geom.Point,
+ 10); // The default `geometryFunction` can only handle `ol.geom.Point` geometries
+ return geometry;
+ };
+
+ /**
+ * @type {ol.source.Vector}
+ * @private
+ */
+ this.source_ = options.source;
+
+ this.source_.on(ol.events.EventType.CHANGE,
+ ol.source.Cluster.prototype.refresh_, this);
+};
+ol.inherits(ol.source.Cluster, ol.source.Vector);
+
+
+/**
+ * Get a reference to the wrapped source.
+ * @return {ol.source.Vector} Source.
+ * @api
+ */
+ol.source.Cluster.prototype.getSource = function() {
+ return this.source_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.Cluster.prototype.loadFeatures = function(extent, resolution,
+ projection) {
+ this.source_.loadFeatures(extent, resolution, projection);
+ if (resolution !== this.resolution_) {
+ this.clear();
+ this.resolution_ = resolution;
+ this.cluster_();
+ this.addFeatures(this.features_);
+ }
+};
+
+
+/**
+ * Set the distance in pixels between clusters.
+ * @param {number} distance The distance in pixels.
+ * @api
+ */
+ol.source.Cluster.prototype.setDistance = function(distance) {
+ this.distance_ = distance;
+ this.refresh_();
+};
+
+
+/**
+ * handle the source changing
+ * @private
+ */
+ol.source.Cluster.prototype.refresh_ = function() {
+ this.clear();
+ this.cluster_();
+ this.addFeatures(this.features_);
+ this.changed();
+};
+
+
+/**
+ * @private
+ */
+ol.source.Cluster.prototype.cluster_ = function() {
+ if (this.resolution_ === undefined) {
+ return;
+ }
+ this.features_.length = 0;
+ var extent = ol.extent.createEmpty();
+ var mapDistance = this.distance_ * this.resolution_;
+ var features = this.source_.getFeatures();
+
+ /**
+ * @type {!Object.<string, boolean>}
+ */
+ var clustered = {};
+
+ for (var i = 0, ii = features.length; i < ii; i++) {
+ var feature = features[i];
+ if (!(ol.getUid(feature).toString() in clustered)) {
+ var geometry = this.geometryFunction_(feature);
+ if (geometry) {
+ var coordinates = geometry.getCoordinates();
+ ol.extent.createOrUpdateFromCoordinate(coordinates, extent);
+ ol.extent.buffer(extent, mapDistance, extent);
+
+ var neighbors = this.source_.getFeaturesInExtent(extent);
+ ol.DEBUG && console.assert(neighbors.length >= 1, 'at least one neighbor found');
+ neighbors = neighbors.filter(function(neighbor) {
+ var uid = ol.getUid(neighbor).toString();
+ if (!(uid in clustered)) {
+ clustered[uid] = true;
+ return true;
+ } else {
+ return false;
+ }
+ });
+ this.features_.push(this.createCluster_(neighbors));
+ }
+ }
+ }
+ ol.DEBUG && console.assert(
+ Object.keys(clustered).length == this.source_.getFeatures().length,
+ 'number of clustered equals number of features in the source');
+};
+
+
+/**
+ * @param {Array.<ol.Feature>} features Features
+ * @return {ol.Feature} The cluster feature.
+ * @private
+ */
+ol.source.Cluster.prototype.createCluster_ = function(features) {
+ var centroid = [0, 0];
+ for (var i = features.length - 1; i >= 0; --i) {
+ var geometry = this.geometryFunction_(features[i]);
+ if (geometry) {
+ ol.coordinate.add(centroid, geometry.getCoordinates());
+ } else {
+ features.splice(i, 1);
+ }
+ }
+ ol.coordinate.scale(centroid, 1 / features.length);
+
+ var cluster = new ol.Feature(new ol.geom.Point(centroid));
+ cluster.set('features', features);
+ return cluster;
+};
+
+goog.provide('ol.uri');
+
+
+/**
+ * Appends query parameters to a URI.
+ *
+ * @param {string} uri The original URI, which may already have query data.
+ * @param {!Object} params An object where keys are URI-encoded parameter keys,
+ * and the values are arbitrary types or arrays.
+ * @return {string} The new URI.
+ */
+ol.uri.appendParams = function(uri, params) {
+ var keyParams = [];
+ // Skip any null or undefined parameter values
+ Object.keys(params).forEach(function(k) {
+ if (params[k] !== null && params[k] !== undefined) {
+ keyParams.push(k + '=' + encodeURIComponent(params[k]));
+ }
+ });
+ var qs = keyParams.join('&');
+ // remove any trailing ? or &
+ uri = uri.replace(/[?&]$/, '');
+ // append ? or & depending on whether uri has existing parameters
+ uri = uri.indexOf('?') === -1 ? uri + '?' : uri + '&';
+ return uri + qs;
+};
+
+goog.provide('ol.source.ImageArcGISRest');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.asserts');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.source.Image');
+goog.require('ol.uri');
+
+
+/**
+ * @classdesc
+ * Source for data from ArcGIS Rest services providing single, untiled images.
+ * Useful when underlying map service has labels.
+ *
+ * If underlying map service is not using labels,
+ * take advantage of ol image caching and use
+ * {@link ol.source.TileArcGISRest} data source.
+ *
+ * @constructor
+ * @fires ol.source.Image.Event
+ * @extends {ol.source.Image}
+ * @param {olx.source.ImageArcGISRestOptions=} opt_options Image ArcGIS Rest Options.
+ * @api
+ */
+ol.source.ImageArcGISRest = function(opt_options) {
+
+ var options = opt_options || {};
+
+ ol.source.Image.call(this, {
+ attributions: options.attributions,
+ logo: options.logo,
+ projection: options.projection,
+ resolutions: options.resolutions
+ });
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ =
+ options.crossOrigin !== undefined ? options.crossOrigin : null;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.url_ = options.url;
+
+ /**
+ * @private
+ * @type {ol.ImageLoadFunctionType}
+ */
+ this.imageLoadFunction_ = options.imageLoadFunction !== undefined ?
+ options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
+
+
+ /**
+ * @private
+ * @type {!Object}
+ */
+ this.params_ = options.params || {};
+
+ /**
+ * @private
+ * @type {ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.imageSize_ = [0, 0];
+
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5;
+
+};
+ol.inherits(ol.source.ImageArcGISRest, ol.source.Image);
+
+
+/**
+ * Get the user-provided params, i.e. those passed to the constructor through
+ * the "params" option, and possibly updated using the updateParams method.
+ * @return {Object} Params.
+ * @api stable
+ */
+ol.source.ImageArcGISRest.prototype.getParams = function() {
+ return this.params_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageArcGISRest.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {
+
+ if (this.url_ === undefined) {
+ return null;
+ }
+
+ resolution = this.findNearestResolution(resolution);
+
+ var image = this.image_;
+ if (image &&
+ this.renderedRevision_ == this.getRevision() &&
+ image.getResolution() == resolution &&
+ image.getPixelRatio() == pixelRatio &&
+ ol.extent.containsExtent(image.getExtent(), extent)) {
+ return image;
+ }
+
+ var params = {
+ 'F': 'image',
+ 'FORMAT': 'PNG32',
+ 'TRANSPARENT': true
+ };
+ ol.obj.assign(params, this.params_);
+
+ extent = extent.slice();
+ var centerX = (extent[0] + extent[2]) / 2;
+ var centerY = (extent[1] + extent[3]) / 2;
+ if (this.ratio_ != 1) {
+ var halfWidth = this.ratio_ * ol.extent.getWidth(extent) / 2;
+ var halfHeight = this.ratio_ * ol.extent.getHeight(extent) / 2;
+ extent[0] = centerX - halfWidth;
+ extent[1] = centerY - halfHeight;
+ extent[2] = centerX + halfWidth;
+ extent[3] = centerY + halfHeight;
+ }
+
+ var imageResolution = resolution / pixelRatio;
+
+ // Compute an integer width and height.
+ var width = Math.ceil(ol.extent.getWidth(extent) / imageResolution);
+ var height = Math.ceil(ol.extent.getHeight(extent) / imageResolution);
+
+ // Modify the extent to match the integer width and height.
+ extent[0] = centerX - imageResolution * width / 2;
+ extent[2] = centerX + imageResolution * width / 2;
+ extent[1] = centerY - imageResolution * height / 2;
+ extent[3] = centerY + imageResolution * height / 2;
+
+ this.imageSize_[0] = width;
+ this.imageSize_[1] = height;
+
+ var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio,
+ projection, params);
+
+ this.image_ = new ol.Image(extent, resolution, pixelRatio,
+ this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_);
+
+ this.renderedRevision_ = this.getRevision();
+
+ ol.events.listen(this.image_, ol.events.EventType.CHANGE,
+ this.handleImageChange, this);
+
+ return this.image_;
+
+};
+
+
+/**
+ * Return the image load function of the source.
+ * @return {ol.ImageLoadFunctionType} The image load function.
+ * @api
+ */
+ol.source.ImageArcGISRest.prototype.getImageLoadFunction = function() {
+ return this.imageLoadFunction_;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {Object} params Params.
+ * @return {string} Request URL.
+ * @private
+ */
+ol.source.ImageArcGISRest.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) {
+
+ ol.DEBUG && console.assert(this.url_ !== undefined, 'url is defined');
+
+ // ArcGIS Server only wants the numeric portion of the projection ID.
+ var srid = projection.getCode().split(':').pop();
+
+ params['SIZE'] = size[0] + ',' + size[1];
+ params['BBOX'] = extent.join(',');
+ params['BBOXSR'] = srid;
+ params['IMAGESR'] = srid;
+ params['DPI'] = 90 * pixelRatio;
+
+ var url = this.url_;
+
+ var modifiedUrl = url
+ .replace(/MapServer\/?$/, 'MapServer/export')
+ .replace(/ImageServer\/?$/, 'ImageServer/exportImage');
+ if (modifiedUrl == url) {
+ ol.asserts.assert(false, 50); // `options.featureTypes` should be an Array
+ }
+ return ol.uri.appendParams(modifiedUrl, params);
+};
+
+
+/**
+ * Return the URL used for this ArcGIS source.
+ * @return {string|undefined} URL.
+ * @api stable
+ */
+ol.source.ImageArcGISRest.prototype.getUrl = function() {
+ return this.url_;
+};
+
+
+/**
+ * Set the image load function of the source.
+ * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
+ * @api
+ */
+ol.source.ImageArcGISRest.prototype.setImageLoadFunction = function(imageLoadFunction) {
+ this.image_ = null;
+ this.imageLoadFunction_ = imageLoadFunction;
+ this.changed();
+};
+
+
+/**
+ * Set the URL to use for requests.
+ * @param {string|undefined} url URL.
+ * @api stable
+ */
+ol.source.ImageArcGISRest.prototype.setUrl = function(url) {
+ if (url != this.url_) {
+ this.url_ = url;
+ this.image_ = null;
+ this.changed();
+ }
+};
+
+
+/**
+ * Update the user-provided params.
+ * @param {Object} params Params.
+ * @api stable
+ */
+ol.source.ImageArcGISRest.prototype.updateParams = function(params) {
+ ol.obj.assign(this.params_, params);
+ this.image_ = null;
+ this.changed();
+};
+
+goog.provide('ol.source.ImageMapGuide');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.source.Image');
+goog.require('ol.uri');
+
+
+/**
+ * @classdesc
+ * Source for images from Mapguide servers
+ *
+ * @constructor
+ * @fires ol.source.Image.Event
+ * @extends {ol.source.Image}
+ * @param {olx.source.ImageMapGuideOptions} options Options.
+ * @api stable
+ */
+ol.source.ImageMapGuide = function(options) {
+
+ ol.source.Image.call(this, {
+ projection: options.projection,
+ resolutions: options.resolutions
+ });
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ =
+ options.crossOrigin !== undefined ? options.crossOrigin : null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.displayDpi_ = options.displayDpi !== undefined ?
+ options.displayDpi : 96;
+
+ /**
+ * @private
+ * @type {!Object}
+ */
+ this.params_ = options.params || {};
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.url_ = options.url;
+
+ /**
+ * @private
+ * @type {ol.ImageLoadFunctionType}
+ */
+ this.imageLoadFunction_ = options.imageLoadFunction !== undefined ?
+ options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.metersPerUnit_ = options.metersPerUnit !== undefined ?
+ options.metersPerUnit : 1;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.ratio_ = options.ratio !== undefined ? options.ratio : 1;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.useOverlay_ = options.useOverlay !== undefined ?
+ options.useOverlay : false;
+
+ /**
+ * @private
+ * @type {ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = 0;
+
+};
+ol.inherits(ol.source.ImageMapGuide, ol.source.Image);
+
+
+/**
+ * Get the user-provided params, i.e. those passed to the constructor through
+ * the "params" option, and possibly updated using the updateParams method.
+ * @return {Object} Params.
+ * @api stable
+ */
+ol.source.ImageMapGuide.prototype.getParams = function() {
+ return this.params_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageMapGuide.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {
+ resolution = this.findNearestResolution(resolution);
+ pixelRatio = this.hidpi_ ? pixelRatio : 1;
+
+ var image = this.image_;
+ if (image &&
+ this.renderedRevision_ == this.getRevision() &&
+ image.getResolution() == resolution &&
+ image.getPixelRatio() == pixelRatio &&
+ ol.extent.containsExtent(image.getExtent(), extent)) {
+ return image;
+ }
+
+ if (this.ratio_ != 1) {
+ extent = extent.slice();
+ ol.extent.scaleFromCenter(extent, this.ratio_);
+ }
+ var width = ol.extent.getWidth(extent) / resolution;
+ var height = ol.extent.getHeight(extent) / resolution;
+ var size = [width * pixelRatio, height * pixelRatio];
+
+ if (this.url_ !== undefined) {
+ var imageUrl = this.getUrl(this.url_, this.params_, extent, size,
+ projection);
+ image = new ol.Image(extent, resolution, pixelRatio,
+ this.getAttributions(), imageUrl, this.crossOrigin_,
+ this.imageLoadFunction_);
+ ol.events.listen(image, ol.events.EventType.CHANGE,
+ this.handleImageChange, this);
+ } else {
+ image = null;
+ }
+ this.image_ = image;
+ this.renderedRevision_ = this.getRevision();
+
+ return image;
+};
+
+
+/**
+ * Return the image load function of the source.
+ * @return {ol.ImageLoadFunctionType} The image load function.
+ * @api
+ */
+ol.source.ImageMapGuide.prototype.getImageLoadFunction = function() {
+ return this.imageLoadFunction_;
+};
+
+
+/**
+ * @param {ol.Extent} extent The map extents.
+ * @param {ol.Size} size The viewport size.
+ * @param {number} metersPerUnit The meters-per-unit value.
+ * @param {number} dpi The display resolution.
+ * @return {number} The computed map scale.
+ */
+ol.source.ImageMapGuide.getScale = function(extent, size, metersPerUnit, dpi) {
+ var mcsW = ol.extent.getWidth(extent);
+ var mcsH = ol.extent.getHeight(extent);
+ var devW = size[0];
+ var devH = size[1];
+ var mpp = 0.0254 / dpi;
+ if (devH * mcsW > devW * mcsH) {
+ return mcsW * metersPerUnit / (devW * mpp); // width limited
+ } else {
+ return mcsH * metersPerUnit / (devH * mpp); // height limited
+ }
+};
+
+
+/**
+ * Update the user-provided params.
+ * @param {Object} params Params.
+ * @api stable
+ */
+ol.source.ImageMapGuide.prototype.updateParams = function(params) {
+ ol.obj.assign(this.params_, params);
+ this.changed();
+};
+
+
+/**
+ * @param {string} baseUrl The mapagent url.
+ * @param {Object.<string, string|number>} params Request parameters.
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Size} size Size.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string} The mapagent map image request URL.
+ */
+ol.source.ImageMapGuide.prototype.getUrl = function(baseUrl, params, extent, size, projection) {
+ var scale = ol.source.ImageMapGuide.getScale(extent, size,
+ this.metersPerUnit_, this.displayDpi_);
+ var center = ol.extent.getCenter(extent);
+ var baseParams = {
+ 'OPERATION': this.useOverlay_ ? 'GETDYNAMICMAPOVERLAYIMAGE' : 'GETMAPIMAGE',
+ 'VERSION': '2.0.0',
+ 'LOCALE': 'en',
+ 'CLIENTAGENT': 'ol.source.ImageMapGuide source',
+ 'CLIP': '1',
+ 'SETDISPLAYDPI': this.displayDpi_,
+ 'SETDISPLAYWIDTH': Math.round(size[0]),
+ 'SETDISPLAYHEIGHT': Math.round(size[1]),
+ 'SETVIEWSCALE': scale,
+ 'SETVIEWCENTERX': center[0],
+ 'SETVIEWCENTERY': center[1]
+ };
+ ol.obj.assign(baseParams, params);
+ return ol.uri.appendParams(baseUrl, baseParams);
+};
+
+
+/**
+ * Set the image load function of the MapGuide source.
+ * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
+ * @api
+ */
+ol.source.ImageMapGuide.prototype.setImageLoadFunction = function(
+ imageLoadFunction) {
+ this.image_ = null;
+ this.imageLoadFunction_ = imageLoadFunction;
+ this.changed();
+};
+
+goog.provide('ol.source.ImageStatic');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.proj');
+goog.require('ol.source.Image');
+
+
+/**
+ * @classdesc
+ * A layer source for displaying a single, static image.
+ *
+ * @constructor
+ * @extends {ol.source.Image}
+ * @param {olx.source.ImageStaticOptions} options Options.
+ * @api stable
+ */
+ol.source.ImageStatic = function(options) {
+ var imageExtent = options.imageExtent;
+
+ var crossOrigin = options.crossOrigin !== undefined ?
+ options.crossOrigin : null;
+
+ var /** @type {ol.ImageLoadFunctionType} */ imageLoadFunction =
+ options.imageLoadFunction !== undefined ?
+ options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
+
+ ol.source.Image.call(this, {
+ attributions: options.attributions,
+ logo: options.logo,
+ projection: ol.proj.get(options.projection)
+ });
+
+ /**
+ * @private
+ * @type {ol.Image}
+ */
+ this.image_ = new ol.Image(imageExtent, undefined, 1, this.getAttributions(),
+ options.url, crossOrigin, imageLoadFunction);
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.imageSize_ = options.imageSize ? options.imageSize : null;
+
+ ol.events.listen(this.image_, ol.events.EventType.CHANGE,
+ this.handleImageChange, this);
+
+};
+ol.inherits(ol.source.ImageStatic, ol.source.Image);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageStatic.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {
+ if (ol.extent.intersects(extent, this.image_.getExtent())) {
+ return this.image_;
+ }
+ return null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageStatic.prototype.handleImageChange = function(evt) {
+ if (this.image_.getState() == ol.Image.State.LOADED) {
+ var imageExtent = this.image_.getExtent();
+ var image = this.image_.getImage();
+ var imageWidth, imageHeight;
+ if (this.imageSize_) {
+ imageWidth = this.imageSize_[0];
+ imageHeight = this.imageSize_[1];
+ } else {
+ // TODO: remove the type cast when a closure-compiler > 20160315 is used.
+ // see: https://github.com/google/closure-compiler/pull/1664
+ imageWidth = /** @type {number} */ (image.width);
+ imageHeight = /** @type {number} */ (image.height);
+ }
+ var resolution = ol.extent.getHeight(imageExtent) / imageHeight;
+ var targetWidth = Math.ceil(ol.extent.getWidth(imageExtent) / resolution);
+ if (targetWidth != imageWidth) {
+ var context = ol.dom.createCanvasContext2D(targetWidth, imageHeight);
+ var canvas = context.canvas;
+ context.drawImage(image, 0, 0, imageWidth, imageHeight,
+ 0, 0, canvas.width, canvas.height);
+ this.image_.setImage(canvas);
+ }
+ }
+ ol.source.Image.prototype.handleImageChange.call(this, evt);
+};
+
+goog.provide('ol.source.WMSServerType');
+
+
+/**
+ * Available server types: `'carmentaserver'`, `'geoserver'`, `'mapserver'`,
+ * `'qgis'`. These are servers that have vendor parameters beyond the WMS
+ * specification that OpenLayers can make use of.
+ * @enum {string}
+ */
+ol.source.WMSServerType = {
+ CARMENTA_SERVER: 'carmentaserver',
+ GEOSERVER: 'geoserver',
+ MAPSERVER: 'mapserver',
+ QGIS: 'qgis'
+};
+
+// FIXME cannot be shared between maps with different projections
+
+goog.provide('ol.source.ImageWMS');
+
+goog.require('ol');
+goog.require('ol.Image');
+goog.require('ol.asserts');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.source.Image');
+goog.require('ol.source.WMSServerType');
+goog.require('ol.string');
+goog.require('ol.uri');
+
+
+/**
+ * @classdesc
+ * Source for WMS servers providing single, untiled images.
+ *
+ * @constructor
+ * @fires ol.source.Image.Event
+ * @extends {ol.source.Image}
+ * @param {olx.source.ImageWMSOptions=} opt_options Options.
+ * @api stable
+ */
+ol.source.ImageWMS = function(opt_options) {
+
+ var options = opt_options || {};
+
+ ol.source.Image.call(this, {
+ attributions: options.attributions,
+ logo: options.logo,
+ projection: options.projection,
+ resolutions: options.resolutions
+ });
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ =
+ options.crossOrigin !== undefined ? options.crossOrigin : null;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.url_ = options.url;
+
+ /**
+ * @private
+ * @type {ol.ImageLoadFunctionType}
+ */
+ this.imageLoadFunction_ = options.imageLoadFunction !== undefined ?
+ options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
+
+ /**
+ * @private
+ * @type {!Object}
+ */
+ this.params_ = options.params || {};
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.v13_ = true;
+ this.updateV13_();
+
+ /**
+ * @private
+ * @type {ol.source.WMSServerType|undefined}
+ */
+ this.serverType_ =
+ /** @type {ol.source.WMSServerType|undefined} */ (options.serverType);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true;
+
+ /**
+ * @private
+ * @type {ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.imageSize_ = [0, 0];
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.renderedRevision_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5;
+
+};
+ol.inherits(ol.source.ImageWMS, ol.source.Image);
+
+
+/**
+ * @const
+ * @type {ol.Size}
+ * @private
+ */
+ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_ = [101, 101];
+
+
+/**
+ * Return the GetFeatureInfo URL for the passed coordinate, resolution, and
+ * projection. Return `undefined` if the GetFeatureInfo URL cannot be
+ * constructed.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @param {ol.ProjectionLike} projection Projection.
+ * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should
+ * be provided. If `QUERY_LAYERS` is not provided then the layers specified
+ * in the `LAYERS` parameter will be used. `VERSION` should not be
+ * specified here.
+ * @return {string|undefined} GetFeatureInfo URL.
+ * @api stable
+ */
+ol.source.ImageWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) {
+
+ ol.DEBUG && console.assert(!('VERSION' in params),
+ 'key VERSION is not allowed in params');
+
+ if (this.url_ === undefined) {
+ return undefined;
+ }
+
+ var extent = ol.extent.getForViewAndSize(
+ coordinate, resolution, 0,
+ ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_);
+
+ var baseParams = {
+ 'SERVICE': 'WMS',
+ 'VERSION': ol.DEFAULT_WMS_VERSION,
+ 'REQUEST': 'GetFeatureInfo',
+ 'FORMAT': 'image/png',
+ 'TRANSPARENT': true,
+ 'QUERY_LAYERS': this.params_['LAYERS']
+ };
+ ol.obj.assign(baseParams, this.params_, params);
+
+ var x = Math.floor((coordinate[0] - extent[0]) / resolution);
+ var y = Math.floor((extent[3] - coordinate[1]) / resolution);
+ baseParams[this.v13_ ? 'I' : 'X'] = x;
+ baseParams[this.v13_ ? 'J' : 'Y'] = y;
+
+ return this.getRequestUrl_(
+ extent, ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_,
+ 1, ol.proj.get(projection), baseParams);
+};
+
+
+/**
+ * Get the user-provided params, i.e. those passed to the constructor through
+ * the "params" option, and possibly updated using the updateParams method.
+ * @return {Object} Params.
+ * @api stable
+ */
+ol.source.ImageWMS.prototype.getParams = function() {
+ return this.params_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageWMS.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {
+
+ if (this.url_ === undefined) {
+ return null;
+ }
+
+ resolution = this.findNearestResolution(resolution);
+
+ if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) {
+ pixelRatio = 1;
+ }
+
+ extent = extent.slice();
+ var centerX = (extent[0] + extent[2]) / 2;
+ var centerY = (extent[1] + extent[3]) / 2;
+
+ var imageResolution = resolution / pixelRatio;
+ var imageWidth = ol.extent.getWidth(extent) / imageResolution;
+ var imageHeight = ol.extent.getHeight(extent) / imageResolution;
+
+ var image = this.image_;
+ if (image &&
+ this.renderedRevision_ == this.getRevision() &&
+ image.getResolution() == resolution &&
+ image.getPixelRatio() == pixelRatio &&
+ ol.extent.containsExtent(image.getExtent(), extent)) {
+ return image;
+ }
+
+ if (this.ratio_ != 1) {
+ var halfWidth = this.ratio_ * ol.extent.getWidth(extent) / 2;
+ var halfHeight = this.ratio_ * ol.extent.getHeight(extent) / 2;
+ extent[0] = centerX - halfWidth;
+ extent[1] = centerY - halfHeight;
+ extent[2] = centerX + halfWidth;
+ extent[3] = centerY + halfHeight;
+ }
+
+ var params = {
+ 'SERVICE': 'WMS',
+ 'VERSION': ol.DEFAULT_WMS_VERSION,
+ 'REQUEST': 'GetMap',
+ 'FORMAT': 'image/png',
+ 'TRANSPARENT': true
+ };
+ ol.obj.assign(params, this.params_);
+
+ this.imageSize_[0] = Math.ceil(imageWidth * this.ratio_);
+ this.imageSize_[1] = Math.ceil(imageHeight * this.ratio_);
+
+ var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio,
+ projection, params);
+
+ this.image_ = new ol.Image(extent, resolution, pixelRatio,
+ this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_);
+
+ this.renderedRevision_ = this.getRevision();
+
+ ol.events.listen(this.image_, ol.events.EventType.CHANGE,
+ this.handleImageChange, this);
+
+ return this.image_;
+
+};
+
+
+/**
+ * Return the image load function of the source.
+ * @return {ol.ImageLoadFunctionType} The image load function.
+ * @api
+ */
+ol.source.ImageWMS.prototype.getImageLoadFunction = function() {
+ return this.imageLoadFunction_;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Size} size Size.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {Object} params Params.
+ * @return {string} Request URL.
+ * @private
+ */
+ol.source.ImageWMS.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) {
+
+ ol.asserts.assert(this.url_ !== undefined, 9); // `url` must be configured or set using `#setUrl()`
+
+ params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode();
+
+ if (!('STYLES' in this.params_)) {
+ params['STYLES'] = '';
+ }
+
+ if (pixelRatio != 1) {
+ switch (this.serverType_) {
+ case ol.source.WMSServerType.GEOSERVER:
+ var dpi = (90 * pixelRatio + 0.5) | 0;
+ if ('FORMAT_OPTIONS' in params) {
+ params['FORMAT_OPTIONS'] += ';dpi:' + dpi;
+ } else {
+ params['FORMAT_OPTIONS'] = 'dpi:' + dpi;
+ }
+ break;
+ case ol.source.WMSServerType.MAPSERVER:
+ params['MAP_RESOLUTION'] = 90 * pixelRatio;
+ break;
+ case ol.source.WMSServerType.CARMENTA_SERVER:
+ case ol.source.WMSServerType.QGIS:
+ params['DPI'] = 90 * pixelRatio;
+ break;
+ default:
+ ol.asserts.assert(false, 8); // Unknown `serverType` configured
+ break;
+ }
+ }
+
+ params['WIDTH'] = size[0];
+ params['HEIGHT'] = size[1];
+
+ var axisOrientation = projection.getAxisOrientation();
+ var bbox;
+ if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') {
+ bbox = [extent[1], extent[0], extent[3], extent[2]];
+ } else {
+ bbox = extent;
+ }
+ params['BBOX'] = bbox.join(',');
+
+ return ol.uri.appendParams(/** @type {string} */ (this.url_), params);
+};
+
+
+/**
+ * Return the URL used for this WMS source.
+ * @return {string|undefined} URL.
+ * @api stable
+ */
+ol.source.ImageWMS.prototype.getUrl = function() {
+ return this.url_;
+};
+
+
+/**
+ * Set the image load function of the source.
+ * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
+ * @api
+ */
+ol.source.ImageWMS.prototype.setImageLoadFunction = function(
+ imageLoadFunction) {
+ this.image_ = null;
+ this.imageLoadFunction_ = imageLoadFunction;
+ this.changed();
+};
+
+
+/**
+ * Set the URL to use for requests.
+ * @param {string|undefined} url URL.
+ * @api stable
+ */
+ol.source.ImageWMS.prototype.setUrl = function(url) {
+ if (url != this.url_) {
+ this.url_ = url;
+ this.image_ = null;
+ this.changed();
+ }
+};
+
+
+/**
+ * Update the user-provided params.
+ * @param {Object} params Params.
+ * @api stable
+ */
+ol.source.ImageWMS.prototype.updateParams = function(params) {
+ ol.obj.assign(this.params_, params);
+ this.updateV13_();
+ this.image_ = null;
+ this.changed();
+};
+
+
+/**
+ * @private
+ */
+ol.source.ImageWMS.prototype.updateV13_ = function() {
+ var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION;
+ this.v13_ = ol.string.compareVersions(version, '1.3') >= 0;
+};
+
+goog.provide('ol.source.OSM');
+
+goog.require('ol');
+goog.require('ol.Attribution');
+goog.require('ol.source.XYZ');
+
+
+/**
+ * @classdesc
+ * Layer source for the OpenStreetMap tile server.
+ *
+ * @constructor
+ * @extends {ol.source.XYZ}
+ * @param {olx.source.OSMOptions=} opt_options Open Street Map options.
+ * @api stable
+ */
+ol.source.OSM = function(opt_options) {
+
+ var options = opt_options || {};
+
+ var attributions;
+ if (options.attributions !== undefined) {
+ attributions = options.attributions;
+ } else {
+ attributions = [ol.source.OSM.ATTRIBUTION];
+ }
+
+ var crossOrigin = options.crossOrigin !== undefined ?
+ options.crossOrigin : 'anonymous';
+
+ var url = options.url !== undefined ?
+ options.url : 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+
+ ol.source.XYZ.call(this, {
+ attributions: attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: crossOrigin,
+ opaque: options.opaque !== undefined ? options.opaque : true,
+ maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileLoadFunction: options.tileLoadFunction,
+ url: url,
+ wrapX: options.wrapX
+ });
+
+};
+ol.inherits(ol.source.OSM, ol.source.XYZ);
+
+
+/**
+ * The attribution containing a link to the OpenStreetMap Copyright and License
+ * page.
+ * @const
+ * @type {ol.Attribution}
+ * @api
+ */
+ol.source.OSM.ATTRIBUTION = new ol.Attribution({
+ html: '&copy; ' +
+ '<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> ' +
+ 'contributors.'
+});
+
+goog.provide('ol.ext.pixelworks');
+/** @typedef {function(*)} */
+ol.ext.pixelworks;
+(function() {
+var exports = {};
+var module = {exports: exports};
+var define;
+/**
+ * @fileoverview
+ * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility}
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pixelworks = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+var Processor = _dereq_('./processor');
+
+exports.Processor = Processor;
+
+},{"./processor":2}],2:[function(_dereq_,module,exports){
+var newImageData = _dereq_('./util').newImageData;
+
+/**
+ * Create a function for running operations. This function is serialized for
+ * use in a worker.
+ * @param {function(Array, Object):*} operation The operation.
+ * @return {function(Object):ArrayBuffer} A function that takes an object with
+ * buffers, meta, imageOps, width, and height properties and returns an array
+ * buffer.
+ */
+function createMinion(operation) {
+ var workerHasImageData = true;
+ try {
+ new ImageData(10, 10);
+ } catch (_) {
+ workerHasImageData = false;
+ }
+
+ function newWorkerImageData(data, width, height) {
+ if (workerHasImageData) {
+ return new ImageData(data, width, height);
+ } else {
+ return {data: data, width: width, height: height};
+ }
+ }
+
+ return function(data) {
+ // bracket notation for minification support
+ var buffers = data['buffers'];
+ var meta = data['meta'];
+ var imageOps = data['imageOps'];
+ var width = data['width'];
+ var height = data['height'];
+
+ var numBuffers = buffers.length;
+ var numBytes = buffers[0].byteLength;
+ var output, b;
+
+ if (imageOps) {
+ var images = new Array(numBuffers);
+ for (b = 0; b < numBuffers; ++b) {
+ images[b] = newWorkerImageData(
+ new Uint8ClampedArray(buffers[b]), width, height);
+ }
+ output = operation(images, meta).data;
+ } else {
+ output = new Uint8ClampedArray(numBytes);
+ var arrays = new Array(numBuffers);
+ var pixels = new Array(numBuffers);
+ for (b = 0; b < numBuffers; ++b) {
+ arrays[b] = new Uint8ClampedArray(buffers[b]);
+ pixels[b] = [0, 0, 0, 0];
+ }
+ for (var i = 0; i < numBytes; i += 4) {
+ for (var j = 0; j < numBuffers; ++j) {
+ var array = arrays[j];
+ pixels[j][0] = array[i];
+ pixels[j][1] = array[i + 1];
+ pixels[j][2] = array[i + 2];
+ pixels[j][3] = array[i + 3];
+ }
+ var pixel = operation(pixels, meta);
+ output[i] = pixel[0];
+ output[i + 1] = pixel[1];
+ output[i + 2] = pixel[2];
+ output[i + 3] = pixel[3];
+ }
+ }
+ return output.buffer;
+ };
+}
+
+/**
+ * Create a worker for running operations.
+ * @param {Object} config Configuration.
+ * @param {function(MessageEvent)} onMessage Called with a message event.
+ * @return {Worker} The worker.
+ */
+function createWorker(config, onMessage) {
+ var lib = Object.keys(config.lib || {}).map(function(name) {
+ return 'var ' + name + ' = ' + config.lib[name].toString() + ';';
+ });
+
+ var lines = lib.concat([
+ 'var __minion__ = (' + createMinion.toString() + ')(', config.operation.toString(), ');',
+ 'self.addEventListener("message", function(event) {',
+ ' var buffer = __minion__(event.data);',
+ ' self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);',
+ '});'
+ ]);
+
+ var blob = new Blob(lines, {type: 'text/javascript'});
+ var source = URL.createObjectURL(blob);
+ var worker = new Worker(source);
+ worker.addEventListener('message', onMessage);
+ return worker;
+}
+
+/**
+ * Create a faux worker for running operations.
+ * @param {Object} config Configuration.
+ * @param {function(MessageEvent)} onMessage Called with a message event.
+ * @return {Object} The faux worker.
+ */
+function createFauxWorker(config, onMessage) {
+ var minion = createMinion(config.operation);
+ return {
+ postMessage: function(data) {
+ setTimeout(function() {
+ onMessage({'data': {'buffer': minion(data), 'meta': data['meta']}});
+ }, 0);
+ }
+ };
+}
+
+/**
+ * A processor runs pixel or image operations in workers.
+ * @param {Object} config Configuration.
+ */
+function Processor(config) {
+ this._imageOps = !!config.imageOps;
+ var threads;
+ if (config.threads === 0) {
+ threads = 0;
+ } else if (this._imageOps) {
+ threads = 1;
+ } else {
+ threads = config.threads || 1;
+ }
+ var workers = [];
+ if (threads) {
+ for (var i = 0; i < threads; ++i) {
+ workers[i] = createWorker(config, this._onWorkerMessage.bind(this, i));
+ }
+ } else {
+ workers[0] = createFauxWorker(config, this._onWorkerMessage.bind(this, 0));
+ }
+ this._workers = workers;
+ this._queue = [];
+ this._maxQueueLength = config.queue || Infinity;
+ this._running = 0;
+ this._dataLookup = {};
+ this._job = null;
+}
+
+/**
+ * Run operation on input data.
+ * @param {Array.<Array|ImageData>} inputs Array of pixels or image data
+ * (depending on the operation type).
+ * @param {Object} meta A user data object. This is passed to all operations
+ * and must be serializable.
+ * @param {function(Error, ImageData, Object)} callback Called when work
+ * completes. The first argument is any error. The second is the ImageData
+ * generated by operations. The third is the user data object.
+ */
+Processor.prototype.process = function(inputs, meta, callback) {
+ this._enqueue({
+ inputs: inputs,
+ meta: meta,
+ callback: callback
+ });
+ this._dispatch();
+};
+
+/**
+ * Stop responding to any completed work and destroy the processor.
+ */
+Processor.prototype.destroy = function() {
+ for (var key in this) {
+ this[key] = null;
+ }
+ this._destroyed = true;
+};
+
+/**
+ * Add a job to the queue.
+ * @param {Object} job The job.
+ */
+Processor.prototype._enqueue = function(job) {
+ this._queue.push(job);
+ while (this._queue.length > this._maxQueueLength) {
+ this._queue.shift().callback(null, null);
+ }
+};
+
+/**
+ * Dispatch a job.
+ */
+Processor.prototype._dispatch = function() {
+ if (this._running === 0 && this._queue.length > 0) {
+ var job = this._job = this._queue.shift();
+ var width = job.inputs[0].width;
+ var height = job.inputs[0].height;
+ var buffers = job.inputs.map(function(input) {
+ return input.data.buffer;
+ });
+ var threads = this._workers.length;
+ this._running = threads;
+ if (threads === 1) {
+ this._workers[0].postMessage({
+ 'buffers': buffers,
+ 'meta': job.meta,
+ 'imageOps': this._imageOps,
+ 'width': width,
+ 'height': height
+ }, buffers);
+ } else {
+ var length = job.inputs[0].data.length;
+ var segmentLength = 4 * Math.ceil(length / 4 / threads);
+ for (var i = 0; i < threads; ++i) {
+ var offset = i * segmentLength;
+ var slices = [];
+ for (var j = 0, jj = buffers.length; j < jj; ++j) {
+ slices.push(buffers[i].slice(offset, offset + segmentLength));
+ }
+ this._workers[i].postMessage({
+ 'buffers': slices,
+ 'meta': job.meta,
+ 'imageOps': this._imageOps,
+ 'width': width,
+ 'height': height
+ }, slices);
+ }
+ }
+ }
+};
+
+/**
+ * Handle messages from the worker.
+ * @param {number} index The worker index.
+ * @param {MessageEvent} event The message event.
+ */
+Processor.prototype._onWorkerMessage = function(index, event) {
+ if (this._destroyed) {
+ return;
+ }
+ this._dataLookup[index] = event.data;
+ --this._running;
+ if (this._running === 0) {
+ this._resolveJob();
+ }
+};
+
+/**
+ * Resolve a job. If there are no more worker threads, the processor callback
+ * will be called.
+ */
+Processor.prototype._resolveJob = function() {
+ var job = this._job;
+ var threads = this._workers.length;
+ var data, meta;
+ if (threads === 1) {
+ data = new Uint8ClampedArray(this._dataLookup[0]['buffer']);
+ meta = this._dataLookup[0]['meta'];
+ } else {
+ var length = job.inputs[0].data.length;
+ data = new Uint8ClampedArray(length);
+ meta = new Array(length);
+ var segmentLength = 4 * Math.ceil(length / 4 / threads);
+ for (var i = 0; i < threads; ++i) {
+ var buffer = this._dataLookup[i]['buffer'];
+ var offset = i * segmentLength;
+ data.set(new Uint8ClampedArray(buffer), offset);
+ meta[i] = this._dataLookup[i]['meta'];
+ }
+ }
+ this._job = null;
+ this._dataLookup = {};
+ job.callback(null,
+ newImageData(data, job.inputs[0].width, job.inputs[0].height), meta);
+ this._dispatch();
+};
+
+module.exports = Processor;
+
+},{"./util":3}],3:[function(_dereq_,module,exports){
+var hasImageData = true;
+try {
+ new ImageData(10, 10);
+} catch (_) {
+ hasImageData = false;
+}
+
+var context = document.createElement('canvas').getContext('2d');
+
+function newImageData(data, width, height) {
+ if (hasImageData) {
+ return new ImageData(data, width, height);
+ } else {
+ var imageData = context.createImageData(width, height);
+ imageData.data.set(data);
+ return imageData;
+ }
+}
+
+exports.newImageData = newImageData;
+
+},{}]},{},[1])(1)
+});
+ol.ext.pixelworks = module.exports;
+})();
+
+goog.provide('ol.source.Raster');
+
+goog.require('ol');
+goog.require('ol.transform');
+goog.require('ol.ImageCanvas');
+goog.require('ol.TileQueue');
+goog.require('ol.dom');
+goog.require('ol.events');
+goog.require('ol.events.Event');
+goog.require('ol.events.EventType');
+goog.require('ol.ext.pixelworks');
+goog.require('ol.extent');
+goog.require('ol.layer.Image');
+goog.require('ol.layer.Tile');
+goog.require('ol.obj');
+goog.require('ol.renderer.canvas.ImageLayer');
+goog.require('ol.renderer.canvas.TileLayer');
+goog.require('ol.source.Image');
+goog.require('ol.source.State');
+goog.require('ol.source.Tile');
+
+
+/**
+ * @classdesc
+ * A source that transforms data from any number of input sources using an array
+ * of {@link ol.RasterOperation} functions to transform input pixel values into
+ * output pixel values.
+ *
+ * @constructor
+ * @extends {ol.source.Image}
+ * @fires ol.source.Raster.Event
+ * @param {olx.source.RasterOptions} options Options.
+ * @api
+ */
+ol.source.Raster = function(options) {
+
+ /**
+ * @private
+ * @type {*}
+ */
+ this.worker_ = null;
+
+ /**
+ * @private
+ * @type {ol.source.Raster.OperationType}
+ */
+ this.operationType_ = options.operationType !== undefined ?
+ options.operationType : ol.source.Raster.OperationType.PIXEL;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.threads_ = options.threads !== undefined ? options.threads : 1;
+
+ /**
+ * @private
+ * @type {Array.<ol.renderer.canvas.Layer>}
+ */
+ this.renderers_ = ol.source.Raster.createRenderers_(options.sources);
+
+ for (var r = 0, rr = this.renderers_.length; r < rr; ++r) {
+ ol.events.listen(this.renderers_[r], ol.events.EventType.CHANGE,
+ this.changed, this);
+ }
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.canvasContext_ = ol.dom.createCanvasContext2D();
+
+ /**
+ * @private
+ * @type {ol.TileQueue}
+ */
+ this.tileQueue_ = new ol.TileQueue(
+ function() {
+ return 1;
+ },
+ this.changed.bind(this));
+
+ var layerStatesArray = ol.source.Raster.getLayerStatesArray_(this.renderers_);
+ var layerStates = {};
+ for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) {
+ layerStates[ol.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
+ }
+
+ /**
+ * The most recently rendered state.
+ * @type {?ol.SourceRasterRenderedState}
+ * @private
+ */
+ this.renderedState_ = null;
+
+ /**
+ * The most recently rendered image canvas.
+ * @type {ol.ImageCanvas}
+ * @private
+ */
+ this.renderedImageCanvas_ = null;
+
+ /**
+ * @private
+ * @type {olx.FrameState}
+ */
+ this.frameState_ = {
+ animate: false,
+ attributions: {},
+ coordinateToPixelTransform: ol.transform.create(),
+ extent: null,
+ focus: null,
+ index: 0,
+ layerStates: layerStates,
+ layerStatesArray: layerStatesArray,
+ logos: {},
+ pixelRatio: 1,
+ pixelToCoordinateTransform: ol.transform.create(),
+ postRenderFunctions: [],
+ size: [0, 0],
+ skippedFeatureUids: {},
+ tileQueue: this.tileQueue_,
+ time: Date.now(),
+ usedTiles: {},
+ viewState: /** @type {olx.ViewState} */ ({
+ rotation: 0
+ }),
+ viewHints: [],
+ wantedTiles: {}
+ };
+
+ ol.source.Image.call(this, {});
+
+ if (options.operation !== undefined) {
+ this.setOperation(options.operation, options.lib);
+ }
+
+};
+ol.inherits(ol.source.Raster, ol.source.Image);
+
+
+/**
+ * Set the operation.
+ * @param {ol.RasterOperation} operation New operation.
+ * @param {Object=} opt_lib Functions that will be available to operations run
+ * in a worker.
+ * @api
+ */
+ol.source.Raster.prototype.setOperation = function(operation, opt_lib) {
+ this.worker_ = new ol.ext.pixelworks.Processor({
+ operation: operation,
+ imageOps: this.operationType_ === ol.source.Raster.OperationType.IMAGE,
+ queue: 1,
+ lib: opt_lib,
+ threads: this.threads_
+ });
+ this.changed();
+};
+
+
+/**
+ * Update the stored frame state.
+ * @param {ol.Extent} extent The view extent (in map units).
+ * @param {number} resolution The view resolution.
+ * @param {ol.proj.Projection} projection The view projection.
+ * @return {olx.FrameState} The updated frame state.
+ * @private
+ */
+ol.source.Raster.prototype.updateFrameState_ = function(extent, resolution, projection) {
+
+ var frameState = /** @type {olx.FrameState} */ (
+ ol.obj.assign({}, this.frameState_));
+
+ frameState.viewState = /** @type {olx.ViewState} */ (
+ ol.obj.assign({}, frameState.viewState));
+
+ var center = ol.extent.getCenter(extent);
+ var width = Math.round(ol.extent.getWidth(extent) / resolution);
+ var height = Math.round(ol.extent.getHeight(extent) / resolution);
+
+ frameState.extent = extent;
+ frameState.focus = ol.extent.getCenter(extent);
+ frameState.size[0] = width;
+ frameState.size[1] = height;
+
+ var viewState = frameState.viewState;
+ viewState.center = center;
+ viewState.projection = projection;
+ viewState.resolution = resolution;
+ return frameState;
+};
+
+
+/**
+ * Determine if the most recently rendered image canvas is dirty.
+ * @param {ol.Extent} extent The requested extent.
+ * @param {number} resolution The requested resolution.
+ * @return {boolean} The image is dirty.
+ * @private
+ */
+ol.source.Raster.prototype.isDirty_ = function(extent, resolution) {
+ var state = this.renderedState_;
+ return !state ||
+ this.getRevision() !== state.revision ||
+ resolution !== state.resolution ||
+ !ol.extent.equals(extent, state.extent);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, projection) {
+
+ if (!this.allSourcesReady_()) {
+ return null;
+ }
+
+ var currentExtent = extent.slice();
+ if (!this.isDirty_(currentExtent, resolution)) {
+ return this.renderedImageCanvas_;
+ }
+
+ var context = this.canvasContext_;
+ var canvas = context.canvas;
+
+ var width = Math.round(ol.extent.getWidth(currentExtent) / resolution);
+ var height = Math.round(ol.extent.getHeight(currentExtent) / resolution);
+
+ if (width !== canvas.width ||
+ height !== canvas.height) {
+ canvas.width = width;
+ canvas.height = height;
+ }
+
+ var frameState = this.updateFrameState_(currentExtent, resolution, projection);
+
+ var imageCanvas = new ol.ImageCanvas(
+ currentExtent, resolution, 1, this.getAttributions(), canvas,
+ this.composeFrame_.bind(this, frameState));
+
+ this.renderedImageCanvas_ = imageCanvas;
+
+ this.renderedState_ = {
+ extent: currentExtent,
+ resolution: resolution,
+ revision: this.getRevision()
+ };
+
+ return imageCanvas;
+};
+
+
+/**
+ * Determine if all sources are ready.
+ * @return {boolean} All sources are ready.
+ * @private
+ */
+ol.source.Raster.prototype.allSourcesReady_ = function() {
+ var ready = true;
+ var source;
+ for (var i = 0, ii = this.renderers_.length; i < ii; ++i) {
+ source = this.renderers_[i].getLayer().getSource();
+ if (source.getState() !== ol.source.State.READY) {
+ ready = false;
+ break;
+ }
+ }
+ return ready;
+};
+
+
+/**
+ * Compose the frame. This renders data from all sources, runs pixel-wise
+ * operations, and renders the result to the stored canvas context.
+ * @param {olx.FrameState} frameState The frame state.
+ * @param {function(Error)} callback Called when composition is complete.
+ * @private
+ */
+ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) {
+ var len = this.renderers_.length;
+ var imageDatas = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ var imageData = ol.source.Raster.getImageData_(
+ this.renderers_[i], frameState, frameState.layerStatesArray[i]);
+ if (imageData) {
+ imageDatas[i] = imageData;
+ } else {
+ // image not yet ready
+ imageDatas = null;
+ break;
+ }
+ }
+
+ if (imageDatas) {
+ var data = {};
+ this.dispatchEvent(new ol.source.Raster.Event(
+ ol.source.Raster.EventType.BEFOREOPERATIONS, frameState, data));
+
+ this.worker_.process(imageDatas, data,
+ this.onWorkerComplete_.bind(this, frameState, callback));
+ }
+
+ frameState.tileQueue.loadMoreTiles(16, 16);
+};
+
+
+/**
+ * Called when pixel processing is complete.
+ * @param {olx.FrameState} frameState The frame state.
+ * @param {function(Error)} callback Called when rendering is complete.
+ * @param {Error} err Any error during processing.
+ * @param {ImageData} output The output image data.
+ * @param {Object} data The user data.
+ * @private
+ */
+ol.source.Raster.prototype.onWorkerComplete_ = function(frameState, callback, err, output, data) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ if (!output) {
+ // job aborted
+ return;
+ }
+
+ this.dispatchEvent(new ol.source.Raster.Event(
+ ol.source.Raster.EventType.AFTEROPERATIONS, frameState, data));
+
+ var resolution = frameState.viewState.resolution / frameState.pixelRatio;
+ if (!this.isDirty_(frameState.extent, resolution)) {
+ this.canvasContext_.putImageData(output, 0, 0);
+ }
+
+ callback(null);
+};
+
+
+/**
+ * Get image data from a renderer.
+ * @param {ol.renderer.canvas.Layer} renderer Layer renderer.
+ * @param {olx.FrameState} frameState The frame state.
+ * @param {ol.LayerState} layerState The layer state.
+ * @return {ImageData} The image data.
+ * @private
+ */
+ol.source.Raster.getImageData_ = function(renderer, frameState, layerState) {
+ if (!renderer.prepareFrame(frameState, layerState)) {
+ return null;
+ }
+ var width = frameState.size[0];
+ var height = frameState.size[1];
+ if (!ol.source.Raster.context_) {
+ ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height);
+ } else {
+ var canvas = ol.source.Raster.context_.canvas;
+ if (canvas.width !== width || canvas.height !== height) {
+ ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height);
+ } else {
+ ol.source.Raster.context_.clearRect(0, 0, width, height);
+ }
+ }
+ renderer.composeFrame(frameState, layerState, ol.source.Raster.context_);
+ return ol.source.Raster.context_.getImageData(0, 0, width, height);
+};
+
+
+/**
+ * A reusable canvas context.
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+ol.source.Raster.context_ = null;
+
+
+/**
+ * Get a list of layer states from a list of renderers.
+ * @param {Array.<ol.renderer.canvas.Layer>} renderers Layer renderers.
+ * @return {Array.<ol.LayerState>} The layer states.
+ * @private
+ */
+ol.source.Raster.getLayerStatesArray_ = function(renderers) {
+ return renderers.map(function(renderer) {
+ return renderer.getLayer().getLayerState();
+ });
+};
+
+
+/**
+ * Create renderers for all sources.
+ * @param {Array.<ol.source.Source>} sources The sources.
+ * @return {Array.<ol.renderer.canvas.Layer>} Array of layer renderers.
+ * @private
+ */
+ol.source.Raster.createRenderers_ = function(sources) {
+ var len = sources.length;
+ var renderers = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ renderers[i] = ol.source.Raster.createRenderer_(sources[i]);
+ }
+ return renderers;
+};
+
+
+/**
+ * Create a renderer for the provided source.
+ * @param {ol.source.Source} source The source.
+ * @return {ol.renderer.canvas.Layer} The renderer.
+ * @private
+ */
+ol.source.Raster.createRenderer_ = function(source) {
+ var renderer = null;
+ if (source instanceof ol.source.Tile) {
+ renderer = ol.source.Raster.createTileRenderer_(source);
+ } else if (source instanceof ol.source.Image) {
+ renderer = ol.source.Raster.createImageRenderer_(source);
+ } else {
+ ol.DEBUG && console.assert(false, 'Unsupported source type: ' + source);
+ }
+ return renderer;
+};
+
+
+/**
+ * Create an image renderer for the provided source.
+ * @param {ol.source.Image} source The source.
+ * @return {ol.renderer.canvas.Layer} The renderer.
+ * @private
+ */
+ol.source.Raster.createImageRenderer_ = function(source) {
+ var layer = new ol.layer.Image({source: source});
+ return new ol.renderer.canvas.ImageLayer(layer);
+};
+
+
+/**
+ * Create a tile renderer for the provided source.
+ * @param {ol.source.Tile} source The source.
+ * @return {ol.renderer.canvas.Layer} The renderer.
+ * @private
+ */
+ol.source.Raster.createTileRenderer_ = function(source) {
+ var layer = new ol.layer.Tile({source: source});
+ return new ol.renderer.canvas.TileLayer(layer);
+};
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.source.Raster} instances are instances of this
+ * type.
+ *
+ * @constructor
+ * @extends {ol.events.Event}
+ * @implements {oli.source.RasterEvent}
+ * @param {string} type Type.
+ * @param {olx.FrameState} frameState The frame state.
+ * @param {Object} data An object made available to operations.
+ */
+ol.source.Raster.Event = function(type, frameState, data) {
+ ol.events.Event.call(this, type);
+
+ /**
+ * The raster extent.
+ * @type {ol.Extent}
+ * @api
+ */
+ this.extent = frameState.extent;
+
+ /**
+ * The pixel resolution (map units per pixel).
+ * @type {number}
+ * @api
+ */
+ this.resolution = frameState.viewState.resolution / frameState.pixelRatio;
+
+ /**
+ * An object made available to all operations. This can be used by operations
+ * as a storage object (e.g. for calculating statistics).
+ * @type {Object}
+ * @api
+ */
+ this.data = data;
+
+};
+ol.inherits(ol.source.Raster.Event, ol.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.source.Raster.EventType = {
+ /**
+ * Triggered before operations are run.
+ * @event ol.source.Raster.Event#beforeoperations
+ * @api
+ */
+ BEFOREOPERATIONS: 'beforeoperations',
+
+ /**
+ * Triggered after operations are run.
+ * @event ol.source.Raster.Event#afteroperations
+ * @api
+ */
+ AFTEROPERATIONS: 'afteroperations'
+};
+
+
+/**
+ * Raster operation type. Supported values are `'pixel'` and `'image'`.
+ * @enum {string}
+ */
+ol.source.Raster.OperationType = {
+ PIXEL: 'pixel',
+ IMAGE: 'image'
+};
+
+goog.provide('ol.source.Stamen');
+
+goog.require('ol');
+goog.require('ol.Attribution');
+goog.require('ol.source.OSM');
+goog.require('ol.source.XYZ');
+
+
+/**
+ * @classdesc
+ * Layer source for the Stamen tile server.
+ *
+ * @constructor
+ * @extends {ol.source.XYZ}
+ * @param {olx.source.StamenOptions} options Stamen options.
+ * @api stable
+ */
+ol.source.Stamen = function(options) {
+
+ var i = options.layer.indexOf('-');
+ var provider = i == -1 ? options.layer : options.layer.slice(0, i);
+ ol.DEBUG && console.assert(provider in ol.source.Stamen.ProviderConfig,
+ 'known provider configured');
+ var providerConfig = ol.source.Stamen.ProviderConfig[provider];
+
+ ol.DEBUG && console.assert(options.layer in ol.source.Stamen.LayerConfig,
+ 'known layer configured');
+ var layerConfig = ol.source.Stamen.LayerConfig[options.layer];
+
+ var url = options.url !== undefined ? options.url :
+ 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' + options.layer +
+ '/{z}/{x}/{y}.' + layerConfig.extension;
+
+ ol.source.XYZ.call(this, {
+ attributions: ol.source.Stamen.ATTRIBUTIONS,
+ cacheSize: options.cacheSize,
+ crossOrigin: 'anonymous',
+ maxZoom: options.maxZoom != undefined ? options.maxZoom : providerConfig.maxZoom,
+ minZoom: options.minZoom != undefined ? options.minZoom : providerConfig.minZoom,
+ opaque: layerConfig.opaque,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileLoadFunction: options.tileLoadFunction,
+ url: url
+ });
+
+};
+ol.inherits(ol.source.Stamen, ol.source.XYZ);
+
+
+/**
+ * @const
+ * @type {Array.<ol.Attribution>}
+ */
+ol.source.Stamen.ATTRIBUTIONS = [
+ new ol.Attribution({
+ html: 'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, ' +
+ 'under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY' +
+ ' 3.0</a>.'
+ }),
+ ol.source.OSM.ATTRIBUTION
+];
+
+/**
+ * @type {Object.<string, {extension: string, opaque: boolean}>}
+ */
+ol.source.Stamen.LayerConfig = {
+ 'terrain': {
+ extension: 'jpg',
+ opaque: true
+ },
+ 'terrain-background': {
+ extension: 'jpg',
+ opaque: true
+ },
+ 'terrain-labels': {
+ extension: 'png',
+ opaque: false
+ },
+ 'terrain-lines': {
+ extension: 'png',
+ opaque: false
+ },
+ 'toner-background': {
+ extension: 'png',
+ opaque: true
+ },
+ 'toner': {
+ extension: 'png',
+ opaque: true
+ },
+ 'toner-hybrid': {
+ extension: 'png',
+ opaque: false
+ },
+ 'toner-labels': {
+ extension: 'png',
+ opaque: false
+ },
+ 'toner-lines': {
+ extension: 'png',
+ opaque: false
+ },
+ 'toner-lite': {
+ extension: 'png',
+ opaque: true
+ },
+ 'watercolor': {
+ extension: 'jpg',
+ opaque: true
+ }
+};
+
+/**
+ * @type {Object.<string, {minZoom: number, maxZoom: number}>}
+ */
+ol.source.Stamen.ProviderConfig = {
+ 'terrain': {
+ minZoom: 4,
+ maxZoom: 18
+ },
+ 'toner': {
+ minZoom: 0,
+ maxZoom: 20
+ },
+ 'watercolor': {
+ minZoom: 1,
+ maxZoom: 16
+ }
+};
+
+goog.provide('ol.source.TileArcGISRest');
+
+goog.require('ol');
+goog.require('ol.extent');
+goog.require('ol.math');
+goog.require('ol.obj');
+goog.require('ol.size');
+goog.require('ol.source.TileImage');
+goog.require('ol.tilecoord');
+goog.require('ol.uri');
+
+
+/**
+ * @classdesc
+ * Layer source for tile data from ArcGIS Rest services. Map and Image
+ * Services are supported.
+ *
+ * For cached ArcGIS services, better performance is available using the
+ * {@link ol.source.XYZ} data source.
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.TileArcGISRestOptions=} opt_options Tile ArcGIS Rest
+ * options.
+ * @api
+ */
+ol.source.TileArcGISRest = function(opt_options) {
+
+ var options = opt_options || {};
+
+ ol.source.TileImage.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ logo: options.logo,
+ projection: options.projection,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileGrid: options.tileGrid,
+ tileLoadFunction: options.tileLoadFunction,
+ url: options.url,
+ urls: options.urls,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+ /**
+ * @private
+ * @type {!Object}
+ */
+ this.params_ = options.params || {};
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.tmpExtent_ = ol.extent.createEmpty();
+
+ this.setKey(this.getKeyForParams_());
+};
+ol.inherits(ol.source.TileArcGISRest, ol.source.TileImage);
+
+
+/**
+ * @private
+ * @return {string} The key for the current params.
+ */
+ol.source.TileArcGISRest.prototype.getKeyForParams_ = function() {
+ var i = 0;
+ var res = [];
+ for (var key in this.params_) {
+ res[i++] = key + '-' + this.params_[key];
+ }
+ return res.join('/');
+};
+
+
+/**
+ * Get the user-provided params, i.e. those passed to the constructor through
+ * the "params" option, and possibly updated using the updateParams method.
+ * @return {Object} Params.
+ * @api
+ */
+ol.source.TileArcGISRest.prototype.getParams = function() {
+ return this.params_;
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Size} tileSize Tile size.
+ * @param {ol.Extent} tileExtent Tile extent.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {Object} params Params.
+ * @return {string|undefined} Request URL.
+ * @private
+ */
+ol.source.TileArcGISRest.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent,
+ pixelRatio, projection, params) {
+
+ var urls = this.urls;
+ if (!urls) {
+ return undefined;
+ }
+
+ // ArcGIS Server only wants the numeric portion of the projection ID.
+ var srid = projection.getCode().split(':').pop();
+
+ params['SIZE'] = tileSize[0] + ',' + tileSize[1];
+ params['BBOX'] = tileExtent.join(',');
+ params['BBOXSR'] = srid;
+ params['IMAGESR'] = srid;
+ params['DPI'] = Math.round(
+ params['DPI'] ? params['DPI'] * pixelRatio : 90 * pixelRatio
+ );
+
+ var url;
+ if (urls.length == 1) {
+ url = urls[0];
+ } else {
+ var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length);
+ url = urls[index];
+ }
+
+ var modifiedUrl = url
+ .replace(/MapServer\/?$/, 'MapServer/export')
+ .replace(/ImageServer\/?$/, 'ImageServer/exportImage');
+ return ol.uri.appendParams(modifiedUrl, params);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileArcGISRest.prototype.getTilePixelRatio = function(pixelRatio) {
+ return /** @type {number} */ (pixelRatio);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileArcGISRest.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) {
+
+ var tileGrid = this.getTileGrid();
+ if (!tileGrid) {
+ tileGrid = this.getTileGridForProjection(projection);
+ }
+
+ if (tileGrid.getResolutions().length <= tileCoord[0]) {
+ return undefined;
+ }
+
+ var tileExtent = tileGrid.getTileCoordExtent(
+ tileCoord, this.tmpExtent_);
+ var tileSize = ol.size.toSize(
+ tileGrid.getTileSize(tileCoord[0]), this.tmpSize);
+
+ if (pixelRatio != 1) {
+ tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize);
+ }
+
+ // Apply default params and override with user specified values.
+ var baseParams = {
+ 'F': 'image',
+ 'FORMAT': 'PNG32',
+ 'TRANSPARENT': true
+ };
+ ol.obj.assign(baseParams, this.params_);
+
+ return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
+ pixelRatio, projection, baseParams);
+};
+
+
+/**
+ * Update the user-provided params.
+ * @param {Object} params Params.
+ * @api stable
+ */
+ol.source.TileArcGISRest.prototype.updateParams = function(params) {
+ ol.obj.assign(this.params_, params);
+ this.setKey(this.getKeyForParams_());
+};
+
+goog.provide('ol.source.TileDebug');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.dom');
+goog.require('ol.size');
+goog.require('ol.source.Tile');
+
+
+/**
+ * @classdesc
+ * A pseudo tile source, which does not fetch tiles from a server, but renders
+ * a grid outline for the tile grid/projection along with the coordinates for
+ * each tile. See examples/canvas-tiles for an example.
+ *
+ * Uses Canvas context2d, so requires Canvas support.
+ *
+ * @constructor
+ * @extends {ol.source.Tile}
+ * @param {olx.source.TileDebugOptions} options Debug tile options.
+ * @api
+ */
+ol.source.TileDebug = function(options) {
+
+ ol.source.Tile.call(this, {
+ opaque: false,
+ projection: options.projection,
+ tileGrid: options.tileGrid,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+};
+ol.inherits(ol.source.TileDebug, ol.source.Tile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileDebug.prototype.getTile = function(z, x, y) {
+ var tileCoordKey = this.getKeyZXY(z, x, y);
+ if (this.tileCache.containsKey(tileCoordKey)) {
+ return /** @type {!ol.source.TileDebug.Tile_} */ (this.tileCache.get(tileCoordKey));
+ } else {
+ var tileSize = ol.size.toSize(this.tileGrid.getTileSize(z));
+ var tileCoord = [z, x, y];
+ var textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord);
+ var text = !textTileCoord ? '' :
+ this.getTileCoordForTileUrlFunction(textTileCoord).toString();
+ var tile = new ol.source.TileDebug.Tile_(tileCoord, tileSize, text);
+ this.tileCache.set(tileCoordKey, tile);
+ return tile;
+ }
+};
+
+
+/**
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Size} tileSize Tile size.
+ * @param {string} text Text.
+ * @private
+ */
+ol.source.TileDebug.Tile_ = function(tileCoord, tileSize, text) {
+
+ ol.Tile.call(this, tileCoord, ol.Tile.State.LOADED);
+
+ /**
+ * @private
+ * @type {ol.Size}
+ */
+ this.tileSize_ = tileSize;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.text_ = text;
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = null;
+
+};
+ol.inherits(ol.source.TileDebug.Tile_, ol.Tile);
+
+
+/**
+ * Get the image element for this tile.
+ * @return {HTMLCanvasElement} Image.
+ */
+ol.source.TileDebug.Tile_.prototype.getImage = function() {
+ if (this.canvas_) {
+ return this.canvas_;
+ } else {
+ var tileSize = this.tileSize_;
+ var context = ol.dom.createCanvasContext2D(tileSize[0], tileSize[1]);
+
+ context.strokeStyle = 'black';
+ context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5);
+
+ context.fillStyle = 'black';
+ context.textAlign = 'center';
+ context.textBaseline = 'middle';
+ context.font = '24px sans-serif';
+ context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2);
+
+ this.canvas_ = context.canvas;
+ return context.canvas;
+ }
+};
+
+// FIXME check order of async callbacks
+
+/**
+ * @see http://mapbox.com/developers/api/
+ */
+
+goog.provide('ol.source.TileJSON');
+
+goog.require('ol');
+goog.require('ol.Attribution');
+goog.require('ol.TileUrlFunction');
+goog.require('ol.extent');
+goog.require('ol.net');
+goog.require('ol.proj');
+goog.require('ol.source.State');
+goog.require('ol.source.TileImage');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * Layer source for tile data in TileJSON format.
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.TileJSONOptions} options TileJSON options.
+ * @api stable
+ */
+ol.source.TileJSON = function(options) {
+
+ /**
+ * @type {TileJSON}
+ * @private
+ */
+ this.tileJSON_ = null;
+
+ ol.source.TileImage.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ projection: ol.proj.get('EPSG:3857'),
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ state: ol.source.State.LOADING,
+ tileLoadFunction: options.tileLoadFunction,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+ if (options.jsonp) {
+ ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this),
+ this.handleTileJSONError.bind(this));
+ } else {
+ var client = new XMLHttpRequest();
+ client.addEventListener('load', this.onXHRLoad_.bind(this));
+ client.addEventListener('error', this.onXHRError_.bind(this));
+ client.open('GET', options.url);
+ client.send();
+ }
+
+};
+ol.inherits(ol.source.TileJSON, ol.source.TileImage);
+
+
+/**
+ * @private
+ * @param {Event} event The load event.
+ */
+ol.source.TileJSON.prototype.onXHRLoad_ = function(event) {
+ var client = /** @type {XMLHttpRequest} */ (event.target);
+ // status will be 0 for file:// urls
+ if (!client.status || client.status >= 200 && client.status < 300) {
+ var response;
+ try {
+ response = /** @type {TileJSON} */(JSON.parse(client.responseText));
+ } catch (err) {
+ this.handleTileJSONError();
+ return;
+ }
+ this.handleTileJSONResponse(response);
+ } else {
+ this.handleTileJSONError();
+ }
+};
+
+
+/**
+ * @private
+ * @param {Event} event The error event.
+ */
+ol.source.TileJSON.prototype.onXHRError_ = function(event) {
+ this.handleTileJSONError();
+};
+
+
+/**
+ * @return {TileJSON} The tilejson object.
+ * @api
+ */
+ol.source.TileJSON.prototype.getTileJSON = function() {
+ return this.tileJSON_;
+};
+
+
+/**
+ * @protected
+ * @param {TileJSON} tileJSON Tile JSON.
+ */
+ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) {
+
+ var epsg4326Projection = ol.proj.get('EPSG:4326');
+
+ var sourceProjection = this.getProjection();
+ var extent;
+ if (tileJSON.bounds !== undefined) {
+ var transform = ol.proj.getTransformFromProjections(
+ epsg4326Projection, sourceProjection);
+ extent = ol.extent.applyTransform(tileJSON.bounds, transform);
+ }
+
+ if (tileJSON.scheme !== undefined) {
+ ol.DEBUG && console.assert(tileJSON.scheme == 'xyz', 'tileJSON-scheme is "xyz"');
+ }
+ var minZoom = tileJSON.minzoom || 0;
+ var maxZoom = tileJSON.maxzoom || 22;
+ var tileGrid = ol.tilegrid.createXYZ({
+ extent: ol.tilegrid.extentFromProjection(sourceProjection),
+ maxZoom: maxZoom,
+ minZoom: minZoom
+ });
+ this.tileGrid = tileGrid;
+
+ this.tileUrlFunction =
+ ol.TileUrlFunction.createFromTemplates(tileJSON.tiles, tileGrid);
+
+ if (tileJSON.attribution !== undefined && !this.getAttributions()) {
+ var attributionExtent = extent !== undefined ?
+ extent : epsg4326Projection.getExtent();
+ /** @type {Object.<string, Array.<ol.TileRange>>} */
+ var tileRanges = {};
+ var z, zKey;
+ for (z = minZoom; z <= maxZoom; ++z) {
+ zKey = z.toString();
+ tileRanges[zKey] =
+ [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)];
+ }
+ this.setAttributions([
+ new ol.Attribution({
+ html: tileJSON.attribution,
+ tileRanges: tileRanges
+ })
+ ]);
+ }
+ this.tileJSON_ = tileJSON;
+ this.setState(ol.source.State.READY);
+
+};
+
+
+/**
+ * @protected
+ */
+ol.source.TileJSON.prototype.handleTileJSONError = function() {
+ this.setState(ol.source.State.ERROR);
+};
+
+goog.provide('ol.source.TileUTFGrid');
+
+goog.require('ol');
+goog.require('ol.Attribution');
+goog.require('ol.Tile');
+goog.require('ol.TileUrlFunction');
+goog.require('ol.asserts');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.extent');
+goog.require('ol.net');
+goog.require('ol.proj');
+goog.require('ol.source.State');
+goog.require('ol.source.Tile');
+goog.require('ol.tilegrid');
+
+
+/**
+ * @classdesc
+ * Layer source for UTFGrid interaction data loaded from TileJSON format.
+ *
+ * @constructor
+ * @extends {ol.source.Tile}
+ * @param {olx.source.TileUTFGridOptions} options Source options.
+ * @api
+ */
+ol.source.TileUTFGrid = function(options) {
+ ol.source.Tile.call(this, {
+ projection: ol.proj.get('EPSG:3857'),
+ state: ol.source.State.LOADING
+ });
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.preemptive_ = options.preemptive !== undefined ?
+ options.preemptive : true;
+
+ /**
+ * @private
+ * @type {!ol.TileUrlFunctionType}
+ */
+ this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction;
+
+ /**
+ * @private
+ * @type {string|undefined}
+ */
+ this.template_ = undefined;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.jsonp_ = options.jsonp || false;
+
+ if (options.url) {
+ if (this.jsonp_) {
+ ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this),
+ this.handleTileJSONError.bind(this));
+ } else {
+ var client = new XMLHttpRequest();
+ client.addEventListener('load', this.onXHRLoad_.bind(this));
+ client.addEventListener('error', this.onXHRError_.bind(this));
+ client.open('GET', options.url);
+ client.send();
+ }
+ } else if (options.tileJSON) {
+ this.handleTileJSONResponse(options.tileJSON);
+ } else {
+ ol.asserts.assert(false, 51); // Either `url` or `tileJSON` options must be provided
+ }
+};
+ol.inherits(ol.source.TileUTFGrid, ol.source.Tile);
+
+
+/**
+ * @private
+ * @param {Event} event The load event.
+ */
+ol.source.TileUTFGrid.prototype.onXHRLoad_ = function(event) {
+ var client = /** @type {XMLHttpRequest} */ (event.target);
+ // status will be 0 for file:// urls
+ if (!client.status || client.status >= 200 && client.status < 300) {
+ var response;
+ try {
+ response = /** @type {TileJSON} */(JSON.parse(client.responseText));
+ } catch (err) {
+ this.handleTileJSONError();
+ return;
+ }
+ this.handleTileJSONResponse(response);
+ } else {
+ this.handleTileJSONError();
+ }
+};
+
+
+/**
+ * @private
+ * @param {Event} event The error event.
+ */
+ol.source.TileUTFGrid.prototype.onXHRError_ = function(event) {
+ this.handleTileJSONError();
+};
+
+
+/**
+ * Return the template from TileJSON.
+ * @return {string|undefined} The template from TileJSON.
+ * @api
+ */
+ol.source.TileUTFGrid.prototype.getTemplate = function() {
+ return this.template_;
+};
+
+
+/**
+ * Calls the callback (synchronously by default) with the available data
+ * for given coordinate and resolution (or `null` if not yet loaded or
+ * in case of an error).
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @param {function(this: T, *)} callback Callback.
+ * @param {T=} opt_this The object to use as `this` in the callback.
+ * @param {boolean=} opt_request If `true` the callback is always async.
+ * The tile data is requested if not yet loaded.
+ * @template T
+ * @api
+ */
+ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function(
+ coordinate, resolution, callback, opt_this, opt_request) {
+ if (this.tileGrid) {
+ var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, resolution);
+ var tile = /** @type {!ol.source.TileUTFGrid.Tile_} */(this.getTile(
+ tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection()));
+ tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_request);
+ } else {
+ if (opt_request === true) {
+ setTimeout(function() {
+ callback.call(opt_this, null);
+ }, 0);
+ } else {
+ callback.call(opt_this, null);
+ }
+ }
+};
+
+
+/**
+ * @protected
+ */
+ol.source.TileUTFGrid.prototype.handleTileJSONError = function() {
+ this.setState(ol.source.State.ERROR);
+};
+
+
+/**
+ * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse
+ * @protected
+ * @param {TileJSON} tileJSON Tile JSON.
+ */
+ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) {
+
+ var epsg4326Projection = ol.proj.get('EPSG:4326');
+
+ var sourceProjection = this.getProjection();
+ var extent;
+ if (tileJSON.bounds !== undefined) {
+ var transform = ol.proj.getTransformFromProjections(
+ epsg4326Projection, sourceProjection);
+ extent = ol.extent.applyTransform(tileJSON.bounds, transform);
+ }
+
+ if (tileJSON.scheme !== undefined) {
+ ol.DEBUG && console.assert(tileJSON.scheme == 'xyz', 'tileJSON-scheme is "xyz"');
+ }
+ var minZoom = tileJSON.minzoom || 0;
+ var maxZoom = tileJSON.maxzoom || 22;
+ var tileGrid = ol.tilegrid.createXYZ({
+ extent: ol.tilegrid.extentFromProjection(sourceProjection),
+ maxZoom: maxZoom,
+ minZoom: minZoom
+ });
+ this.tileGrid = tileGrid;
+
+ this.template_ = tileJSON.template;
+
+ var grids = tileJSON.grids;
+ if (!grids) {
+ this.setState(ol.source.State.ERROR);
+ return;
+ }
+
+ this.tileUrlFunction_ =
+ ol.TileUrlFunction.createFromTemplates(grids, tileGrid);
+
+ if (tileJSON.attribution !== undefined) {
+ var attributionExtent = extent !== undefined ?
+ extent : epsg4326Projection.getExtent();
+ /** @type {Object.<string, Array.<ol.TileRange>>} */
+ var tileRanges = {};
+ var z, zKey;
+ for (z = minZoom; z <= maxZoom; ++z) {
+ zKey = z.toString();
+ tileRanges[zKey] =
+ [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)];
+ }
+ this.setAttributions([
+ new ol.Attribution({
+ html: tileJSON.attribution,
+ tileRanges: tileRanges
+ })
+ ]);
+ }
+
+ this.setState(ol.source.State.READY);
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projection) {
+ var tileCoordKey = this.getKeyZXY(z, x, y);
+ if (this.tileCache.containsKey(tileCoordKey)) {
+ return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
+ } else {
+ ol.DEBUG && console.assert(projection, 'argument projection is truthy');
+ var tileCoord = [z, x, y];
+ var urlTileCoord =
+ this.getTileCoordForTileUrlFunction(tileCoord, projection);
+ var tileUrl = this.tileUrlFunction_(urlTileCoord, pixelRatio, projection);
+ var tile = new ol.source.TileUTFGrid.Tile_(
+ tileCoord,
+ tileUrl !== undefined ? ol.Tile.State.IDLE : ol.Tile.State.EMPTY,
+ tileUrl !== undefined ? tileUrl : '',
+ this.tileGrid.getTileCoordExtent(tileCoord),
+ this.preemptive_,
+ this.jsonp_);
+ this.tileCache.set(tileCoordKey, tile);
+ return tile;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) {
+ var tileCoordKey = this.getKeyZXY(z, x, y);
+ if (this.tileCache.containsKey(tileCoordKey)) {
+ this.tileCache.get(tileCoordKey);
+ }
+};
+
+
+/**
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Tile.State} state State.
+ * @param {string} src Image source URI.
+ * @param {ol.Extent} extent Extent of the tile.
+ * @param {boolean} preemptive Load the tile when visible (before it's needed).
+ * @param {boolean} jsonp Load the tile as a script.
+ * @private
+ */
+ol.source.TileUTFGrid.Tile_ = function(tileCoord, state, src, extent, preemptive, jsonp) {
+
+ ol.Tile.call(this, tileCoord, state);
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.extent_ = extent;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.preemptive_ = preemptive;
+
+ /**
+ * @private
+ * @type {Array.<string>}
+ */
+ this.grid_ = null;
+
+ /**
+ * @private
+ * @type {Array.<string>}
+ */
+ this.keys_ = null;
+
+ /**
+ * @private
+ * @type {Object.<string, Object>|undefined}
+ */
+ this.data_ = null;
+
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.jsonp_ = jsonp;
+
+};
+ol.inherits(ol.source.TileUTFGrid.Tile_, ol.Tile);
+
+
+/**
+ * Get the image element for this tile.
+ * @return {Image} Image.
+ */
+ol.source.TileUTFGrid.Tile_.prototype.getImage = function() {
+ return null;
+};
+
+
+/**
+ * Synchronously returns data at given coordinate (if available).
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {*} The data.
+ */
+ol.source.TileUTFGrid.Tile_.prototype.getData = function(coordinate) {
+ if (!this.grid_ || !this.keys_) {
+ return null;
+ }
+ var xRelative = (coordinate[0] - this.extent_[0]) /
+ (this.extent_[2] - this.extent_[0]);
+ var yRelative = (coordinate[1] - this.extent_[1]) /
+ (this.extent_[3] - this.extent_[1]);
+
+ var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)];
+
+ if (typeof row !== 'string') {
+ return null;
+ }
+
+ var code = row.charCodeAt(Math.floor(xRelative * row.length));
+ if (code >= 93) {
+ code--;
+ }
+ if (code >= 35) {
+ code--;
+ }
+ code -= 32;
+
+ var data = null;
+ if (code in this.keys_) {
+ var id = this.keys_[code];
+ if (this.data_ && id in this.data_) {
+ data = this.data_[id];
+ } else {
+ data = id;
+ }
+ }
+ return data;
+};
+
+
+/**
+ * Calls the callback (synchronously by default) with the available data
+ * for given coordinate (or `null` if not yet loaded).
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {function(this: T, *)} callback Callback.
+ * @param {T=} opt_this The object to use as `this` in the callback.
+ * @param {boolean=} opt_request If `true` the callback is always async.
+ * The tile data is requested if not yet loaded.
+ * @template T
+ */
+ol.source.TileUTFGrid.Tile_.prototype.forDataAtCoordinate = function(coordinate, callback, opt_this, opt_request) {
+ if (this.state == ol.Tile.State.IDLE && opt_request === true) {
+ ol.events.listenOnce(this, ol.events.EventType.CHANGE, function(e) {
+ callback.call(opt_this, this.getData(coordinate));
+ }, this);
+ this.loadInternal_();
+ } else {
+ if (opt_request === true) {
+ setTimeout(function() {
+ callback.call(opt_this, this.getData(coordinate));
+ }.bind(this), 0);
+ } else {
+ callback.call(opt_this, this.getData(coordinate));
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileUTFGrid.Tile_.prototype.getKey = function() {
+ return this.src_;
+};
+
+
+/**
+ * @private
+ */
+ol.source.TileUTFGrid.Tile_.prototype.handleError_ = function() {
+ this.state = ol.Tile.State.ERROR;
+ this.changed();
+};
+
+
+/**
+ * @param {!UTFGridJSON} json UTFGrid data.
+ * @private
+ */
+ol.source.TileUTFGrid.Tile_.prototype.handleLoad_ = function(json) {
+ this.grid_ = json.grid;
+ this.keys_ = json.keys;
+ this.data_ = json.data;
+
+ this.state = ol.Tile.State.EMPTY;
+ this.changed();
+};
+
+
+/**
+ * @private
+ */
+ol.source.TileUTFGrid.Tile_.prototype.loadInternal_ = function() {
+ if (this.state == ol.Tile.State.IDLE) {
+ this.state = ol.Tile.State.LOADING;
+ if (this.jsonp_) {
+ ol.net.jsonp(this.src_, this.handleLoad_.bind(this),
+ this.handleError_.bind(this));
+ } else {
+ var client = new XMLHttpRequest();
+ client.addEventListener('load', this.onXHRLoad_.bind(this));
+ client.addEventListener('error', this.onXHRError_.bind(this));
+ client.open('GET', this.src_);
+ client.send();
+ }
+ }
+};
+
+
+/**
+ * @private
+ * @param {Event} event The load event.
+ */
+ol.source.TileUTFGrid.Tile_.prototype.onXHRLoad_ = function(event) {
+ var client = /** @type {XMLHttpRequest} */ (event.target);
+ // status will be 0 for file:// urls
+ if (!client.status || client.status >= 200 && client.status < 300) {
+ var response;
+ try {
+ response = /** @type {!UTFGridJSON} */(JSON.parse(client.responseText));
+ } catch (err) {
+ this.handleError_();
+ return;
+ }
+ this.handleLoad_(response);
+ } else {
+ this.handleError_();
+ }
+};
+
+
+/**
+ * @private
+ * @param {Event} event The error event.
+ */
+ol.source.TileUTFGrid.Tile_.prototype.onXHRError_ = function(event) {
+ this.handleError_();
+};
+
+
+/**
+ * Load not yet loaded URI.
+ */
+ol.source.TileUTFGrid.Tile_.prototype.load = function() {
+ if (this.preemptive_) {
+ this.loadInternal_();
+ }
+};
+
+// FIXME add minZoom support
+// FIXME add date line wrap (tile coord transform)
+// FIXME cannot be shared between maps with different projections
+
+goog.provide('ol.source.TileWMS');
+
+goog.require('ol');
+goog.require('ol.asserts');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.math');
+goog.require('ol.proj');
+goog.require('ol.size');
+goog.require('ol.source.TileImage');
+goog.require('ol.source.WMSServerType');
+goog.require('ol.tilecoord');
+goog.require('ol.string');
+goog.require('ol.uri');
+
+/**
+ * @classdesc
+ * Layer source for tile data from WMS servers.
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.TileWMSOptions=} opt_options Tile WMS options.
+ * @api stable
+ */
+ol.source.TileWMS = function(opt_options) {
+
+ var options = opt_options || {};
+
+ var params = options.params || {};
+
+ var transparent = 'TRANSPARENT' in params ? params['TRANSPARENT'] : true;
+
+ ol.source.TileImage.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ logo: options.logo,
+ opaque: !transparent,
+ projection: options.projection,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileGrid: options.tileGrid,
+ tileLoadFunction: options.tileLoadFunction,
+ url: options.url,
+ urls: options.urls,
+ wrapX: options.wrapX !== undefined ? options.wrapX : true
+ });
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.gutter_ = options.gutter !== undefined ? options.gutter : 0;
+
+ /**
+ * @private
+ * @type {!Object}
+ */
+ this.params_ = params;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.v13_ = true;
+
+ /**
+ * @private
+ * @type {ol.source.WMSServerType|undefined}
+ */
+ this.serverType_ =
+ /** @type {ol.source.WMSServerType|undefined} */ (options.serverType);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.coordKeyPrefix_ = '';
+ this.resetCoordKeyPrefix_();
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.tmpExtent_ = ol.extent.createEmpty();
+
+ this.updateV13_();
+ this.setKey(this.getKeyForParams_());
+
+};
+ol.inherits(ol.source.TileWMS, ol.source.TileImage);
+
+
+/**
+ * Return the GetFeatureInfo URL for the passed coordinate, resolution, and
+ * projection. Return `undefined` if the GetFeatureInfo URL cannot be
+ * constructed.
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @param {ol.ProjectionLike} projection Projection.
+ * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should
+ * be provided. If `QUERY_LAYERS` is not provided then the layers specified
+ * in the `LAYERS` parameter will be used. `VERSION` should not be
+ * specified here.
+ * @return {string|undefined} GetFeatureInfo URL.
+ * @api stable
+ */
+ol.source.TileWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) {
+
+ ol.DEBUG && console.assert(!('VERSION' in params),
+ 'key VERSION is not allowed in params');
+
+ var projectionObj = ol.proj.get(projection);
+
+ var tileGrid = this.getTileGrid();
+ if (!tileGrid) {
+ tileGrid = this.getTileGridForProjection(projectionObj);
+ }
+
+ var tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, resolution);
+
+ if (tileGrid.getResolutions().length <= tileCoord[0]) {
+ return undefined;
+ }
+
+ var tileResolution = tileGrid.getResolution(tileCoord[0]);
+ var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
+ var tileSize = ol.size.toSize(
+ tileGrid.getTileSize(tileCoord[0]), this.tmpSize);
+
+ var gutter = this.gutter_;
+ if (gutter !== 0) {
+ tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize);
+ tileExtent = ol.extent.buffer(tileExtent,
+ tileResolution * gutter, tileExtent);
+ }
+
+ var baseParams = {
+ 'SERVICE': 'WMS',
+ 'VERSION': ol.DEFAULT_WMS_VERSION,
+ 'REQUEST': 'GetFeatureInfo',
+ 'FORMAT': 'image/png',
+ 'TRANSPARENT': true,
+ 'QUERY_LAYERS': this.params_['LAYERS']
+ };
+ ol.obj.assign(baseParams, this.params_, params);
+
+ var x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution);
+ var y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution);
+
+ baseParams[this.v13_ ? 'I' : 'X'] = x;
+ baseParams[this.v13_ ? 'J' : 'Y'] = y;
+
+ return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
+ 1, projectionObj, baseParams);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileWMS.prototype.getGutterInternal = function() {
+ return this.gutter_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileWMS.prototype.getKeyZXY = function(z, x, y) {
+ return this.coordKeyPrefix_ + ol.source.TileImage.prototype.getKeyZXY.call(this, z, x, y);
+};
+
+
+/**
+ * Get the user-provided params, i.e. those passed to the constructor through
+ * the "params" option, and possibly updated using the updateParams method.
+ * @return {Object} Params.
+ * @api stable
+ */
+ol.source.TileWMS.prototype.getParams = function() {
+ return this.params_;
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Size} tileSize Tile size.
+ * @param {ol.Extent} tileExtent Tile extent.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @param {Object} params Params.
+ * @return {string|undefined} Request URL.
+ * @private
+ */
+ol.source.TileWMS.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent,
+ pixelRatio, projection, params) {
+
+ var urls = this.urls;
+ if (!urls) {
+ return undefined;
+ }
+
+ params['WIDTH'] = tileSize[0];
+ params['HEIGHT'] = tileSize[1];
+
+ params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode();
+
+ if (!('STYLES' in this.params_)) {
+ params['STYLES'] = '';
+ }
+
+ if (pixelRatio != 1) {
+ switch (this.serverType_) {
+ case ol.source.WMSServerType.GEOSERVER:
+ var dpi = (90 * pixelRatio + 0.5) | 0;
+ if ('FORMAT_OPTIONS' in params) {
+ params['FORMAT_OPTIONS'] += ';dpi:' + dpi;
+ } else {
+ params['FORMAT_OPTIONS'] = 'dpi:' + dpi;
+ }
+ break;
+ case ol.source.WMSServerType.MAPSERVER:
+ params['MAP_RESOLUTION'] = 90 * pixelRatio;
+ break;
+ case ol.source.WMSServerType.CARMENTA_SERVER:
+ case ol.source.WMSServerType.QGIS:
+ params['DPI'] = 90 * pixelRatio;
+ break;
+ default:
+ ol.asserts.assert(false, 52); // Unknown `serverType` configured
+ break;
+ }
+ }
+
+ var axisOrientation = projection.getAxisOrientation();
+ var bbox = tileExtent;
+ if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') {
+ var tmp;
+ tmp = tileExtent[0];
+ bbox[0] = tileExtent[1];
+ bbox[1] = tmp;
+ tmp = tileExtent[2];
+ bbox[2] = tileExtent[3];
+ bbox[3] = tmp;
+ }
+ params['BBOX'] = bbox.join(',');
+
+ var url;
+ if (urls.length == 1) {
+ url = urls[0];
+ } else {
+ var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length);
+ url = urls[index];
+ }
+ return ol.uri.appendParams(url, params);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileWMS.prototype.getTilePixelRatio = function(pixelRatio) {
+ return (!this.hidpi_ || this.serverType_ === undefined) ? 1 :
+ /** @type {number} */ (pixelRatio);
+};
+
+
+/**
+ * @private
+ */
+ol.source.TileWMS.prototype.resetCoordKeyPrefix_ = function() {
+ var i = 0;
+ var res = [];
+
+ if (this.urls) {
+ var j, jj;
+ for (j = 0, jj = this.urls.length; j < jj; ++j) {
+ res[i++] = this.urls[j];
+ }
+ }
+
+ this.coordKeyPrefix_ = res.join('#');
+};
+
+
+/**
+ * @private
+ * @return {string} The key for the current params.
+ */
+ol.source.TileWMS.prototype.getKeyForParams_ = function() {
+ var i = 0;
+ var res = [];
+ for (var key in this.params_) {
+ res[i++] = key + '-' + this.params_[key];
+ }
+ return res.join('/');
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileWMS.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) {
+
+ var tileGrid = this.getTileGrid();
+ if (!tileGrid) {
+ tileGrid = this.getTileGridForProjection(projection);
+ }
+
+ if (tileGrid.getResolutions().length <= tileCoord[0]) {
+ return undefined;
+ }
+
+ if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) {
+ pixelRatio = 1;
+ }
+
+ var tileResolution = tileGrid.getResolution(tileCoord[0]);
+ var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
+ var tileSize = ol.size.toSize(
+ tileGrid.getTileSize(tileCoord[0]), this.tmpSize);
+
+ var gutter = this.gutter_;
+ if (gutter !== 0) {
+ tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize);
+ tileExtent = ol.extent.buffer(tileExtent,
+ tileResolution * gutter, tileExtent);
+ }
+
+ if (pixelRatio != 1) {
+ tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize);
+ }
+
+ var baseParams = {
+ 'SERVICE': 'WMS',
+ 'VERSION': ol.DEFAULT_WMS_VERSION,
+ 'REQUEST': 'GetMap',
+ 'FORMAT': 'image/png',
+ 'TRANSPARENT': true
+ };
+ ol.obj.assign(baseParams, this.params_);
+
+ return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
+ pixelRatio, projection, baseParams);
+};
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileWMS.prototype.setUrls = function(urls) {
+ ol.source.TileImage.prototype.setUrls.call(this, urls);
+ this.resetCoordKeyPrefix_();
+};
+
+
+/**
+ * Update the user-provided params.
+ * @param {Object} params Params.
+ * @api stable
+ */
+ol.source.TileWMS.prototype.updateParams = function(params) {
+ ol.obj.assign(this.params_, params);
+ this.resetCoordKeyPrefix_();
+ this.updateV13_();
+ this.setKey(this.getKeyForParams_());
+};
+
+
+/**
+ * @private
+ */
+ol.source.TileWMS.prototype.updateV13_ = function() {
+ var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION;
+ this.v13_ = ol.string.compareVersions(version, '1.3') >= 0;
+};
+
+goog.provide('ol.VectorTile');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.dom');
+goog.require('ol.featureloader');
+
+
+/**
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Tile.State} state State.
+ * @param {string} src Data source url.
+ * @param {ol.format.Feature} format Feature format.
+ * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
+ */
+ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) {
+
+ ol.Tile.call(this, tileCoord, state);
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.context_ = ol.dom.createCanvasContext2D();
+
+ /**
+ * @private
+ * @type {ol.format.Feature}
+ */
+ this.format_ = format;
+
+ /**
+ * @private
+ * @type {Array.<ol.Feature>}
+ */
+ this.features_ = null;
+
+ /**
+ * @private
+ * @type {ol.FeatureLoader}
+ */
+ this.loader_;
+
+ /**
+ * Data projection
+ * @private
+ * @type {ol.proj.Projection}
+ */
+ this.projection_;
+
+ /**
+ * @private
+ * @type {ol.TileReplayState}
+ */
+ this.replayState_ = {
+ dirty: false,
+ renderedRenderOrder: null,
+ renderedRevision: -1,
+ renderedTileRevision: -1,
+ replayGroup: null
+ };
+
+ /**
+ * @private
+ * @type {ol.TileLoadFunctionType}
+ */
+ this.tileLoadFunction_ = tileLoadFunction;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.url_ = src;
+
+};
+ol.inherits(ol.VectorTile, ol.Tile);
+
+
+/**
+ * @return {CanvasRenderingContext2D} The rendering context.
+ */
+ol.VectorTile.prototype.getContext = function() {
+ return this.context_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.VectorTile.prototype.getImage = function() {
+ return this.replayState_.renderedTileRevision == -1 ?
+ null : this.context_.canvas;
+};
+
+
+/**
+ * Get the feature format assigned for reading this tile's features.
+ * @return {ol.format.Feature} Feature format.
+ * @api
+ */
+ol.VectorTile.prototype.getFormat = function() {
+ return this.format_;
+};
+
+
+/**
+ * @return {Array.<ol.Feature>} Features.
+ */
+ol.VectorTile.prototype.getFeatures = function() {
+ return this.features_;
+};
+
+
+/**
+ * @return {ol.TileReplayState} The replay state.
+ */
+ol.VectorTile.prototype.getReplayState = function() {
+ return this.replayState_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.VectorTile.prototype.getKey = function() {
+ return this.url_;
+};
+
+
+/**
+ * @return {ol.proj.Projection} Feature projection.
+ */
+ol.VectorTile.prototype.getProjection = function() {
+ return this.projection_;
+};
+
+
+/**
+ * Load the tile.
+ */
+ol.VectorTile.prototype.load = function() {
+ if (this.state == ol.Tile.State.IDLE) {
+ this.setState(ol.Tile.State.LOADING);
+ this.tileLoadFunction_(this, this.url_);
+ this.loader_(null, NaN, null);
+ }
+};
+
+
+/**
+ * Handler for successful tile load.
+ * @param {Array.<ol.Feature>} features The loaded features.
+ * @param {ol.proj.Projection} dataProjection Data projection.
+ */
+ol.VectorTile.prototype.onLoad_ = function(features, dataProjection) {
+ this.setProjection(dataProjection);
+ this.setFeatures(features);
+};
+
+
+/**
+ * Handler for tile load errors.
+ */
+ol.VectorTile.prototype.onError_ = function() {
+ this.setState(ol.Tile.State.ERROR);
+};
+
+
+/**
+ * @param {Array.<ol.Feature>} features Features.
+ * @api
+ */
+ol.VectorTile.prototype.setFeatures = function(features) {
+ this.features_ = features;
+ this.setState(ol.Tile.State.LOADED);
+};
+
+
+/**
+ * Set the projection of the features that were added with {@link #setFeatures}.
+ * @param {ol.proj.Projection} projection Feature projection.
+ * @api
+ */
+ol.VectorTile.prototype.setProjection = function(projection) {
+ this.projection_ = projection;
+};
+
+
+/**
+ * @param {ol.Tile.State} tileState Tile state.
+ */
+ol.VectorTile.prototype.setState = function(tileState) {
+ this.state = tileState;
+ this.changed();
+};
+
+
+/**
+ * Set the feature loader for reading this tile's features.
+ * @param {ol.FeatureLoader} loader Feature loader.
+ * @api
+ */
+ol.VectorTile.prototype.setLoader = function(loader) {
+ this.loader_ = loader;
+};
+
+
+/**
+ * Sets the loader for a tile.
+ * @param {ol.VectorTile} tile Vector tile.
+ * @param {string} url URL.
+ */
+ol.VectorTile.defaultLoadFunction = function(tile, url) {
+ var loader = ol.featureloader.loadFeaturesXhr(
+ url, tile.getFormat(), tile.onLoad_.bind(tile), tile.onError_.bind(tile));
+
+ tile.setLoader(loader);
+};
+
+goog.provide('ol.source.VectorTile');
+
+goog.require('ol');
+goog.require('ol.Tile');
+goog.require('ol.VectorTile');
+goog.require('ol.events');
+goog.require('ol.events.EventType');
+goog.require('ol.size');
+goog.require('ol.source.UrlTile');
+
+
+/**
+ * @classdesc
+ * Class for layer sources providing vector data divided into a tile grid, to be
+ * used with {@link ol.layer.VectorTile}. Although this source receives tiles
+ * with vector features from the server, it is not meant for feature editing.
+ * Features are optimized for rendering, their geometries are clipped at or near
+ * tile boundaries and simplified for a view resolution. See
+ * {@link ol.source.Vector} for vector sources that are suitable for feature
+ * editing.
+ *
+ * @constructor
+ * @fires ol.source.Tile.Event
+ * @extends {ol.source.UrlTile}
+ * @param {olx.source.VectorTileOptions} options Vector tile options.
+ * @api
+ */
+ol.source.VectorTile = function(options) {
+
+ ol.source.UrlTile.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128,
+ extent: options.extent,
+ logo: options.logo,
+ opaque: false,
+ projection: options.projection,
+ state: options.state,
+ tileGrid: options.tileGrid,
+ tileLoadFunction: options.tileLoadFunction ?
+ options.tileLoadFunction : ol.VectorTile.defaultLoadFunction,
+ tileUrlFunction: options.tileUrlFunction,
+ tilePixelRatio: options.tilePixelRatio,
+ url: options.url,
+ urls: options.urls,
+ wrapX: options.wrapX === undefined ? true : options.wrapX
+ });
+
+ /**
+ * @private
+ * @type {ol.format.Feature}
+ */
+ this.format_ = options.format ? options.format : null;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.overlaps_ = options.overlaps == undefined ? true : options.overlaps;
+
+ /**
+ * @protected
+ * @type {function(new: ol.VectorTile, ol.TileCoord, ol.Tile.State, string,
+ * ol.format.Feature, ol.TileLoadFunctionType)}
+ */
+ this.tileClass = options.tileClass ? options.tileClass : ol.VectorTile;
+
+};
+ol.inherits(ol.source.VectorTile, ol.source.UrlTile);
+
+
+/**
+ * @return {boolean} The source can have overlapping geometries.
+ */
+ol.source.VectorTile.prototype.getOverlaps = function() {
+ return this.overlaps_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projection) {
+ var tileCoordKey = this.getKeyZXY(z, x, y);
+ if (this.tileCache.containsKey(tileCoordKey)) {
+ return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
+ } else {
+ var tileCoord = [z, x, y];
+ var urlTileCoord = this.getTileCoordForTileUrlFunction(
+ tileCoord, projection);
+ var tileUrl = urlTileCoord ?
+ this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined;
+ var tile = new this.tileClass(
+ tileCoord,
+ tileUrl !== undefined ? ol.Tile.State.IDLE : ol.Tile.State.EMPTY,
+ tileUrl !== undefined ? tileUrl : '',
+ this.format_, this.tileLoadFunction);
+ ol.events.listen(tile, ol.events.EventType.CHANGE,
+ this.handleTileChange, this);
+
+ this.tileCache.set(tileCoordKey, tile);
+ return tile;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.VectorTile.prototype.getTilePixelRatio = function(opt_pixelRatio) {
+ return opt_pixelRatio == undefined ?
+ ol.source.UrlTile.prototype.getTilePixelRatio.call(this, opt_pixelRatio) :
+ opt_pixelRatio;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.VectorTile.prototype.getTilePixelSize = function(z, pixelRatio, projection) {
+ var tileSize = ol.size.toSize(this.tileGrid.getTileSize(z));
+ return [Math.round(tileSize[0] * pixelRatio), Math.round(tileSize[1] * pixelRatio)];
+};
+
+goog.provide('ol.tilegrid.WMTS');
+
+goog.require('ol');
+goog.require('ol.array');
+goog.require('ol.proj');
+goog.require('ol.tilegrid.TileGrid');
+
+
+/**
+ * @classdesc
+ * Set the grid pattern for sources accessing WMTS tiled-image servers.
+ *
+ * @constructor
+ * @extends {ol.tilegrid.TileGrid}
+ * @param {olx.tilegrid.WMTSOptions} options WMTS options.
+ * @struct
+ * @api
+ */
+ol.tilegrid.WMTS = function(options) {
+
+ ol.DEBUG && console.assert(
+ options.resolutions.length == options.matrixIds.length,
+ 'options resolutions and matrixIds must have equal length (%s == %s)',
+ options.resolutions.length, options.matrixIds.length);
+
+ /**
+ * @private
+ * @type {!Array.<string>}
+ */
+ this.matrixIds_ = options.matrixIds;
+ // FIXME: should the matrixIds become optionnal?
+
+ ol.tilegrid.TileGrid.call(this, {
+ extent: options.extent,
+ origin: options.origin,
+ origins: options.origins,
+ resolutions: options.resolutions,
+ tileSize: options.tileSize,
+ tileSizes: options.tileSizes,
+ sizes: options.sizes
+ });
+
+};
+ol.inherits(ol.tilegrid.WMTS, ol.tilegrid.TileGrid);
+
+
+/**
+ * @param {number} z Z.
+ * @return {string} MatrixId..
+ */
+ol.tilegrid.WMTS.prototype.getMatrixId = function(z) {
+ ol.DEBUG && console.assert(0 <= z && z < this.matrixIds_.length,
+ 'attempted to retrieve matrixId for illegal z (%s)', z);
+ return this.matrixIds_[z];
+};
+
+
+/**
+ * Get the list of matrix identifiers.
+ * @return {Array.<string>} MatrixIds.
+ * @api
+ */
+ol.tilegrid.WMTS.prototype.getMatrixIds = function() {
+ return this.matrixIds_;
+};
+
+
+/**
+ * Create a tile grid from a WMTS capabilities matrix set and an
+ * optional TileMatrixSetLimits.
+ * @param {Object} matrixSet An object representing a matrixSet in the
+ * capabilities document.
+ * @param {ol.Extent=} opt_extent An optional extent to restrict the tile
+ * ranges the server provides.
+ * @param {Array.<Object>=} opt_matrixLimits An optional object representing
+ * the available matrices for tileGrid.
+ * @return {ol.tilegrid.WMTS} WMTS tileGrid instance.
+ * @api
+ */
+ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = function(matrixSet, opt_extent,
+ opt_matrixLimits) {
+
+ /** @type {!Array.<number>} */
+ var resolutions = [];
+ /** @type {!Array.<string>} */
+ var matrixIds = [];
+ /** @type {!Array.<ol.Coordinate>} */
+ var origins = [];
+ /** @type {!Array.<ol.Size>} */
+ var tileSizes = [];
+ /** @type {!Array.<ol.Size>} */
+ var sizes = [];
+
+ var matrixLimits = opt_matrixLimits !== undefined ? opt_matrixLimits : [];
+
+ var supportedCRSPropName = 'SupportedCRS';
+ var matrixIdsPropName = 'TileMatrix';
+ var identifierPropName = 'Identifier';
+ var scaleDenominatorPropName = 'ScaleDenominator';
+ var topLeftCornerPropName = 'TopLeftCorner';
+ var tileWidthPropName = 'TileWidth';
+ var tileHeightPropName = 'TileHeight';
+
+ var projection;
+ projection = ol.proj.get(matrixSet[supportedCRSPropName].replace(
+ /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'));
+ var metersPerUnit = projection.getMetersPerUnit();
+ // swap origin x and y coordinates if axis orientation is lat/long
+ var switchOriginXY = projection.getAxisOrientation().substr(0, 2) == 'ne';
+
+ matrixSet[matrixIdsPropName].sort(function(a, b) {
+ return b[scaleDenominatorPropName] - a[scaleDenominatorPropName];
+ });
+
+ matrixSet[matrixIdsPropName].forEach(function(elt, index, array) {
+
+ var matrixAvailable;
+ // use of matrixLimits to filter TileMatrices from GetCapabilities
+ // TileMatrixSet from unavailable matrix levels.
+ if (matrixLimits.length > 0) {
+ matrixAvailable = ol.array.find(matrixLimits,
+ function(elt_ml, index_ml, array_ml) {
+ return elt[identifierPropName] == elt_ml[matrixIdsPropName];
+ });
+ } else {
+ matrixAvailable = true;
+ }
+
+ if (matrixAvailable) {
+ matrixIds.push(elt[identifierPropName]);
+ var resolution = elt[scaleDenominatorPropName] * 0.28E-3 / metersPerUnit;
+ var tileWidth = elt[tileWidthPropName];
+ var tileHeight = elt[tileHeightPropName];
+ if (switchOriginXY) {
+ origins.push([elt[topLeftCornerPropName][1],
+ elt[topLeftCornerPropName][0]]);
+ } else {
+ origins.push(elt[topLeftCornerPropName]);
+ }
+ resolutions.push(resolution);
+ tileSizes.push(tileWidth == tileHeight ?
+ tileWidth : [tileWidth, tileHeight]);
+ // top-left origin, so height is negative
+ sizes.push([elt['MatrixWidth'], -elt['MatrixHeight']]);
+ }
+ });
+
+ return new ol.tilegrid.WMTS({
+ extent: opt_extent,
+ origins: origins,
+ resolutions: resolutions,
+ matrixIds: matrixIds,
+ tileSizes: tileSizes,
+ sizes: sizes
+ });
+};
+
+goog.provide('ol.source.WMTS');
+
+goog.require('ol');
+goog.require('ol.TileUrlFunction');
+goog.require('ol.array');
+goog.require('ol.extent');
+goog.require('ol.obj');
+goog.require('ol.proj');
+goog.require('ol.source.TileImage');
+goog.require('ol.tilegrid.WMTS');
+goog.require('ol.uri');
+
+
+/**
+ * @classdesc
+ * Layer source for tile data from WMTS servers.
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.WMTSOptions} options WMTS options.
+ * @api stable
+ */
+ol.source.WMTS = function(options) {
+
+ // TODO: add support for TileMatrixLimits
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.version_ = options.version !== undefined ? options.version : '1.0.0';
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.format_ = options.format !== undefined ? options.format : 'image/jpeg';
+
+ /**
+ * @private
+ * @type {!Object}
+ */
+ this.dimensions_ = options.dimensions !== undefined ? options.dimensions : {};
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.layer_ = options.layer;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.matrixSet_ = options.matrixSet;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.style_ = options.style;
+
+ var urls = options.urls;
+ if (urls === undefined && options.url !== undefined) {
+ urls = ol.TileUrlFunction.expandUrl(options.url);
+ }
+
+ // FIXME: should we guess this requestEncoding from options.url(s)
+ // structure? that would mean KVP only if a template is not provided.
+
+ /**
+ * @private
+ * @type {ol.source.WMTS.RequestEncoding}
+ */
+ this.requestEncoding_ = options.requestEncoding !== undefined ?
+ /** @type {ol.source.WMTS.RequestEncoding} */ (options.requestEncoding) :
+ ol.source.WMTS.RequestEncoding.KVP;
+
+ var requestEncoding = this.requestEncoding_;
+
+ // FIXME: should we create a default tileGrid?
+ // we could issue a getCapabilities xhr to retrieve missing configuration
+ var tileGrid = options.tileGrid;
+
+ // context property names are lower case to allow for a case insensitive
+ // replacement as some services use different naming conventions
+ var context = {
+ 'layer': this.layer_,
+ 'style': this.style_,
+ 'tilematrixset': this.matrixSet_
+ };
+
+ if (requestEncoding == ol.source.WMTS.RequestEncoding.KVP) {
+ ol.obj.assign(context, {
+ 'Service': 'WMTS',
+ 'Request': 'GetTile',
+ 'Version': this.version_,
+ 'Format': this.format_
+ });
+ }
+
+ var dimensions = this.dimensions_;
+
+ /**
+ * @param {string} template Template.
+ * @return {ol.TileUrlFunctionType} Tile URL function.
+ */
+ function createFromWMTSTemplate(template) {
+
+ // TODO: we may want to create our own appendParams function so that params
+ // order conforms to wmts spec guidance, and so that we can avoid to escape
+ // special template params
+
+ template = (requestEncoding == ol.source.WMTS.RequestEncoding.KVP) ?
+ ol.uri.appendParams(template, context) :
+ template.replace(/\{(\w+?)\}/g, function(m, p) {
+ return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m;
+ });
+
+ return (
+ /**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string|undefined} Tile URL.
+ */
+ function(tileCoord, pixelRatio, projection) {
+ if (!tileCoord) {
+ return undefined;
+ } else {
+ var localContext = {
+ 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]),
+ 'TileCol': tileCoord[1],
+ 'TileRow': -tileCoord[2] - 1
+ };
+ ol.obj.assign(localContext, dimensions);
+ var url = template;
+ if (requestEncoding == ol.source.WMTS.RequestEncoding.KVP) {
+ url = ol.uri.appendParams(url, localContext);
+ } else {
+ url = url.replace(/\{(\w+?)\}/g, function(m, p) {
+ return localContext[p];
+ });
+ }
+ return url;
+ }
+ });
+ }
+
+ var tileUrlFunction = (urls && urls.length > 0) ?
+ ol.TileUrlFunction.createFromTileUrlFunctions(
+ urls.map(createFromWMTSTemplate)) :
+ ol.TileUrlFunction.nullTileUrlFunction;
+
+ ol.source.TileImage.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ logo: options.logo,
+ projection: options.projection,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileClass: options.tileClass,
+ tileGrid: tileGrid,
+ tileLoadFunction: options.tileLoadFunction,
+ tilePixelRatio: options.tilePixelRatio,
+ tileUrlFunction: tileUrlFunction,
+ urls: urls,
+ wrapX: options.wrapX !== undefined ? options.wrapX : false
+ });
+
+ this.setKey(this.getKeyForDimensions_());
+
+};
+ol.inherits(ol.source.WMTS, ol.source.TileImage);
+
+
+/**
+ * Get the dimensions, i.e. those passed to the constructor through the
+ * "dimensions" option, and possibly updated using the updateDimensions
+ * method.
+ * @return {!Object} Dimensions.
+ * @api
+ */
+ol.source.WMTS.prototype.getDimensions = function() {
+ return this.dimensions_;
+};
+
+
+/**
+ * Return the image format of the WMTS source.
+ * @return {string} Format.
+ * @api
+ */
+ol.source.WMTS.prototype.getFormat = function() {
+ return this.format_;
+};
+
+
+/**
+ * Return the layer of the WMTS source.
+ * @return {string} Layer.
+ * @api
+ */
+ol.source.WMTS.prototype.getLayer = function() {
+ return this.layer_;
+};
+
+
+/**
+ * Return the matrix set of the WMTS source.
+ * @return {string} MatrixSet.
+ * @api
+ */
+ol.source.WMTS.prototype.getMatrixSet = function() {
+ return this.matrixSet_;
+};
+
+
+/**
+ * Return the request encoding, either "KVP" or "REST".
+ * @return {ol.source.WMTS.RequestEncoding} Request encoding.
+ * @api
+ */
+ol.source.WMTS.prototype.getRequestEncoding = function() {
+ return this.requestEncoding_;
+};
+
+
+/**
+ * Return the style of the WMTS source.
+ * @return {string} Style.
+ * @api
+ */
+ol.source.WMTS.prototype.getStyle = function() {
+ return this.style_;
+};
+
+
+/**
+ * Return the version of the WMTS source.
+ * @return {string} Version.
+ * @api
+ */
+ol.source.WMTS.prototype.getVersion = function() {
+ return this.version_;
+};
+
+
+/**
+ * @private
+ * @return {string} The key for the current dimensions.
+ */
+ol.source.WMTS.prototype.getKeyForDimensions_ = function() {
+ var i = 0;
+ var res = [];
+ for (var key in this.dimensions_) {
+ res[i++] = key + '-' + this.dimensions_[key];
+ }
+ return res.join('/');
+};
+
+
+/**
+ * Update the dimensions.
+ * @param {Object} dimensions Dimensions.
+ * @api
+ */
+ol.source.WMTS.prototype.updateDimensions = function(dimensions) {
+ ol.obj.assign(this.dimensions_, dimensions);
+ this.setKey(this.getKeyForDimensions_());
+};
+
+
+/**
+ * Generate source options from a capabilities object.
+ * @param {Object} wmtsCap An object representing the capabilities document.
+ * @param {Object} config Configuration properties for the layer. Defaults for
+ * the layer will apply if not provided.
+ *
+ * Required config properties:
+ * - layer - {string} The layer identifier.
+ *
+ * Optional config properties:
+ * - matrixSet - {string} The matrix set identifier, required if there is
+ * more than one matrix set in the layer capabilities.
+ * - projection - {string} The desired CRS when no matrixSet is specified.
+ * eg: "EPSG:3857". If the desired projection is not available,
+ * an error is thrown.
+ * - requestEncoding - {string} url encoding format for the layer. Default is
+ * the first tile url format found in the GetCapabilities response.
+ * - style - {string} The name of the style
+ * - format - {string} Image format for the layer. Default is the first
+ * format returned in the GetCapabilities response.
+ * @return {olx.source.WMTSOptions} WMTS source options object.
+ * @api
+ */
+ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) {
+
+ // TODO: add support for TileMatrixLimits
+ ol.DEBUG && console.assert(config['layer'],
+ 'config "layer" must not be null');
+
+ var layers = wmtsCap['Contents']['Layer'];
+ var l = ol.array.find(layers, function(elt, index, array) {
+ return elt['Identifier'] == config['layer'];
+ });
+ ol.DEBUG && console.assert(l, 'found a matching layer in Contents/Layer');
+
+ ol.DEBUG && console.assert(l['TileMatrixSetLink'].length > 0,
+ 'layer has TileMatrixSetLink');
+ var tileMatrixSets = wmtsCap['Contents']['TileMatrixSet'];
+ var idx, matrixSet, matrixLimits;
+ if (l['TileMatrixSetLink'].length > 1) {
+ if ('projection' in config) {
+ idx = ol.array.findIndex(l['TileMatrixSetLink'],
+ function(elt, index, array) {
+ var tileMatrixSet = ol.array.find(tileMatrixSets, function(el) {
+ return el['Identifier'] == elt['TileMatrixSet'];
+ });
+ var supportedCRS = tileMatrixSet['SupportedCRS'].replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3');
+ var proj1 = ol.proj.get(supportedCRS);
+ var proj2 = ol.proj.get(config['projection']);
+ if (proj1 && proj2) {
+ return ol.proj.equivalent(proj1, proj2);
+ } else {
+ return supportedCRS == config['projection'];
+ }
+ });
+ } else {
+ idx = ol.array.findIndex(l['TileMatrixSetLink'],
+ function(elt, index, array) {
+ return elt['TileMatrixSet'] == config['matrixSet'];
+ });
+ }
+ } else {
+ idx = 0;
+ }
+ if (idx < 0) {
+ idx = 0;
+ }
+ matrixSet = /** @type {string} */
+ (l['TileMatrixSetLink'][idx]['TileMatrixSet']);
+ matrixLimits = /** @type {Array.<Object>} */
+ (l['TileMatrixSetLink'][idx]['TileMatrixSetLimits']);
+
+ ol.DEBUG && console.assert(matrixSet, 'TileMatrixSet must not be null');
+
+ var format = /** @type {string} */ (l['Format'][0]);
+ if ('format' in config) {
+ format = config['format'];
+ }
+ idx = ol.array.findIndex(l['Style'], function(elt, index, array) {
+ if ('style' in config) {
+ return elt['Title'] == config['style'];
+ } else {
+ return elt['isDefault'];
+ }
+ });
+ if (idx < 0) {
+ idx = 0;
+ }
+ var style = /** @type {string} */ (l['Style'][idx]['Identifier']);
+
+ var dimensions = {};
+ if ('Dimension' in l) {
+ l['Dimension'].forEach(function(elt, index, array) {
+ var key = elt['Identifier'];
+ var value = elt['Default'];
+ if (value !== undefined) {
+ ol.DEBUG && console.assert(ol.array.includes(elt['Value'], value),
+ 'default value contained in values');
+ } else {
+ value = elt['Value'][0];
+ }
+ ol.DEBUG && console.assert(value !== undefined, 'value could be found');
+ dimensions[key] = value;
+ });
+ }
+
+ var matrixSets = wmtsCap['Contents']['TileMatrixSet'];
+ var matrixSetObj = ol.array.find(matrixSets, function(elt, index, array) {
+ return elt['Identifier'] == matrixSet;
+ });
+ ol.DEBUG && console.assert(matrixSetObj,
+ 'found matrixSet in Contents/TileMatrixSet');
+
+ var projection;
+ if ('projection' in config) {
+ projection = ol.proj.get(config['projection']);
+ } else {
+ projection = ol.proj.get(matrixSetObj['SupportedCRS'].replace(
+ /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'));
+ }
+
+ var wgs84BoundingBox = l['WGS84BoundingBox'];
+ var extent, wrapX;
+ if (wgs84BoundingBox !== undefined) {
+ var wgs84ProjectionExtent = ol.proj.get('EPSG:4326').getExtent();
+ wrapX = (wgs84BoundingBox[0] == wgs84ProjectionExtent[0] &&
+ wgs84BoundingBox[2] == wgs84ProjectionExtent[2]);
+ extent = ol.proj.transformExtent(
+ wgs84BoundingBox, 'EPSG:4326', projection);
+ var projectionExtent = projection.getExtent();
+ if (projectionExtent) {
+ // If possible, do a sanity check on the extent - it should never be
+ // bigger than the validity extent of the projection of a matrix set.
+ if (!ol.extent.containsExtent(projectionExtent, extent)) {
+ extent = undefined;
+ }
+ }
+ }
+
+ var tileGrid = ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet(
+ matrixSetObj, extent, matrixLimits);
+
+ /** @type {!Array.<string>} */
+ var urls = [];
+ var requestEncoding = config['requestEncoding'];
+ requestEncoding = requestEncoding !== undefined ? requestEncoding : '';
+
+ ol.DEBUG && console.assert(
+ ol.array.includes(['REST', 'RESTful', 'KVP', ''], requestEncoding),
+ 'requestEncoding (%s) is one of "REST", "RESTful", "KVP" or ""',
+ requestEncoding);
+
+ if ('OperationsMetadata' in wmtsCap && 'GetTile' in wmtsCap['OperationsMetadata']) {
+ var gets = wmtsCap['OperationsMetadata']['GetTile']['DCP']['HTTP']['Get'];
+ ol.DEBUG && console.assert(gets.length >= 1);
+
+ for (var i = 0, ii = gets.length; i < ii; ++i) {
+ var constraint = ol.array.find(gets[i]['Constraint'], function(element) {
+ return element['name'] == 'GetEncoding';
+ });
+ var encodings = constraint['AllowedValues']['Value'];
+ ol.DEBUG && console.assert(encodings.length >= 1);
+
+ if (requestEncoding === '') {
+ // requestEncoding not provided, use the first encoding from the list
+ requestEncoding = encodings[0];
+ }
+ if (requestEncoding === ol.source.WMTS.RequestEncoding.KVP) {
+ if (ol.array.includes(encodings, ol.source.WMTS.RequestEncoding.KVP)) {
+ urls.push(/** @type {string} */ (gets[i]['href']));
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ if (urls.length === 0) {
+ requestEncoding = ol.source.WMTS.RequestEncoding.REST;
+ l['ResourceURL'].forEach(function(element) {
+ if (element['resourceType'] === 'tile') {
+ format = element['format'];
+ urls.push(/** @type {string} */ (element['template']));
+ }
+ });
+ }
+ ol.DEBUG && console.assert(urls.length > 0, 'At least one URL found');
+
+ return {
+ urls: urls,
+ layer: config['layer'],
+ matrixSet: matrixSet,
+ format: format,
+ projection: projection,
+ requestEncoding: requestEncoding,
+ tileGrid: tileGrid,
+ style: style,
+ dimensions: dimensions,
+ wrapX: wrapX
+ };
+
+};
+
+
+/**
+ * Request encoding. One of 'KVP', 'REST'.
+ * @enum {string}
+ */
+ol.source.WMTS.RequestEncoding = {
+ KVP: 'KVP', // see spec §8
+ REST: 'REST' // see spec §10
+};
+
+goog.provide('ol.source.Zoomify');
+
+goog.require('ol');
+goog.require('ol.ImageTile');
+goog.require('ol.Tile');
+goog.require('ol.asserts');
+goog.require('ol.dom');
+goog.require('ol.extent');
+goog.require('ol.source.TileImage');
+goog.require('ol.tilegrid.TileGrid');
+
+
+/**
+ * @classdesc
+ * Layer source for tile data in Zoomify format.
+ *
+ * @constructor
+ * @extends {ol.source.TileImage}
+ * @param {olx.source.ZoomifyOptions=} opt_options Options.
+ * @api stable
+ */
+ol.source.Zoomify = function(opt_options) {
+
+ var options = opt_options || {};
+
+ var size = options.size;
+ var tierSizeCalculation = options.tierSizeCalculation !== undefined ?
+ options.tierSizeCalculation :
+ ol.source.Zoomify.TierSizeCalculation.DEFAULT;
+
+ var imageWidth = size[0];
+ var imageHeight = size[1];
+ var tierSizeInTiles = [];
+ var tileSize = ol.DEFAULT_TILE_SIZE;
+
+ switch (tierSizeCalculation) {
+ case ol.source.Zoomify.TierSizeCalculation.DEFAULT:
+ while (imageWidth > tileSize || imageHeight > tileSize) {
+ tierSizeInTiles.push([
+ Math.ceil(imageWidth / tileSize),
+ Math.ceil(imageHeight / tileSize)
+ ]);
+ tileSize += tileSize;
+ }
+ break;
+ case ol.source.Zoomify.TierSizeCalculation.TRUNCATED:
+ var width = imageWidth;
+ var height = imageHeight;
+ while (width > tileSize || height > tileSize) {
+ tierSizeInTiles.push([
+ Math.ceil(width / tileSize),
+ Math.ceil(height / tileSize)
+ ]);
+ width >>= 1;
+ height >>= 1;
+ }
+ break;
+ default:
+ ol.asserts.assert(false, 53); // Unknown `tierSizeCalculation` configured
+ break;
+ }
+
+ tierSizeInTiles.push([1, 1]);
+ tierSizeInTiles.reverse();
+
+ var resolutions = [1];
+ var tileCountUpToTier = [0];
+ var i, ii;
+ for (i = 1, ii = tierSizeInTiles.length; i < ii; i++) {
+ resolutions.push(1 << i);
+ tileCountUpToTier.push(
+ tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] +
+ tileCountUpToTier[i - 1]
+ );
+ }
+ resolutions.reverse();
+
+ var extent = [0, -size[1], size[0], 0];
+ var tileGrid = new ol.tilegrid.TileGrid({
+ extent: extent,
+ origin: ol.extent.getTopLeft(extent),
+ resolutions: resolutions
+ });
+
+ var url = options.url;
+
+ /**
+ * @this {ol.source.TileImage}
+ * @param {ol.TileCoord} tileCoord Tile Coordinate.
+ * @param {number} pixelRatio Pixel ratio.
+ * @param {ol.proj.Projection} projection Projection.
+ * @return {string|undefined} Tile URL.
+ */
+ function tileUrlFunction(tileCoord, pixelRatio, projection) {
+ if (!tileCoord) {
+ return undefined;
+ } else {
+ var tileCoordZ = tileCoord[0];
+ var tileCoordX = tileCoord[1];
+ var tileCoordY = -tileCoord[2] - 1;
+ var tileIndex =
+ tileCoordX +
+ tileCoordY * tierSizeInTiles[tileCoordZ][0] +
+ tileCountUpToTier[tileCoordZ];
+ var tileGroup = (tileIndex / ol.DEFAULT_TILE_SIZE) | 0;
+ return url + 'TileGroup' + tileGroup + '/' +
+ tileCoordZ + '-' + tileCoordX + '-' + tileCoordY + '.jpg';
+ }
+ }
+
+ ol.source.TileImage.call(this, {
+ attributions: options.attributions,
+ cacheSize: options.cacheSize,
+ crossOrigin: options.crossOrigin,
+ logo: options.logo,
+ reprojectionErrorThreshold: options.reprojectionErrorThreshold,
+ tileClass: ol.source.Zoomify.Tile_,
+ tileGrid: tileGrid,
+ tileUrlFunction: tileUrlFunction
+ });
+
+};
+ol.inherits(ol.source.Zoomify, ol.source.TileImage);
+
+
+/**
+ * @constructor
+ * @extends {ol.ImageTile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.Tile.State} state State.
+ * @param {string} src Image source URI.
+ * @param {?string} crossOrigin Cross origin.
+ * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
+ * @private
+ */
+ol.source.Zoomify.Tile_ = function(
+ tileCoord, state, src, crossOrigin, tileLoadFunction) {
+
+ ol.ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction);
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement}
+ */
+ this.zoomifyImage_ = null;
+
+};
+ol.inherits(ol.source.Zoomify.Tile_, ol.ImageTile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.Zoomify.Tile_.prototype.getImage = function() {
+ if (this.zoomifyImage_) {
+ return this.zoomifyImage_;
+ }
+ var tileSize = ol.DEFAULT_TILE_SIZE;
+ var image = ol.ImageTile.prototype.getImage.call(this);
+ if (this.state == ol.Tile.State.LOADED) {
+ if (image.width == tileSize && image.height == tileSize) {
+ this.zoomifyImage_ = image;
+ return image;
+ } else {
+ var context = ol.dom.createCanvasContext2D(tileSize, tileSize);
+ context.drawImage(image, 0, 0);
+ this.zoomifyImage_ = context.canvas;
+ return context.canvas;
+ }
+ } else {
+ return image;
+ }
+};
+
+
+/**
+ * @enum {string}
+ */
+ol.source.Zoomify.TierSizeCalculation = {
+ DEFAULT: 'default',
+ TRUNCATED: 'truncated'
+};
+
+goog.provide('ol.style.Atlas');
+
+goog.require('ol');
+goog.require('ol.dom');
+
+
+/**
+ * This class facilitates the creation of image atlases.
+ *
+ * Images added to an atlas will be rendered onto a single
+ * atlas canvas. The distribution of images on the canvas is
+ * managed with the bin packing algorithm described in:
+ * http://www.blackpawn.com/texts/lightmaps/
+ *
+ * @constructor
+ * @struct
+ * @param {number} size The size in pixels of the sprite image.
+ * @param {number} space The space in pixels between images.
+ * Because texture coordinates are float values, the edges of
+ * images might not be completely correct (in a way that the
+ * edges overlap when being rendered). To avoid this we add a
+ * padding around each image.
+ */
+ol.style.Atlas = function(size, space) {
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.space_ = space;
+
+ /**
+ * @private
+ * @type {Array.<ol.AtlasBlock>}
+ */
+ this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}];
+
+ /**
+ * @private
+ * @type {Object.<string, ol.AtlasInfo>}
+ */
+ this.entries_ = {};
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.context_ = ol.dom.createCanvasContext2D(size, size);
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = this.context_.canvas;
+};
+
+
+/**
+ * @param {string} id The identifier of the entry to check.
+ * @return {?ol.AtlasInfo} The atlas info.
+ */
+ol.style.Atlas.prototype.get = function(id) {
+ return this.entries_[id] || null;
+};
+
+
+/**
+ * @param {string} id The identifier of the entry to add.
+ * @param {number} width The width.
+ * @param {number} height The height.
+ * @param {function(CanvasRenderingContext2D, number, number)} renderCallback
+ * Called to render the new image onto an atlas image.
+ * @param {Object=} opt_this Value to use as `this` when executing
+ * `renderCallback`.
+ * @return {?ol.AtlasInfo} The position and atlas image for the entry.
+ */
+ol.style.Atlas.prototype.add = function(id, width, height, renderCallback, opt_this) {
+ var block, i, ii;
+ for (i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) {
+ block = this.emptyBlocks_[i];
+ if (block.width >= width + this.space_ &&
+ block.height >= height + this.space_) {
+ // we found a block that is big enough for our entry
+ var entry = {
+ offsetX: block.x + this.space_,
+ offsetY: block.y + this.space_,
+ image: this.canvas_
+ };
+ this.entries_[id] = entry;
+
+ // render the image on the atlas image
+ renderCallback.call(opt_this, this.context_,
+ block.x + this.space_, block.y + this.space_);
+
+ // split the block after the insertion, either horizontally or vertically
+ this.split_(i, block, width + this.space_, height + this.space_);
+
+ return entry;
+ }
+ }
+
+ // there is no space for the new entry in this atlas
+ return null;
+};
+
+
+/**
+ * @private
+ * @param {number} index The index of the block.
+ * @param {ol.AtlasBlock} block The block to split.
+ * @param {number} width The width of the entry to insert.
+ * @param {number} height The height of the entry to insert.
+ */
+ol.style.Atlas.prototype.split_ = function(index, block, width, height) {
+ var deltaWidth = block.width - width;
+ var deltaHeight = block.height - height;
+
+ /** @type {ol.AtlasBlock} */
+ var newBlock1;
+ /** @type {ol.AtlasBlock} */
+ var newBlock2;
+
+ if (deltaWidth > deltaHeight) {
+ // split vertically
+ // block right of the inserted entry
+ newBlock1 = {
+ x: block.x + width,
+ y: block.y,
+ width: block.width - width,
+ height: block.height
+ };
+
+ // block below the inserted entry
+ newBlock2 = {
+ x: block.x,
+ y: block.y + height,
+ width: width,
+ height: block.height - height
+ };
+ this.updateBlocks_(index, newBlock1, newBlock2);
+ } else {
+ // split horizontally
+ // block right of the inserted entry
+ newBlock1 = {
+ x: block.x + width,
+ y: block.y,
+ width: block.width - width,
+ height: height
+ };
+
+ // block below the inserted entry
+ newBlock2 = {
+ x: block.x,
+ y: block.y + height,
+ width: block.width,
+ height: block.height - height
+ };
+ this.updateBlocks_(index, newBlock1, newBlock2);
+ }
+};
+
+
+/**
+ * Remove the old block and insert new blocks at the same array position.
+ * The new blocks are inserted at the same position, so that splitted
+ * blocks (that are potentially smaller) are filled first.
+ * @private
+ * @param {number} index The index of the block to remove.
+ * @param {ol.AtlasBlock} newBlock1 The 1st block to add.
+ * @param {ol.AtlasBlock} newBlock2 The 2nd block to add.
+ */
+ol.style.Atlas.prototype.updateBlocks_ = function(index, newBlock1, newBlock2) {
+ var args = [index, 1];
+ if (newBlock1.width > 0 && newBlock1.height > 0) {
+ args.push(newBlock1);
+ }
+ if (newBlock2.width > 0 && newBlock2.height > 0) {
+ args.push(newBlock2);
+ }
+ this.emptyBlocks_.splice.apply(this.emptyBlocks_, args);
+};
+
+goog.provide('ol.style.AtlasManager');
+
+goog.require('ol');
+goog.require('ol.style.Atlas');
+
+
+/**
+ * Manages the creation of image atlases.
+ *
+ * Images added to this manager will be inserted into an atlas, which
+ * will be used for rendering.
+ * The `size` given in the constructor is the size for the first
+ * atlas. After that, when new atlases are created, they will have
+ * twice the size as the latest atlas (until `maxSize` is reached).
+ *
+ * If an application uses many images or very large images, it is recommended
+ * to set a higher `size` value to avoid the creation of too many atlases.
+ *
+ * @constructor
+ * @struct
+ * @api
+ * @param {olx.style.AtlasManagerOptions=} opt_options Options.
+ */
+ol.style.AtlasManager = function(opt_options) {
+
+ var options = opt_options || {};
+
+ /**
+ * The size in pixels of the latest atlas image.
+ * @private
+ * @type {number}
+ */
+ this.currentSize_ = options.initialSize !== undefined ?
+ options.initialSize : ol.INITIAL_ATLAS_SIZE;
+
+ /**
+ * The maximum size in pixels of atlas images.
+ * @private
+ * @type {number}
+ */
+ this.maxSize_ = options.maxSize !== undefined ?
+ options.maxSize : ol.MAX_ATLAS_SIZE != -1 ?
+ ol.MAX_ATLAS_SIZE : ol.WEBGL_MAX_TEXTURE_SIZE !== undefined ?
+ ol.WEBGL_MAX_TEXTURE_SIZE : 2048;
+
+ /**
+ * The size in pixels between images.
+ * @private
+ * @type {number}
+ */
+ this.space_ = options.space !== undefined ? options.space : 1;
+
+ /**
+ * @private
+ * @type {Array.<ol.style.Atlas>}
+ */
+ this.atlases_ = [new ol.style.Atlas(this.currentSize_, this.space_)];
+
+ /**
+ * The size in pixels of the latest atlas image for hit-detection images.
+ * @private
+ * @type {number}
+ */
+ this.currentHitSize_ = this.currentSize_;
+
+ /**
+ * @private
+ * @type {Array.<ol.style.Atlas>}
+ */
+ this.hitAtlases_ = [new ol.style.Atlas(this.currentHitSize_, this.space_)];
+};
+
+
+/**
+ * @param {string} id The identifier of the entry to check.
+ * @return {?ol.AtlasManagerInfo} The position and atlas image for the
+ * entry, or `null` if the entry is not part of the atlas manager.
+ */
+ol.style.AtlasManager.prototype.getInfo = function(id) {
+ /** @type {?ol.AtlasInfo} */
+ var info = this.getInfo_(this.atlases_, id);
+
+ if (!info) {
+ return null;
+ }
+ var hitInfo = /** @type {ol.AtlasInfo} */ (this.getInfo_(this.hitAtlases_, id));
+
+ return this.mergeInfos_(info, hitInfo);
+};
+
+
+/**
+ * @private
+ * @param {Array.<ol.style.Atlas>} atlases The atlases to search.
+ * @param {string} id The identifier of the entry to check.
+ * @return {?ol.AtlasInfo} The position and atlas image for the entry,
+ * or `null` if the entry is not part of the atlases.
+ */
+ol.style.AtlasManager.prototype.getInfo_ = function(atlases, id) {
+ var atlas, info, i, ii;
+ for (i = 0, ii = atlases.length; i < ii; ++i) {
+ atlas = atlases[i];
+ info = atlas.get(id);
+ if (info) {
+ return info;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @private
+ * @param {ol.AtlasInfo} info The info for the real image.
+ * @param {ol.AtlasInfo} hitInfo The info for the hit-detection
+ * image.
+ * @return {?ol.AtlasManagerInfo} The position and atlas image for the
+ * entry, or `null` if the entry is not part of the atlases.
+ */
+ol.style.AtlasManager.prototype.mergeInfos_ = function(info, hitInfo) {
+ ol.DEBUG && console.assert(info.offsetX === hitInfo.offsetX,
+ 'in order to merge, offsetX of info and hitInfo must be equal');
+ ol.DEBUG && console.assert(info.offsetY === hitInfo.offsetY,
+ 'in order to merge, offsetY of info and hitInfo must be equal');
+ return /** @type {ol.AtlasManagerInfo} */ ({
+ offsetX: info.offsetX,
+ offsetY: info.offsetY,
+ image: info.image,
+ hitImage: hitInfo.image
+ });
+};
+
+
+/**
+ * Add an image to the atlas manager.
+ *
+ * If an entry for the given id already exists, the entry will
+ * be overridden (but the space on the atlas graphic will not be freed).
+ *
+ * If `renderHitCallback` is provided, the image (or the hit-detection version
+ * of the image) will be rendered into a separate hit-detection atlas image.
+ *
+ * @param {string} id The identifier of the entry to add.
+ * @param {number} width The width.
+ * @param {number} height The height.
+ * @param {function(CanvasRenderingContext2D, number, number)} renderCallback
+ * Called to render the new image onto an atlas image.
+ * @param {function(CanvasRenderingContext2D, number, number)=}
+ * opt_renderHitCallback Called to render a hit-detection image onto a hit
+ * detection atlas image.
+ * @param {Object=} opt_this Value to use as `this` when executing
+ * `renderCallback` and `renderHitCallback`.
+ * @return {?ol.AtlasManagerInfo} The position and atlas image for the
+ * entry, or `null` if the image is too big.
+ */
+ol.style.AtlasManager.prototype.add = function(id, width, height,
+ renderCallback, opt_renderHitCallback, opt_this) {
+ if (width + this.space_ > this.maxSize_ ||
+ height + this.space_ > this.maxSize_) {
+ return null;
+ }
+
+ /** @type {?ol.AtlasInfo} */
+ var info = this.add_(false,
+ id, width, height, renderCallback, opt_this);
+ if (!info) {
+ return null;
+ }
+
+ // even if no hit-detection entry is requested, we insert a fake entry into
+ // the hit-detection atlas, to make sure that the offset is the same for
+ // the original image and the hit-detection image.
+ var renderHitCallback = opt_renderHitCallback !== undefined ?
+ opt_renderHitCallback : ol.nullFunction;
+
+ var hitInfo = /** @type {ol.AtlasInfo} */ (this.add_(true,
+ id, width, height, renderHitCallback, opt_this));
+
+ return this.mergeInfos_(info, hitInfo);
+};
+
+
+/**
+ * @private
+ * @param {boolean} isHitAtlas If the hit-detection atlases are used.
+ * @param {string} id The identifier of the entry to add.
+ * @param {number} width The width.
+ * @param {number} height The height.
+ * @param {function(CanvasRenderingContext2D, number, number)} renderCallback
+ * Called to render the new image onto an atlas image.
+ * @param {Object=} opt_this Value to use as `this` when executing
+ * `renderCallback` and `renderHitCallback`.
+ * @return {?ol.AtlasInfo} The position and atlas image for the entry,
+ * or `null` if the image is too big.
+ */
+ol.style.AtlasManager.prototype.add_ = function(isHitAtlas, id, width, height,
+ renderCallback, opt_this) {
+ var atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_;
+ var atlas, info, i, ii;
+ for (i = 0, ii = atlases.length; i < ii; ++i) {
+ atlas = atlases[i];
+ info = atlas.add(id, width, height, renderCallback, opt_this);
+ if (info) {
+ return info;
+ } else if (!info && i === ii - 1) {
+ // the entry could not be added to one of the existing atlases,
+ // create a new atlas that is twice as big and try to add to this one.
+ var size;
+ if (isHitAtlas) {
+ size = Math.min(this.currentHitSize_ * 2, this.maxSize_);
+ this.currentHitSize_ = size;
+ } else {
+ size = Math.min(this.currentSize_ * 2, this.maxSize_);
+ this.currentSize_ = size;
+ }
+ atlas = new ol.style.Atlas(size, this.space_);
+ atlases.push(atlas);
+ // run the loop another time
+ ++ii;
+ }
+ }
+ ol.DEBUG && console.assert(false, 'Failed to add to atlasmanager');
+ return null;
+};
+
+// Copyright 2009 The Closure Library Authors.
+// All Rights Reserved.
+//
+// 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.
+//
+// This file has been auto-generated by GenJsDeps, please do not edit.
+
+goog.addDependency(
+ 'demos/editor/equationeditor.js', ['goog.demos.editor.EquationEditor'],
+ ['goog.ui.equation.EquationEditorDialog']);
+goog.addDependency(
+ 'demos/editor/helloworld.js', ['goog.demos.editor.HelloWorld'],
+ ['goog.dom', 'goog.dom.TagName', 'goog.editor.Plugin']);
+goog.addDependency(
+ 'demos/editor/helloworlddialog.js',
+ [
+ 'goog.demos.editor.HelloWorldDialog',
+ 'goog.demos.editor.HelloWorldDialog.OkEvent'
+ ],
+ [
+ 'goog.dom.TagName', 'goog.events.Event', 'goog.string',
+ 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.AbstractDialog.Builder',
+ 'goog.ui.editor.AbstractDialog.EventType'
+ ]);
+goog.addDependency(
+ 'demos/editor/helloworlddialogplugin.js',
+ [
+ 'goog.demos.editor.HelloWorldDialogPlugin',
+ 'goog.demos.editor.HelloWorldDialogPlugin.Command'
+ ],
+ [
+ 'goog.demos.editor.HelloWorldDialog', 'goog.dom.TagName',
+ 'goog.editor.plugins.AbstractDialogPlugin', 'goog.editor.range',
+ 'goog.functions', 'goog.ui.editor.AbstractDialog.EventType'
+ ]);
+
+/**
+ * @fileoverview Custom exports file.
+ * @suppress {checkVars,extraRequire}
+ */
+
+goog.require('ol');
+goog.require('ol.AssertionError');
+goog.require('ol.Attribution');
+goog.require('ol.Collection');
+goog.require('ol.DeviceOrientation');
+goog.require('ol.Feature');
+goog.require('ol.Geolocation');
+goog.require('ol.Graticule');
+goog.require('ol.Image');
+goog.require('ol.ImageTile');
+goog.require('ol.Kinetic');
+goog.require('ol.Map');
+goog.require('ol.MapBrowserEvent');
+goog.require('ol.MapEvent');
+goog.require('ol.Object');
+goog.require('ol.Observable');
+goog.require('ol.Overlay');
+goog.require('ol.Sphere');
+goog.require('ol.Tile');
+goog.require('ol.VectorTile');
+goog.require('ol.View');
+goog.require('ol.animation');
+goog.require('ol.color');
+goog.require('ol.colorlike');
+goog.require('ol.control');
+goog.require('ol.control.Attribution');
+goog.require('ol.control.Control');
+goog.require('ol.control.FullScreen');
+goog.require('ol.control.MousePosition');
+goog.require('ol.control.OverviewMap');
+goog.require('ol.control.Rotate');
+goog.require('ol.control.ScaleLine');
+goog.require('ol.control.Zoom');
+goog.require('ol.control.ZoomSlider');
+goog.require('ol.control.ZoomToExtent');
+goog.require('ol.coordinate');
+goog.require('ol.easing');
+goog.require('ol.events.Event');
+goog.require('ol.events.condition');
+goog.require('ol.extent');
+goog.require('ol.featureloader');
+goog.require('ol.format.EsriJSON');
+goog.require('ol.format.Feature');
+goog.require('ol.format.GML');
+goog.require('ol.format.GML2');
+goog.require('ol.format.GML3');
+goog.require('ol.format.GMLBase');
+goog.require('ol.format.GPX');
+goog.require('ol.format.GeoJSON');
+goog.require('ol.format.IGC');
+goog.require('ol.format.KML');
+goog.require('ol.format.MVT');
+goog.require('ol.format.OSMXML');
+goog.require('ol.format.Polyline');
+goog.require('ol.format.TopoJSON');
+goog.require('ol.format.WFS');
+goog.require('ol.format.WKT');
+goog.require('ol.format.WMSCapabilities');
+goog.require('ol.format.WMSGetFeatureInfo');
+goog.require('ol.format.WMTSCapabilities');
+goog.require('ol.format.filter');
+goog.require('ol.format.filter.And');
+goog.require('ol.format.filter.Bbox');
+goog.require('ol.format.filter.Comparison');
+goog.require('ol.format.filter.ComparisonBinary');
+goog.require('ol.format.filter.EqualTo');
+goog.require('ol.format.filter.Filter');
+goog.require('ol.format.filter.GreaterThan');
+goog.require('ol.format.filter.GreaterThanOrEqualTo');
+goog.require('ol.format.filter.Intersects');
+goog.require('ol.format.filter.IsBetween');
+goog.require('ol.format.filter.IsLike');
+goog.require('ol.format.filter.IsNull');
+goog.require('ol.format.filter.LessThan');
+goog.require('ol.format.filter.LessThanOrEqualTo');
+goog.require('ol.format.filter.Not');
+goog.require('ol.format.filter.NotEqualTo');
+goog.require('ol.format.filter.Or');
+goog.require('ol.format.filter.Spatial');
+goog.require('ol.format.filter.Within');
+goog.require('ol.geom.Circle');
+goog.require('ol.geom.Geometry');
+goog.require('ol.geom.GeometryCollection');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.LinearRing');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.MultiPoint');
+goog.require('ol.geom.MultiPolygon');
+goog.require('ol.geom.Point');
+goog.require('ol.geom.Polygon');
+goog.require('ol.geom.SimpleGeometry');
+goog.require('ol.has');
+goog.require('ol.interaction');
+goog.require('ol.interaction.DoubleClickZoom');
+goog.require('ol.interaction.DragAndDrop');
+goog.require('ol.interaction.DragBox');
+goog.require('ol.interaction.DragPan');
+goog.require('ol.interaction.DragRotate');
+goog.require('ol.interaction.DragRotateAndZoom');
+goog.require('ol.interaction.DragZoom');
+goog.require('ol.interaction.Draw');
+goog.require('ol.interaction.Extent');
+goog.require('ol.interaction.Interaction');
+goog.require('ol.interaction.KeyboardPan');
+goog.require('ol.interaction.KeyboardZoom');
+goog.require('ol.interaction.Modify');
+goog.require('ol.interaction.MouseWheelZoom');
+goog.require('ol.interaction.PinchRotate');
+goog.require('ol.interaction.PinchZoom');
+goog.require('ol.interaction.Pointer');
+goog.require('ol.interaction.Select');
+goog.require('ol.interaction.Snap');
+goog.require('ol.interaction.Translate');
+goog.require('ol.layer.Base');
+goog.require('ol.layer.Group');
+goog.require('ol.layer.Heatmap');
+goog.require('ol.layer.Image');
+goog.require('ol.layer.Layer');
+goog.require('ol.layer.Tile');
+goog.require('ol.layer.Vector');
+goog.require('ol.layer.VectorTile');
+goog.require('ol.loadingstrategy');
+goog.require('ol.proj');
+goog.require('ol.proj.Projection');
+goog.require('ol.proj.Units');
+goog.require('ol.proj.common');
+goog.require('ol.render');
+goog.require('ol.render.Event');
+goog.require('ol.render.Feature');
+goog.require('ol.render.VectorContext');
+goog.require('ol.render.canvas.Immediate');
+goog.require('ol.render.webgl.Immediate');
+goog.require('ol.size');
+goog.require('ol.source.BingMaps');
+goog.require('ol.source.CartoDB');
+goog.require('ol.source.Cluster');
+goog.require('ol.source.Image');
+goog.require('ol.source.ImageArcGISRest');
+goog.require('ol.source.ImageCanvas');
+goog.require('ol.source.ImageMapGuide');
+goog.require('ol.source.ImageStatic');
+goog.require('ol.source.ImageVector');
+goog.require('ol.source.ImageWMS');
+goog.require('ol.source.OSM');
+goog.require('ol.source.Raster');
+goog.require('ol.source.Source');
+goog.require('ol.source.Stamen');
+goog.require('ol.source.Tile');
+goog.require('ol.source.TileArcGISRest');
+goog.require('ol.source.TileDebug');
+goog.require('ol.source.TileImage');
+goog.require('ol.source.TileJSON');
+goog.require('ol.source.TileUTFGrid');
+goog.require('ol.source.TileWMS');
+goog.require('ol.source.UrlTile');
+goog.require('ol.source.Vector');
+goog.require('ol.source.VectorTile');
+goog.require('ol.source.WMTS');
+goog.require('ol.source.XYZ');
+goog.require('ol.source.Zoomify');
+goog.require('ol.style.AtlasManager');
+goog.require('ol.style.Circle');
+goog.require('ol.style.Fill');
+goog.require('ol.style.Icon');
+goog.require('ol.style.Image');
+goog.require('ol.style.RegularShape');
+goog.require('ol.style.Stroke');
+goog.require('ol.style.Style');
+goog.require('ol.style.Text');
+goog.require('ol.tilegrid');
+goog.require('ol.tilegrid.TileGrid');
+goog.require('ol.tilegrid.WMTS');
+goog.require('ol.webgl.Context');
+goog.require('ol.xml');
+
+
+
+goog.exportSymbol(
+ 'ol.animation.bounce',
+ ol.animation.bounce,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.animation.pan',
+ ol.animation.pan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.animation.rotate',
+ ol.animation.rotate,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.animation.zoom',
+ ol.animation.zoom,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.AssertionError.prototype,
+ 'code',
+ ol.AssertionError.prototype.code);
+
+goog.exportSymbol(
+ 'ol.Attribution',
+ ol.Attribution,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Attribution.prototype,
+ 'getHTML',
+ ol.Attribution.prototype.getHTML);
+
+goog.exportSymbol(
+ 'ol.Collection',
+ ol.Collection,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'clear',
+ ol.Collection.prototype.clear);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'extend',
+ ol.Collection.prototype.extend);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'forEach',
+ ol.Collection.prototype.forEach);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'getArray',
+ ol.Collection.prototype.getArray);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'item',
+ ol.Collection.prototype.item);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'getLength',
+ ol.Collection.prototype.getLength);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'insertAt',
+ ol.Collection.prototype.insertAt);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'pop',
+ ol.Collection.prototype.pop);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'push',
+ ol.Collection.prototype.push);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'remove',
+ ol.Collection.prototype.remove);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'removeAt',
+ ol.Collection.prototype.removeAt);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'setAt',
+ ol.Collection.prototype.setAt);
+
+goog.exportProperty(
+ ol.Collection.Event.prototype,
+ 'element',
+ ol.Collection.Event.prototype.element);
+
+goog.exportSymbol(
+ 'ol.color.asArray',
+ ol.color.asArray,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.color.asString',
+ ol.color.asString,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.colorlike.asColorLike',
+ ol.colorlike.asColorLike,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.coordinate.add',
+ ol.coordinate.add,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.coordinate.createStringXY',
+ ol.coordinate.createStringXY,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.coordinate.format',
+ ol.coordinate.format,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.coordinate.rotate',
+ ol.coordinate.rotate,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.coordinate.toStringHDMS',
+ ol.coordinate.toStringHDMS,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.coordinate.toStringXY',
+ ol.coordinate.toStringXY,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.DeviceOrientation',
+ ol.DeviceOrientation,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getAlpha',
+ ol.DeviceOrientation.prototype.getAlpha);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getBeta',
+ ol.DeviceOrientation.prototype.getBeta);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getGamma',
+ ol.DeviceOrientation.prototype.getGamma);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getHeading',
+ ol.DeviceOrientation.prototype.getHeading);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getTracking',
+ ol.DeviceOrientation.prototype.getTracking);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'setTracking',
+ ol.DeviceOrientation.prototype.setTracking);
+
+goog.exportSymbol(
+ 'ol.easing.easeIn',
+ ol.easing.easeIn,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.easing.easeOut',
+ ol.easing.easeOut,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.easing.inAndOut',
+ ol.easing.inAndOut,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.easing.linear',
+ ol.easing.linear,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.easing.upAndDown',
+ ol.easing.upAndDown,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.Feature',
+ ol.Feature,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'clone',
+ ol.Feature.prototype.clone);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getGeometry',
+ ol.Feature.prototype.getGeometry);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getId',
+ ol.Feature.prototype.getId);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getGeometryName',
+ ol.Feature.prototype.getGeometryName);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getStyle',
+ ol.Feature.prototype.getStyle);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getStyleFunction',
+ ol.Feature.prototype.getStyleFunction);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'setGeometry',
+ ol.Feature.prototype.setGeometry);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'setStyle',
+ ol.Feature.prototype.setStyle);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'setId',
+ ol.Feature.prototype.setId);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'setGeometryName',
+ ol.Feature.prototype.setGeometryName);
+
+goog.exportSymbol(
+ 'ol.featureloader.xhr',
+ ol.featureloader.xhr,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.Geolocation',
+ ol.Geolocation,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAccuracy',
+ ol.Geolocation.prototype.getAccuracy);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAccuracyGeometry',
+ ol.Geolocation.prototype.getAccuracyGeometry);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAltitude',
+ ol.Geolocation.prototype.getAltitude);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAltitudeAccuracy',
+ ol.Geolocation.prototype.getAltitudeAccuracy);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getHeading',
+ ol.Geolocation.prototype.getHeading);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getPosition',
+ ol.Geolocation.prototype.getPosition);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getProjection',
+ ol.Geolocation.prototype.getProjection);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getSpeed',
+ ol.Geolocation.prototype.getSpeed);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getTracking',
+ ol.Geolocation.prototype.getTracking);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getTrackingOptions',
+ ol.Geolocation.prototype.getTrackingOptions);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'setProjection',
+ ol.Geolocation.prototype.setProjection);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'setTracking',
+ ol.Geolocation.prototype.setTracking);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'setTrackingOptions',
+ ol.Geolocation.prototype.setTrackingOptions);
+
+goog.exportSymbol(
+ 'ol.Graticule',
+ ol.Graticule,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Graticule.prototype,
+ 'getMap',
+ ol.Graticule.prototype.getMap);
+
+goog.exportProperty(
+ ol.Graticule.prototype,
+ 'getMeridians',
+ ol.Graticule.prototype.getMeridians);
+
+goog.exportProperty(
+ ol.Graticule.prototype,
+ 'getParallels',
+ ol.Graticule.prototype.getParallels);
+
+goog.exportProperty(
+ ol.Graticule.prototype,
+ 'setMap',
+ ol.Graticule.prototype.setMap);
+
+goog.exportSymbol(
+ 'ol.has.DEVICE_PIXEL_RATIO',
+ ol.has.DEVICE_PIXEL_RATIO,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.has.CANVAS',
+ ol.has.CANVAS,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.has.DEVICE_ORIENTATION',
+ ol.has.DEVICE_ORIENTATION,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.has.GEOLOCATION',
+ ol.has.GEOLOCATION,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.has.TOUCH',
+ ol.has.TOUCH,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.has.WEBGL',
+ ol.has.WEBGL,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Image.prototype,
+ 'getImage',
+ ol.Image.prototype.getImage);
+
+goog.exportProperty(
+ ol.Image.prototype,
+ 'load',
+ ol.Image.prototype.load);
+
+goog.exportProperty(
+ ol.ImageTile.prototype,
+ 'getImage',
+ ol.ImageTile.prototype.getImage);
+
+goog.exportProperty(
+ ol.ImageTile.prototype,
+ 'load',
+ ol.ImageTile.prototype.load);
+
+goog.exportSymbol(
+ 'ol.inherits',
+ ol.inherits,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.Kinetic',
+ ol.Kinetic,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.loadingstrategy.all',
+ ol.loadingstrategy.all,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.loadingstrategy.bbox',
+ ol.loadingstrategy.bbox,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.loadingstrategy.tile',
+ ol.loadingstrategy.tile,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.Map',
+ ol.Map,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'addControl',
+ ol.Map.prototype.addControl);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'addInteraction',
+ ol.Map.prototype.addInteraction);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'addLayer',
+ ol.Map.prototype.addLayer);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'addOverlay',
+ ol.Map.prototype.addOverlay);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'beforeRender',
+ ol.Map.prototype.beforeRender);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'forEachFeatureAtPixel',
+ ol.Map.prototype.forEachFeatureAtPixel);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'forEachLayerAtPixel',
+ ol.Map.prototype.forEachLayerAtPixel);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'hasFeatureAtPixel',
+ ol.Map.prototype.hasFeatureAtPixel);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getEventCoordinate',
+ ol.Map.prototype.getEventCoordinate);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getEventPixel',
+ ol.Map.prototype.getEventPixel);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getTarget',
+ ol.Map.prototype.getTarget);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getTargetElement',
+ ol.Map.prototype.getTargetElement);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getCoordinateFromPixel',
+ ol.Map.prototype.getCoordinateFromPixel);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getControls',
+ ol.Map.prototype.getControls);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getOverlays',
+ ol.Map.prototype.getOverlays);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getOverlayById',
+ ol.Map.prototype.getOverlayById);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getInteractions',
+ ol.Map.prototype.getInteractions);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getLayerGroup',
+ ol.Map.prototype.getLayerGroup);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getLayers',
+ ol.Map.prototype.getLayers);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getPixelFromCoordinate',
+ ol.Map.prototype.getPixelFromCoordinate);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getSize',
+ ol.Map.prototype.getSize);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getView',
+ ol.Map.prototype.getView);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getViewport',
+ ol.Map.prototype.getViewport);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'renderSync',
+ ol.Map.prototype.renderSync);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'render',
+ ol.Map.prototype.render);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'removeControl',
+ ol.Map.prototype.removeControl);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'removeInteraction',
+ ol.Map.prototype.removeInteraction);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'removeLayer',
+ ol.Map.prototype.removeLayer);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'removeOverlay',
+ ol.Map.prototype.removeOverlay);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'setLayerGroup',
+ ol.Map.prototype.setLayerGroup);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'setSize',
+ ol.Map.prototype.setSize);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'setTarget',
+ ol.Map.prototype.setTarget);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'setView',
+ ol.Map.prototype.setView);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'updateSize',
+ ol.Map.prototype.updateSize);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'originalEvent',
+ ol.MapBrowserEvent.prototype.originalEvent);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'pixel',
+ ol.MapBrowserEvent.prototype.pixel);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'coordinate',
+ ol.MapBrowserEvent.prototype.coordinate);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'dragging',
+ ol.MapBrowserEvent.prototype.dragging);
+
+goog.exportProperty(
+ ol.MapEvent.prototype,
+ 'map',
+ ol.MapEvent.prototype.map);
+
+goog.exportProperty(
+ ol.MapEvent.prototype,
+ 'frameState',
+ ol.MapEvent.prototype.frameState);
+
+goog.exportSymbol(
+ 'ol.Object',
+ ol.Object,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'get',
+ ol.Object.prototype.get);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'getKeys',
+ ol.Object.prototype.getKeys);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'getProperties',
+ ol.Object.prototype.getProperties);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'set',
+ ol.Object.prototype.set);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'setProperties',
+ ol.Object.prototype.setProperties);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'unset',
+ ol.Object.prototype.unset);
+
+goog.exportProperty(
+ ol.Object.Event.prototype,
+ 'key',
+ ol.Object.Event.prototype.key);
+
+goog.exportProperty(
+ ol.Object.Event.prototype,
+ 'oldValue',
+ ol.Object.Event.prototype.oldValue);
+
+goog.exportSymbol(
+ 'ol.Observable',
+ ol.Observable,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.Observable.unByKey',
+ ol.Observable.unByKey,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'changed',
+ ol.Observable.prototype.changed);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'dispatchEvent',
+ ol.Observable.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'getRevision',
+ ol.Observable.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'on',
+ ol.Observable.prototype.on);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'once',
+ ol.Observable.prototype.once);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'un',
+ ol.Observable.prototype.un);
+
+goog.exportProperty(
+ ol.Observable.prototype,
+ 'unByKey',
+ ol.Observable.prototype.unByKey);
+
+goog.exportSymbol(
+ 'ol.Overlay',
+ ol.Overlay,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getElement',
+ ol.Overlay.prototype.getElement);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getId',
+ ol.Overlay.prototype.getId);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getMap',
+ ol.Overlay.prototype.getMap);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getOffset',
+ ol.Overlay.prototype.getOffset);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getPosition',
+ ol.Overlay.prototype.getPosition);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getPositioning',
+ ol.Overlay.prototype.getPositioning);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'setElement',
+ ol.Overlay.prototype.setElement);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'setMap',
+ ol.Overlay.prototype.setMap);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'setOffset',
+ ol.Overlay.prototype.setOffset);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'setPosition',
+ ol.Overlay.prototype.setPosition);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'setPositioning',
+ ol.Overlay.prototype.setPositioning);
+
+goog.exportSymbol(
+ 'ol.render.toContext',
+ ol.render.toContext,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.size.toSize',
+ ol.size.toSize,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Tile.prototype,
+ 'getTileCoord',
+ ol.Tile.prototype.getTileCoord);
+
+goog.exportProperty(
+ ol.Tile.prototype,
+ 'load',
+ ol.Tile.prototype.load);
+
+goog.exportProperty(
+ ol.VectorTile.prototype,
+ 'getFormat',
+ ol.VectorTile.prototype.getFormat);
+
+goog.exportProperty(
+ ol.VectorTile.prototype,
+ 'setFeatures',
+ ol.VectorTile.prototype.setFeatures);
+
+goog.exportProperty(
+ ol.VectorTile.prototype,
+ 'setProjection',
+ ol.VectorTile.prototype.setProjection);
+
+goog.exportProperty(
+ ol.VectorTile.prototype,
+ 'setLoader',
+ ol.VectorTile.prototype.setLoader);
+
+goog.exportSymbol(
+ 'ol.View',
+ ol.View,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'animate',
+ ol.View.prototype.animate);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'constrainCenter',
+ ol.View.prototype.constrainCenter);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'constrainResolution',
+ ol.View.prototype.constrainResolution);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'constrainRotation',
+ ol.View.prototype.constrainRotation);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getCenter',
+ ol.View.prototype.getCenter);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'calculateExtent',
+ ol.View.prototype.calculateExtent);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getMaxResolution',
+ ol.View.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getMinResolution',
+ ol.View.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getProjection',
+ ol.View.prototype.getProjection);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getResolution',
+ ol.View.prototype.getResolution);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getResolutions',
+ ol.View.prototype.getResolutions);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getRotation',
+ ol.View.prototype.getRotation);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getZoom',
+ ol.View.prototype.getZoom);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'fit',
+ ol.View.prototype.fit);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'centerOn',
+ ol.View.prototype.centerOn);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'rotate',
+ ol.View.prototype.rotate);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'setCenter',
+ ol.View.prototype.setCenter);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'setResolution',
+ ol.View.prototype.setResolution);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'setRotation',
+ ol.View.prototype.setRotation);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'setZoom',
+ ol.View.prototype.setZoom);
+
+goog.exportSymbol(
+ 'ol.xml.getAllTextContent',
+ ol.xml.getAllTextContent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.xml.parse',
+ ol.xml.parse,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.webgl.Context.prototype,
+ 'getGL',
+ ol.webgl.Context.prototype.getGL);
+
+goog.exportProperty(
+ ol.webgl.Context.prototype,
+ 'useProgram',
+ ol.webgl.Context.prototype.useProgram);
+
+goog.exportSymbol(
+ 'ol.tilegrid.createXYZ',
+ ol.tilegrid.createXYZ,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.tilegrid.TileGrid',
+ ol.tilegrid.TileGrid,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'forEachTileCoord',
+ ol.tilegrid.TileGrid.prototype.forEachTileCoord);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getMaxZoom',
+ ol.tilegrid.TileGrid.prototype.getMaxZoom);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getMinZoom',
+ ol.tilegrid.TileGrid.prototype.getMinZoom);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getOrigin',
+ ol.tilegrid.TileGrid.prototype.getOrigin);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getResolution',
+ ol.tilegrid.TileGrid.prototype.getResolution);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getResolutions',
+ ol.tilegrid.TileGrid.prototype.getResolutions);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getTileCoordExtent',
+ ol.tilegrid.TileGrid.prototype.getTileCoordExtent);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getTileCoordForCoordAndResolution',
+ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getTileCoordForCoordAndZ',
+ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getTileSize',
+ ol.tilegrid.TileGrid.prototype.getTileSize);
+
+goog.exportProperty(
+ ol.tilegrid.TileGrid.prototype,
+ 'getZForResolution',
+ ol.tilegrid.TileGrid.prototype.getZForResolution);
+
+goog.exportSymbol(
+ 'ol.tilegrid.WMTS',
+ ol.tilegrid.WMTS,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getMatrixIds',
+ ol.tilegrid.WMTS.prototype.getMatrixIds);
+
+goog.exportSymbol(
+ 'ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet',
+ ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.style.AtlasManager',
+ ol.style.AtlasManager,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.style.Circle',
+ ol.style.Circle,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'clone',
+ ol.style.Circle.prototype.clone);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'setRadius',
+ ol.style.Circle.prototype.setRadius);
+
+goog.exportSymbol(
+ 'ol.style.Fill',
+ ol.style.Fill,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Fill.prototype,
+ 'clone',
+ ol.style.Fill.prototype.clone);
+
+goog.exportProperty(
+ ol.style.Fill.prototype,
+ 'getColor',
+ ol.style.Fill.prototype.getColor);
+
+goog.exportProperty(
+ ol.style.Fill.prototype,
+ 'setColor',
+ ol.style.Fill.prototype.setColor);
+
+goog.exportSymbol(
+ 'ol.style.Icon',
+ ol.style.Icon,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'clone',
+ ol.style.Icon.prototype.clone);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getAnchor',
+ ol.style.Icon.prototype.getAnchor);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getColor',
+ ol.style.Icon.prototype.getColor);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getImage',
+ ol.style.Icon.prototype.getImage);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getOrigin',
+ ol.style.Icon.prototype.getOrigin);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getSrc',
+ ol.style.Icon.prototype.getSrc);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getSize',
+ ol.style.Icon.prototype.getSize);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'load',
+ ol.style.Icon.prototype.load);
+
+goog.exportSymbol(
+ 'ol.style.Image',
+ ol.style.Image,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'getOpacity',
+ ol.style.Image.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'getRotateWithView',
+ ol.style.Image.prototype.getRotateWithView);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'getRotation',
+ ol.style.Image.prototype.getRotation);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'getScale',
+ ol.style.Image.prototype.getScale);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'getSnapToPixel',
+ ol.style.Image.prototype.getSnapToPixel);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'setOpacity',
+ ol.style.Image.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'setRotation',
+ ol.style.Image.prototype.setRotation);
+
+goog.exportProperty(
+ ol.style.Image.prototype,
+ 'setScale',
+ ol.style.Image.prototype.setScale);
+
+goog.exportSymbol(
+ 'ol.style.RegularShape',
+ ol.style.RegularShape,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'clone',
+ ol.style.RegularShape.prototype.clone);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getAnchor',
+ ol.style.RegularShape.prototype.getAnchor);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getAngle',
+ ol.style.RegularShape.prototype.getAngle);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getFill',
+ ol.style.RegularShape.prototype.getFill);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getImage',
+ ol.style.RegularShape.prototype.getImage);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getOrigin',
+ ol.style.RegularShape.prototype.getOrigin);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getPoints',
+ ol.style.RegularShape.prototype.getPoints);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getRadius',
+ ol.style.RegularShape.prototype.getRadius);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getRadius2',
+ ol.style.RegularShape.prototype.getRadius2);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getSize',
+ ol.style.RegularShape.prototype.getSize);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getStroke',
+ ol.style.RegularShape.prototype.getStroke);
+
+goog.exportSymbol(
+ 'ol.style.Stroke',
+ ol.style.Stroke,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'clone',
+ ol.style.Stroke.prototype.clone);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'getColor',
+ ol.style.Stroke.prototype.getColor);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'getLineCap',
+ ol.style.Stroke.prototype.getLineCap);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'getLineDash',
+ ol.style.Stroke.prototype.getLineDash);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'getLineJoin',
+ ol.style.Stroke.prototype.getLineJoin);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'getMiterLimit',
+ ol.style.Stroke.prototype.getMiterLimit);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'getWidth',
+ ol.style.Stroke.prototype.getWidth);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'setColor',
+ ol.style.Stroke.prototype.setColor);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'setLineCap',
+ ol.style.Stroke.prototype.setLineCap);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'setLineDash',
+ ol.style.Stroke.prototype.setLineDash);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'setLineJoin',
+ ol.style.Stroke.prototype.setLineJoin);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'setMiterLimit',
+ ol.style.Stroke.prototype.setMiterLimit);
+
+goog.exportProperty(
+ ol.style.Stroke.prototype,
+ 'setWidth',
+ ol.style.Stroke.prototype.setWidth);
+
+goog.exportSymbol(
+ 'ol.style.Style',
+ ol.style.Style,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'clone',
+ ol.style.Style.prototype.clone);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getGeometry',
+ ol.style.Style.prototype.getGeometry);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getGeometryFunction',
+ ol.style.Style.prototype.getGeometryFunction);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getFill',
+ ol.style.Style.prototype.getFill);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'setFill',
+ ol.style.Style.prototype.setFill);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getImage',
+ ol.style.Style.prototype.getImage);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'setImage',
+ ol.style.Style.prototype.setImage);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getStroke',
+ ol.style.Style.prototype.getStroke);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'setStroke',
+ ol.style.Style.prototype.setStroke);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getText',
+ ol.style.Style.prototype.getText);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'setText',
+ ol.style.Style.prototype.setText);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'getZIndex',
+ ol.style.Style.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'setGeometry',
+ ol.style.Style.prototype.setGeometry);
+
+goog.exportProperty(
+ ol.style.Style.prototype,
+ 'setZIndex',
+ ol.style.Style.prototype.setZIndex);
+
+goog.exportSymbol(
+ 'ol.style.Text',
+ ol.style.Text,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'clone',
+ ol.style.Text.prototype.clone);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getFont',
+ ol.style.Text.prototype.getFont);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getOffsetX',
+ ol.style.Text.prototype.getOffsetX);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getOffsetY',
+ ol.style.Text.prototype.getOffsetY);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getFill',
+ ol.style.Text.prototype.getFill);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getRotateWithView',
+ ol.style.Text.prototype.getRotateWithView);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getRotation',
+ ol.style.Text.prototype.getRotation);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getScale',
+ ol.style.Text.prototype.getScale);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getStroke',
+ ol.style.Text.prototype.getStroke);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getText',
+ ol.style.Text.prototype.getText);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getTextAlign',
+ ol.style.Text.prototype.getTextAlign);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'getTextBaseline',
+ ol.style.Text.prototype.getTextBaseline);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setFont',
+ ol.style.Text.prototype.setFont);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setOffsetX',
+ ol.style.Text.prototype.setOffsetX);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setOffsetY',
+ ol.style.Text.prototype.setOffsetY);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setFill',
+ ol.style.Text.prototype.setFill);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setRotation',
+ ol.style.Text.prototype.setRotation);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setScale',
+ ol.style.Text.prototype.setScale);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setStroke',
+ ol.style.Text.prototype.setStroke);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setText',
+ ol.style.Text.prototype.setText);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setTextAlign',
+ ol.style.Text.prototype.setTextAlign);
+
+goog.exportProperty(
+ ol.style.Text.prototype,
+ 'setTextBaseline',
+ ol.style.Text.prototype.setTextBaseline);
+
+goog.exportSymbol(
+ 'ol.Sphere',
+ ol.Sphere,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Sphere.prototype,
+ 'geodesicArea',
+ ol.Sphere.prototype.geodesicArea);
+
+goog.exportProperty(
+ ol.Sphere.prototype,
+ 'haversineDistance',
+ ol.Sphere.prototype.haversineDistance);
+
+goog.exportSymbol(
+ 'ol.source.BingMaps',
+ ol.source.BingMaps,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.BingMaps.TOS_ATTRIBUTION',
+ ol.source.BingMaps.TOS_ATTRIBUTION,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getApiKey',
+ ol.source.BingMaps.prototype.getApiKey);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getImagerySet',
+ ol.source.BingMaps.prototype.getImagerySet);
+
+goog.exportSymbol(
+ 'ol.source.CartoDB',
+ ol.source.CartoDB,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getConfig',
+ ol.source.CartoDB.prototype.getConfig);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'updateConfig',
+ ol.source.CartoDB.prototype.updateConfig);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setConfig',
+ ol.source.CartoDB.prototype.setConfig);
+
+goog.exportSymbol(
+ 'ol.source.Cluster',
+ ol.source.Cluster,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getSource',
+ ol.source.Cluster.prototype.getSource);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'setDistance',
+ ol.source.Cluster.prototype.setDistance);
+
+goog.exportSymbol(
+ 'ol.source.Image',
+ ol.source.Image,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.Image.Event.prototype,
+ 'image',
+ ol.source.Image.Event.prototype.image);
+
+goog.exportSymbol(
+ 'ol.source.ImageArcGISRest',
+ ol.source.ImageArcGISRest,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getParams',
+ ol.source.ImageArcGISRest.prototype.getParams);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getImageLoadFunction',
+ ol.source.ImageArcGISRest.prototype.getImageLoadFunction);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getUrl',
+ ol.source.ImageArcGISRest.prototype.getUrl);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'setImageLoadFunction',
+ ol.source.ImageArcGISRest.prototype.setImageLoadFunction);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'setUrl',
+ ol.source.ImageArcGISRest.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'updateParams',
+ ol.source.ImageArcGISRest.prototype.updateParams);
+
+goog.exportSymbol(
+ 'ol.source.ImageCanvas',
+ ol.source.ImageCanvas,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.ImageMapGuide',
+ ol.source.ImageMapGuide,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getParams',
+ ol.source.ImageMapGuide.prototype.getParams);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getImageLoadFunction',
+ ol.source.ImageMapGuide.prototype.getImageLoadFunction);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'updateParams',
+ ol.source.ImageMapGuide.prototype.updateParams);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'setImageLoadFunction',
+ ol.source.ImageMapGuide.prototype.setImageLoadFunction);
+
+goog.exportSymbol(
+ 'ol.source.ImageStatic',
+ ol.source.ImageStatic,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.ImageVector',
+ ol.source.ImageVector,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getSource',
+ ol.source.ImageVector.prototype.getSource);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getStyle',
+ ol.source.ImageVector.prototype.getStyle);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getStyleFunction',
+ ol.source.ImageVector.prototype.getStyleFunction);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'setStyle',
+ ol.source.ImageVector.prototype.setStyle);
+
+goog.exportSymbol(
+ 'ol.source.ImageWMS',
+ ol.source.ImageWMS,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getGetFeatureInfoUrl',
+ ol.source.ImageWMS.prototype.getGetFeatureInfoUrl);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getParams',
+ ol.source.ImageWMS.prototype.getParams);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getImageLoadFunction',
+ ol.source.ImageWMS.prototype.getImageLoadFunction);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getUrl',
+ ol.source.ImageWMS.prototype.getUrl);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'setImageLoadFunction',
+ ol.source.ImageWMS.prototype.setImageLoadFunction);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'setUrl',
+ ol.source.ImageWMS.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'updateParams',
+ ol.source.ImageWMS.prototype.updateParams);
+
+goog.exportSymbol(
+ 'ol.source.OSM',
+ ol.source.OSM,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.OSM.ATTRIBUTION',
+ ol.source.OSM.ATTRIBUTION,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.Raster',
+ ol.source.Raster,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'setOperation',
+ ol.source.Raster.prototype.setOperation);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'extent',
+ ol.source.Raster.Event.prototype.extent);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'resolution',
+ ol.source.Raster.Event.prototype.resolution);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'data',
+ ol.source.Raster.Event.prototype.data);
+
+goog.exportSymbol(
+ 'ol.source.Source',
+ ol.source.Source,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getAttributions',
+ ol.source.Source.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getLogo',
+ ol.source.Source.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getProjection',
+ ol.source.Source.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getState',
+ ol.source.Source.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'refresh',
+ ol.source.Source.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'setAttributions',
+ ol.source.Source.prototype.setAttributions);
+
+goog.exportSymbol(
+ 'ol.source.Stamen',
+ ol.source.Stamen,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.Tile',
+ ol.source.Tile,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getTileGrid',
+ ol.source.Tile.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.Tile.Event.prototype,
+ 'tile',
+ ol.source.Tile.Event.prototype.tile);
+
+goog.exportSymbol(
+ 'ol.source.TileArcGISRest',
+ ol.source.TileArcGISRest,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getParams',
+ ol.source.TileArcGISRest.prototype.getParams);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'updateParams',
+ ol.source.TileArcGISRest.prototype.updateParams);
+
+goog.exportSymbol(
+ 'ol.source.TileDebug',
+ ol.source.TileDebug,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.TileImage',
+ ol.source.TileImage,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.TileImage.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setTileGridForProjection',
+ ol.source.TileImage.prototype.setTileGridForProjection);
+
+goog.exportSymbol(
+ 'ol.source.TileJSON',
+ ol.source.TileJSON,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getTileJSON',
+ ol.source.TileJSON.prototype.getTileJSON);
+
+goog.exportSymbol(
+ 'ol.source.TileUTFGrid',
+ ol.source.TileUTFGrid,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getTemplate',
+ ol.source.TileUTFGrid.prototype.getTemplate);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'forDataAtCoordinateAndResolution',
+ ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution);
+
+goog.exportSymbol(
+ 'ol.source.TileWMS',
+ ol.source.TileWMS,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getGetFeatureInfoUrl',
+ ol.source.TileWMS.prototype.getGetFeatureInfoUrl);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getParams',
+ ol.source.TileWMS.prototype.getParams);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'updateParams',
+ ol.source.TileWMS.prototype.updateParams);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getTileLoadFunction',
+ ol.source.UrlTile.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getTileUrlFunction',
+ ol.source.UrlTile.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getUrls',
+ ol.source.UrlTile.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'setTileLoadFunction',
+ ol.source.UrlTile.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'setTileUrlFunction',
+ ol.source.UrlTile.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'setUrl',
+ ol.source.UrlTile.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'setUrls',
+ ol.source.UrlTile.prototype.setUrls);
+
+goog.exportSymbol(
+ 'ol.source.Vector',
+ ol.source.Vector,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'addFeature',
+ ol.source.Vector.prototype.addFeature);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'addFeatures',
+ ol.source.Vector.prototype.addFeatures);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'clear',
+ ol.source.Vector.prototype.clear);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'forEachFeature',
+ ol.source.Vector.prototype.forEachFeature);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'forEachFeatureInExtent',
+ ol.source.Vector.prototype.forEachFeatureInExtent);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'forEachFeatureIntersectingExtent',
+ ol.source.Vector.prototype.forEachFeatureIntersectingExtent);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getFeaturesCollection',
+ ol.source.Vector.prototype.getFeaturesCollection);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getFeatures',
+ ol.source.Vector.prototype.getFeatures);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getFeaturesAtCoordinate',
+ ol.source.Vector.prototype.getFeaturesAtCoordinate);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getFeaturesInExtent',
+ ol.source.Vector.prototype.getFeaturesInExtent);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getClosestFeatureToCoordinate',
+ ol.source.Vector.prototype.getClosestFeatureToCoordinate);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getExtent',
+ ol.source.Vector.prototype.getExtent);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getFeatureById',
+ ol.source.Vector.prototype.getFeatureById);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getFormat',
+ ol.source.Vector.prototype.getFormat);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getUrl',
+ ol.source.Vector.prototype.getUrl);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'removeFeature',
+ ol.source.Vector.prototype.removeFeature);
+
+goog.exportProperty(
+ ol.source.Vector.Event.prototype,
+ 'feature',
+ ol.source.Vector.Event.prototype.feature);
+
+goog.exportSymbol(
+ 'ol.source.VectorTile',
+ ol.source.VectorTile,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.WMTS',
+ ol.source.WMTS,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getDimensions',
+ ol.source.WMTS.prototype.getDimensions);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getFormat',
+ ol.source.WMTS.prototype.getFormat);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getLayer',
+ ol.source.WMTS.prototype.getLayer);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getMatrixSet',
+ ol.source.WMTS.prototype.getMatrixSet);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getRequestEncoding',
+ ol.source.WMTS.prototype.getRequestEncoding);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getStyle',
+ ol.source.WMTS.prototype.getStyle);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getVersion',
+ ol.source.WMTS.prototype.getVersion);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'updateDimensions',
+ ol.source.WMTS.prototype.updateDimensions);
+
+goog.exportSymbol(
+ 'ol.source.WMTS.optionsFromCapabilities',
+ ol.source.WMTS.optionsFromCapabilities,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.XYZ',
+ ol.source.XYZ,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.source.Zoomify',
+ ol.source.Zoomify,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'vectorContext',
+ ol.render.Event.prototype.vectorContext);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'frameState',
+ ol.render.Event.prototype.frameState);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'context',
+ ol.render.Event.prototype.context);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'glContext',
+ ol.render.Event.prototype.glContext);
+
+goog.exportProperty(
+ ol.render.Feature.prototype,
+ 'get',
+ ol.render.Feature.prototype.get);
+
+goog.exportProperty(
+ ol.render.Feature.prototype,
+ 'getExtent',
+ ol.render.Feature.prototype.getExtent);
+
+goog.exportProperty(
+ ol.render.Feature.prototype,
+ 'getGeometry',
+ ol.render.Feature.prototype.getGeometry);
+
+goog.exportProperty(
+ ol.render.Feature.prototype,
+ 'getProperties',
+ ol.render.Feature.prototype.getProperties);
+
+goog.exportProperty(
+ ol.render.Feature.prototype,
+ 'getType',
+ ol.render.Feature.prototype.getType);
+
+goog.exportSymbol(
+ 'ol.render.VectorContext',
+ ol.render.VectorContext,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.render.webgl.Immediate.prototype,
+ 'setStyle',
+ ol.render.webgl.Immediate.prototype.setStyle);
+
+goog.exportProperty(
+ ol.render.webgl.Immediate.prototype,
+ 'drawGeometry',
+ ol.render.webgl.Immediate.prototype.drawGeometry);
+
+goog.exportProperty(
+ ol.render.webgl.Immediate.prototype,
+ 'drawFeature',
+ ol.render.webgl.Immediate.prototype.drawFeature);
+
+goog.exportProperty(
+ ol.render.canvas.Immediate.prototype,
+ 'drawCircle',
+ ol.render.canvas.Immediate.prototype.drawCircle);
+
+goog.exportProperty(
+ ol.render.canvas.Immediate.prototype,
+ 'setStyle',
+ ol.render.canvas.Immediate.prototype.setStyle);
+
+goog.exportProperty(
+ ol.render.canvas.Immediate.prototype,
+ 'drawGeometry',
+ ol.render.canvas.Immediate.prototype.drawGeometry);
+
+goog.exportProperty(
+ ol.render.canvas.Immediate.prototype,
+ 'drawFeature',
+ ol.render.canvas.Immediate.prototype.drawFeature);
+
+goog.exportSymbol(
+ 'ol.proj.common.add',
+ ol.proj.common.add,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.METERS_PER_UNIT',
+ ol.proj.METERS_PER_UNIT,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.setProj4',
+ ol.proj.setProj4,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.getPointResolution',
+ ol.proj.getPointResolution,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.addEquivalentProjections',
+ ol.proj.addEquivalentProjections,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.addProjection',
+ ol.proj.addProjection,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.addCoordinateTransforms',
+ ol.proj.addCoordinateTransforms,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.fromLonLat',
+ ol.proj.fromLonLat,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.toLonLat',
+ ol.proj.toLonLat,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.get',
+ ol.proj.get,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.equivalent',
+ ol.proj.equivalent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.getTransform',
+ ol.proj.getTransform,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.transform',
+ ol.proj.transform,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.transformExtent',
+ ol.proj.transformExtent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.proj.Projection',
+ ol.proj.Projection,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'getCode',
+ ol.proj.Projection.prototype.getCode);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'getExtent',
+ ol.proj.Projection.prototype.getExtent);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'getUnits',
+ ol.proj.Projection.prototype.getUnits);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'getMetersPerUnit',
+ ol.proj.Projection.prototype.getMetersPerUnit);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'getWorldExtent',
+ ol.proj.Projection.prototype.getWorldExtent);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'isGlobal',
+ ol.proj.Projection.prototype.isGlobal);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'setGlobal',
+ ol.proj.Projection.prototype.setGlobal);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'setExtent',
+ ol.proj.Projection.prototype.setExtent);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'setWorldExtent',
+ ol.proj.Projection.prototype.setWorldExtent);
+
+goog.exportProperty(
+ ol.proj.Projection.prototype,
+ 'setGetPointResolution',
+ ol.proj.Projection.prototype.setGetPointResolution);
+
+goog.exportSymbol(
+ 'ol.proj.Units.METERS_PER_UNIT',
+ ol.proj.Units.METERS_PER_UNIT,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.layer.Base',
+ ol.layer.Base,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getExtent',
+ ol.layer.Base.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getMaxResolution',
+ ol.layer.Base.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getMinResolution',
+ ol.layer.Base.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getOpacity',
+ ol.layer.Base.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getVisible',
+ ol.layer.Base.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getZIndex',
+ ol.layer.Base.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setExtent',
+ ol.layer.Base.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setMaxResolution',
+ ol.layer.Base.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setMinResolution',
+ ol.layer.Base.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setOpacity',
+ ol.layer.Base.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setVisible',
+ ol.layer.Base.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setZIndex',
+ ol.layer.Base.prototype.setZIndex);
+
+goog.exportSymbol(
+ 'ol.layer.Group',
+ ol.layer.Group,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getLayers',
+ ol.layer.Group.prototype.getLayers);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setLayers',
+ ol.layer.Group.prototype.setLayers);
+
+goog.exportSymbol(
+ 'ol.layer.Heatmap',
+ ol.layer.Heatmap,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getBlur',
+ ol.layer.Heatmap.prototype.getBlur);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getGradient',
+ ol.layer.Heatmap.prototype.getGradient);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getRadius',
+ ol.layer.Heatmap.prototype.getRadius);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setBlur',
+ ol.layer.Heatmap.prototype.setBlur);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setGradient',
+ ol.layer.Heatmap.prototype.setGradient);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setRadius',
+ ol.layer.Heatmap.prototype.setRadius);
+
+goog.exportSymbol(
+ 'ol.layer.Image',
+ ol.layer.Image,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getSource',
+ ol.layer.Image.prototype.getSource);
+
+goog.exportSymbol(
+ 'ol.layer.Layer',
+ ol.layer.Layer,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getSource',
+ ol.layer.Layer.prototype.getSource);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setMap',
+ ol.layer.Layer.prototype.setMap);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setSource',
+ ol.layer.Layer.prototype.setSource);
+
+goog.exportSymbol(
+ 'ol.layer.Tile',
+ ol.layer.Tile,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getPreload',
+ ol.layer.Tile.prototype.getPreload);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getSource',
+ ol.layer.Tile.prototype.getSource);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setPreload',
+ ol.layer.Tile.prototype.setPreload);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getUseInterimTilesOnError',
+ ol.layer.Tile.prototype.getUseInterimTilesOnError);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setUseInterimTilesOnError',
+ ol.layer.Tile.prototype.setUseInterimTilesOnError);
+
+goog.exportSymbol(
+ 'ol.layer.Vector',
+ ol.layer.Vector,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getSource',
+ ol.layer.Vector.prototype.getSource);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getStyle',
+ ol.layer.Vector.prototype.getStyle);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getStyleFunction',
+ ol.layer.Vector.prototype.getStyleFunction);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setStyle',
+ ol.layer.Vector.prototype.setStyle);
+
+goog.exportSymbol(
+ 'ol.layer.VectorTile',
+ ol.layer.VectorTile,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getPreload',
+ ol.layer.VectorTile.prototype.getPreload);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getUseInterimTilesOnError',
+ ol.layer.VectorTile.prototype.getUseInterimTilesOnError);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setPreload',
+ ol.layer.VectorTile.prototype.setPreload);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setUseInterimTilesOnError',
+ ol.layer.VectorTile.prototype.setUseInterimTilesOnError);
+
+goog.exportSymbol(
+ 'ol.interaction.DoubleClickZoom',
+ ol.interaction.DoubleClickZoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.DoubleClickZoom.handleEvent',
+ ol.interaction.DoubleClickZoom.handleEvent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.DragAndDrop',
+ ol.interaction.DragAndDrop,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.DragAndDrop.handleEvent',
+ ol.interaction.DragAndDrop.handleEvent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'features',
+ ol.interaction.DragAndDrop.Event.prototype.features);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'file',
+ ol.interaction.DragAndDrop.Event.prototype.file);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'projection',
+ ol.interaction.DragAndDrop.Event.prototype.projection);
+
+goog.exportSymbol(
+ 'ol.interaction.DragBox',
+ ol.interaction.DragBox,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'getGeometry',
+ ol.interaction.DragBox.prototype.getGeometry);
+
+goog.exportProperty(
+ ol.interaction.DragBox.Event.prototype,
+ 'coordinate',
+ ol.interaction.DragBox.Event.prototype.coordinate);
+
+goog.exportProperty(
+ ol.interaction.DragBox.Event.prototype,
+ 'mapBrowserEvent',
+ ol.interaction.DragBox.Event.prototype.mapBrowserEvent);
+
+goog.exportSymbol(
+ 'ol.interaction.DragPan',
+ ol.interaction.DragPan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.DragRotate',
+ ol.interaction.DragRotate,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.DragRotateAndZoom',
+ ol.interaction.DragRotateAndZoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.DragZoom',
+ ol.interaction.DragZoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Draw',
+ ol.interaction.Draw,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Draw.handleEvent',
+ ol.interaction.Draw.handleEvent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'removeLastPoint',
+ ol.interaction.Draw.prototype.removeLastPoint);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'finishDrawing',
+ ol.interaction.Draw.prototype.finishDrawing);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'extend',
+ ol.interaction.Draw.prototype.extend);
+
+goog.exportSymbol(
+ 'ol.interaction.Draw.createRegularPolygon',
+ ol.interaction.Draw.createRegularPolygon,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Draw.createBox',
+ ol.interaction.Draw.createBox,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Draw.Event.prototype,
+ 'feature',
+ ol.interaction.Draw.Event.prototype.feature);
+
+goog.exportSymbol(
+ 'ol.interaction.Extent',
+ ol.interaction.Extent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'getExtent',
+ ol.interaction.Extent.prototype.getExtent);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'setExtent',
+ ol.interaction.Extent.prototype.setExtent);
+
+goog.exportProperty(
+ ol.interaction.Extent.Event.prototype,
+ 'extent_',
+ ol.interaction.Extent.Event.prototype.extent_);
+
+goog.exportSymbol(
+ 'ol.interaction.defaults',
+ ol.interaction.defaults,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Interaction',
+ ol.interaction.Interaction,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'getActive',
+ ol.interaction.Interaction.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'getMap',
+ ol.interaction.Interaction.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'setActive',
+ ol.interaction.Interaction.prototype.setActive);
+
+goog.exportSymbol(
+ 'ol.interaction.KeyboardPan',
+ ol.interaction.KeyboardPan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.KeyboardPan.handleEvent',
+ ol.interaction.KeyboardPan.handleEvent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.KeyboardZoom',
+ ol.interaction.KeyboardZoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.KeyboardZoom.handleEvent',
+ ol.interaction.KeyboardZoom.handleEvent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Modify',
+ ol.interaction.Modify,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Modify.handleEvent',
+ ol.interaction.Modify.handleEvent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'removePoint',
+ ol.interaction.Modify.prototype.removePoint);
+
+goog.exportProperty(
+ ol.interaction.Modify.Event.prototype,
+ 'features',
+ ol.interaction.Modify.Event.prototype.features);
+
+goog.exportProperty(
+ ol.interaction.Modify.Event.prototype,
+ 'mapBrowserEvent',
+ ol.interaction.Modify.Event.prototype.mapBrowserEvent);
+
+goog.exportSymbol(
+ 'ol.interaction.MouseWheelZoom',
+ ol.interaction.MouseWheelZoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.MouseWheelZoom.handleEvent',
+ ol.interaction.MouseWheelZoom.handleEvent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'setMouseAnchor',
+ ol.interaction.MouseWheelZoom.prototype.setMouseAnchor);
+
+goog.exportSymbol(
+ 'ol.interaction.PinchRotate',
+ ol.interaction.PinchRotate,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.PinchZoom',
+ ol.interaction.PinchZoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Pointer',
+ ol.interaction.Pointer,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Pointer.handleEvent',
+ ol.interaction.Pointer.handleEvent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.interaction.Select',
+ ol.interaction.Select,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getFeatures',
+ ol.interaction.Select.prototype.getFeatures);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getHitTolerance',
+ ol.interaction.Select.prototype.getHitTolerance);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getLayer',
+ ol.interaction.Select.prototype.getLayer);
+
+goog.exportSymbol(
+ 'ol.interaction.Select.handleEvent',
+ ol.interaction.Select.handleEvent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'setHitTolerance',
+ ol.interaction.Select.prototype.setHitTolerance);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'setMap',
+ ol.interaction.Select.prototype.setMap);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'selected',
+ ol.interaction.Select.Event.prototype.selected);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'deselected',
+ ol.interaction.Select.Event.prototype.deselected);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'mapBrowserEvent',
+ ol.interaction.Select.Event.prototype.mapBrowserEvent);
+
+goog.exportSymbol(
+ 'ol.interaction.Snap',
+ ol.interaction.Snap,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'addFeature',
+ ol.interaction.Snap.prototype.addFeature);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'removeFeature',
+ ol.interaction.Snap.prototype.removeFeature);
+
+goog.exportSymbol(
+ 'ol.interaction.Translate',
+ ol.interaction.Translate,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'getHitTolerance',
+ ol.interaction.Translate.prototype.getHitTolerance);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'setHitTolerance',
+ ol.interaction.Translate.prototype.setHitTolerance);
+
+goog.exportProperty(
+ ol.interaction.Translate.Event.prototype,
+ 'features',
+ ol.interaction.Translate.Event.prototype.features);
+
+goog.exportProperty(
+ ol.interaction.Translate.Event.prototype,
+ 'coordinate',
+ ol.interaction.Translate.Event.prototype.coordinate);
+
+goog.exportSymbol(
+ 'ol.geom.Circle',
+ ol.geom.Circle,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'clone',
+ ol.geom.Circle.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getCenter',
+ ol.geom.Circle.prototype.getCenter);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getRadius',
+ ol.geom.Circle.prototype.getRadius);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getType',
+ ol.geom.Circle.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'intersectsExtent',
+ ol.geom.Circle.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'setCenter',
+ ol.geom.Circle.prototype.setCenter);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'setCenterAndRadius',
+ ol.geom.Circle.prototype.setCenterAndRadius);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'setRadius',
+ ol.geom.Circle.prototype.setRadius);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'transform',
+ ol.geom.Circle.prototype.transform);
+
+goog.exportSymbol(
+ 'ol.geom.Geometry',
+ ol.geom.Geometry,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'getClosestPoint',
+ ol.geom.Geometry.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'intersectsCoordinate',
+ ol.geom.Geometry.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'getExtent',
+ ol.geom.Geometry.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'rotate',
+ ol.geom.Geometry.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'scale',
+ ol.geom.Geometry.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'simplify',
+ ol.geom.Geometry.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'transform',
+ ol.geom.Geometry.prototype.transform);
+
+goog.exportSymbol(
+ 'ol.geom.GeometryCollection',
+ ol.geom.GeometryCollection,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'clone',
+ ol.geom.GeometryCollection.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getGeometries',
+ ol.geom.GeometryCollection.prototype.getGeometries);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getType',
+ ol.geom.GeometryCollection.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'intersectsExtent',
+ ol.geom.GeometryCollection.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'setGeometries',
+ ol.geom.GeometryCollection.prototype.setGeometries);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'applyTransform',
+ ol.geom.GeometryCollection.prototype.applyTransform);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'translate',
+ ol.geom.GeometryCollection.prototype.translate);
+
+goog.exportSymbol(
+ 'ol.geom.LinearRing',
+ ol.geom.LinearRing,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'clone',
+ ol.geom.LinearRing.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getArea',
+ ol.geom.LinearRing.prototype.getArea);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getCoordinates',
+ ol.geom.LinearRing.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getType',
+ ol.geom.LinearRing.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'setCoordinates',
+ ol.geom.LinearRing.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.LineString',
+ ol.geom.LineString,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'appendCoordinate',
+ ol.geom.LineString.prototype.appendCoordinate);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'clone',
+ ol.geom.LineString.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'forEachSegment',
+ ol.geom.LineString.prototype.forEachSegment);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getCoordinateAtM',
+ ol.geom.LineString.prototype.getCoordinateAtM);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getCoordinates',
+ ol.geom.LineString.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getCoordinateAt',
+ ol.geom.LineString.prototype.getCoordinateAt);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getLength',
+ ol.geom.LineString.prototype.getLength);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getType',
+ ol.geom.LineString.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'intersectsExtent',
+ ol.geom.LineString.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'setCoordinates',
+ ol.geom.LineString.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.MultiLineString',
+ ol.geom.MultiLineString,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'appendLineString',
+ ol.geom.MultiLineString.prototype.appendLineString);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'clone',
+ ol.geom.MultiLineString.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getCoordinateAtM',
+ ol.geom.MultiLineString.prototype.getCoordinateAtM);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getCoordinates',
+ ol.geom.MultiLineString.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getLineString',
+ ol.geom.MultiLineString.prototype.getLineString);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getLineStrings',
+ ol.geom.MultiLineString.prototype.getLineStrings);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getType',
+ ol.geom.MultiLineString.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'intersectsExtent',
+ ol.geom.MultiLineString.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'setCoordinates',
+ ol.geom.MultiLineString.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.MultiPoint',
+ ol.geom.MultiPoint,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'appendPoint',
+ ol.geom.MultiPoint.prototype.appendPoint);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'clone',
+ ol.geom.MultiPoint.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getCoordinates',
+ ol.geom.MultiPoint.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getPoint',
+ ol.geom.MultiPoint.prototype.getPoint);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getPoints',
+ ol.geom.MultiPoint.prototype.getPoints);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getType',
+ ol.geom.MultiPoint.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'intersectsExtent',
+ ol.geom.MultiPoint.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'setCoordinates',
+ ol.geom.MultiPoint.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.MultiPolygon',
+ ol.geom.MultiPolygon,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'appendPolygon',
+ ol.geom.MultiPolygon.prototype.appendPolygon);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'clone',
+ ol.geom.MultiPolygon.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getArea',
+ ol.geom.MultiPolygon.prototype.getArea);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getCoordinates',
+ ol.geom.MultiPolygon.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getInteriorPoints',
+ ol.geom.MultiPolygon.prototype.getInteriorPoints);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getPolygon',
+ ol.geom.MultiPolygon.prototype.getPolygon);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getPolygons',
+ ol.geom.MultiPolygon.prototype.getPolygons);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getType',
+ ol.geom.MultiPolygon.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'intersectsExtent',
+ ol.geom.MultiPolygon.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'setCoordinates',
+ ol.geom.MultiPolygon.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.Point',
+ ol.geom.Point,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'clone',
+ ol.geom.Point.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getCoordinates',
+ ol.geom.Point.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getType',
+ ol.geom.Point.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'intersectsExtent',
+ ol.geom.Point.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'setCoordinates',
+ ol.geom.Point.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.Polygon',
+ ol.geom.Polygon,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'appendLinearRing',
+ ol.geom.Polygon.prototype.appendLinearRing);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'clone',
+ ol.geom.Polygon.prototype.clone);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getArea',
+ ol.geom.Polygon.prototype.getArea);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getCoordinates',
+ ol.geom.Polygon.prototype.getCoordinates);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getInteriorPoint',
+ ol.geom.Polygon.prototype.getInteriorPoint);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getLinearRingCount',
+ ol.geom.Polygon.prototype.getLinearRingCount);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getLinearRing',
+ ol.geom.Polygon.prototype.getLinearRing);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getLinearRings',
+ ol.geom.Polygon.prototype.getLinearRings);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getType',
+ ol.geom.Polygon.prototype.getType);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'intersectsExtent',
+ ol.geom.Polygon.prototype.intersectsExtent);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'setCoordinates',
+ ol.geom.Polygon.prototype.setCoordinates);
+
+goog.exportSymbol(
+ 'ol.geom.Polygon.circular',
+ ol.geom.Polygon.circular,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.geom.Polygon.fromExtent',
+ ol.geom.Polygon.fromExtent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.geom.Polygon.fromCircle',
+ ol.geom.Polygon.fromCircle,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.geom.SimpleGeometry',
+ ol.geom.SimpleGeometry,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getFirstCoordinate',
+ ol.geom.SimpleGeometry.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getLastCoordinate',
+ ol.geom.SimpleGeometry.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getLayout',
+ ol.geom.SimpleGeometry.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'applyTransform',
+ ol.geom.SimpleGeometry.prototype.applyTransform);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'translate',
+ ol.geom.SimpleGeometry.prototype.translate);
+
+goog.exportSymbol(
+ 'ol.format.EsriJSON',
+ ol.format.EsriJSON,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'readFeature',
+ ol.format.EsriJSON.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'readFeatures',
+ ol.format.EsriJSON.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'readGeometry',
+ ol.format.EsriJSON.prototype.readGeometry);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'readProjection',
+ ol.format.EsriJSON.prototype.readProjection);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'writeGeometry',
+ ol.format.EsriJSON.prototype.writeGeometry);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'writeGeometryObject',
+ ol.format.EsriJSON.prototype.writeGeometryObject);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'writeFeature',
+ ol.format.EsriJSON.prototype.writeFeature);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'writeFeatureObject',
+ ol.format.EsriJSON.prototype.writeFeatureObject);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'writeFeatures',
+ ol.format.EsriJSON.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.EsriJSON.prototype,
+ 'writeFeaturesObject',
+ ol.format.EsriJSON.prototype.writeFeaturesObject);
+
+goog.exportSymbol(
+ 'ol.format.Feature',
+ ol.format.Feature,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.GeoJSON',
+ ol.format.GeoJSON,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'readFeature',
+ ol.format.GeoJSON.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'readFeatures',
+ ol.format.GeoJSON.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'readGeometry',
+ ol.format.GeoJSON.prototype.readGeometry);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'readProjection',
+ ol.format.GeoJSON.prototype.readProjection);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'writeFeature',
+ ol.format.GeoJSON.prototype.writeFeature);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'writeFeatureObject',
+ ol.format.GeoJSON.prototype.writeFeatureObject);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'writeFeatures',
+ ol.format.GeoJSON.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'writeFeaturesObject',
+ ol.format.GeoJSON.prototype.writeFeaturesObject);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'writeGeometry',
+ ol.format.GeoJSON.prototype.writeGeometry);
+
+goog.exportProperty(
+ ol.format.GeoJSON.prototype,
+ 'writeGeometryObject',
+ ol.format.GeoJSON.prototype.writeGeometryObject);
+
+goog.exportSymbol(
+ 'ol.format.GML',
+ ol.format.GML,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.GML.prototype,
+ 'writeFeatures',
+ ol.format.GML.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.GML.prototype,
+ 'writeFeaturesNode',
+ ol.format.GML.prototype.writeFeaturesNode);
+
+goog.exportSymbol(
+ 'ol.format.GML2',
+ ol.format.GML2,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.GML3',
+ ol.format.GML3,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.GML3.prototype,
+ 'writeGeometryNode',
+ ol.format.GML3.prototype.writeGeometryNode);
+
+goog.exportProperty(
+ ol.format.GML3.prototype,
+ 'writeFeatures',
+ ol.format.GML3.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.GML3.prototype,
+ 'writeFeaturesNode',
+ ol.format.GML3.prototype.writeFeaturesNode);
+
+goog.exportProperty(
+ ol.format.GMLBase.prototype,
+ 'readFeatures',
+ ol.format.GMLBase.prototype.readFeatures);
+
+goog.exportSymbol(
+ 'ol.format.GPX',
+ ol.format.GPX,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.GPX.prototype,
+ 'readFeature',
+ ol.format.GPX.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.GPX.prototype,
+ 'readFeatures',
+ ol.format.GPX.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.GPX.prototype,
+ 'readProjection',
+ ol.format.GPX.prototype.readProjection);
+
+goog.exportProperty(
+ ol.format.GPX.prototype,
+ 'writeFeatures',
+ ol.format.GPX.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.GPX.prototype,
+ 'writeFeaturesNode',
+ ol.format.GPX.prototype.writeFeaturesNode);
+
+goog.exportSymbol(
+ 'ol.format.IGC',
+ ol.format.IGC,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.IGC.prototype,
+ 'readFeature',
+ ol.format.IGC.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.IGC.prototype,
+ 'readFeatures',
+ ol.format.IGC.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.IGC.prototype,
+ 'readProjection',
+ ol.format.IGC.prototype.readProjection);
+
+goog.exportSymbol(
+ 'ol.format.KML',
+ ol.format.KML,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readFeature',
+ ol.format.KML.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readFeatures',
+ ol.format.KML.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readName',
+ ol.format.KML.prototype.readName);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readNetworkLinks',
+ ol.format.KML.prototype.readNetworkLinks);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readRegion',
+ ol.format.KML.prototype.readRegion);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readRegionFromNode',
+ ol.format.KML.prototype.readRegionFromNode);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'readProjection',
+ ol.format.KML.prototype.readProjection);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'writeFeatures',
+ ol.format.KML.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.KML.prototype,
+ 'writeFeaturesNode',
+ ol.format.KML.prototype.writeFeaturesNode);
+
+goog.exportSymbol(
+ 'ol.format.MVT',
+ ol.format.MVT,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.MVT.prototype,
+ 'readFeatures',
+ ol.format.MVT.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.MVT.prototype,
+ 'readProjection',
+ ol.format.MVT.prototype.readProjection);
+
+goog.exportProperty(
+ ol.format.MVT.prototype,
+ 'setLayers',
+ ol.format.MVT.prototype.setLayers);
+
+goog.exportSymbol(
+ 'ol.format.OSMXML',
+ ol.format.OSMXML,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.OSMXML.prototype,
+ 'readFeatures',
+ ol.format.OSMXML.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.OSMXML.prototype,
+ 'readProjection',
+ ol.format.OSMXML.prototype.readProjection);
+
+goog.exportSymbol(
+ 'ol.format.Polyline',
+ ol.format.Polyline,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.Polyline.encodeDeltas',
+ ol.format.Polyline.encodeDeltas,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.Polyline.decodeDeltas',
+ ol.format.Polyline.decodeDeltas,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.Polyline.encodeFloats',
+ ol.format.Polyline.encodeFloats,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.Polyline.decodeFloats',
+ ol.format.Polyline.decodeFloats,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.Polyline.prototype,
+ 'readFeature',
+ ol.format.Polyline.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.Polyline.prototype,
+ 'readFeatures',
+ ol.format.Polyline.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.Polyline.prototype,
+ 'readGeometry',
+ ol.format.Polyline.prototype.readGeometry);
+
+goog.exportProperty(
+ ol.format.Polyline.prototype,
+ 'readProjection',
+ ol.format.Polyline.prototype.readProjection);
+
+goog.exportProperty(
+ ol.format.Polyline.prototype,
+ 'writeGeometry',
+ ol.format.Polyline.prototype.writeGeometry);
+
+goog.exportSymbol(
+ 'ol.format.TopoJSON',
+ ol.format.TopoJSON,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.TopoJSON.prototype,
+ 'readFeatures',
+ ol.format.TopoJSON.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.TopoJSON.prototype,
+ 'readProjection',
+ ol.format.TopoJSON.prototype.readProjection);
+
+goog.exportSymbol(
+ 'ol.format.WFS',
+ ol.format.WFS,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.WFS.prototype,
+ 'readFeatures',
+ ol.format.WFS.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.WFS.prototype,
+ 'readTransactionResponse',
+ ol.format.WFS.prototype.readTransactionResponse);
+
+goog.exportProperty(
+ ol.format.WFS.prototype,
+ 'readFeatureCollectionMetadata',
+ ol.format.WFS.prototype.readFeatureCollectionMetadata);
+
+goog.exportProperty(
+ ol.format.WFS.prototype,
+ 'writeGetFeature',
+ ol.format.WFS.prototype.writeGetFeature);
+
+goog.exportProperty(
+ ol.format.WFS.prototype,
+ 'writeTransaction',
+ ol.format.WFS.prototype.writeTransaction);
+
+goog.exportProperty(
+ ol.format.WFS.prototype,
+ 'readProjection',
+ ol.format.WFS.prototype.readProjection);
+
+goog.exportSymbol(
+ 'ol.format.WKT',
+ ol.format.WKT,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.WKT.prototype,
+ 'readFeature',
+ ol.format.WKT.prototype.readFeature);
+
+goog.exportProperty(
+ ol.format.WKT.prototype,
+ 'readFeatures',
+ ol.format.WKT.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.WKT.prototype,
+ 'readGeometry',
+ ol.format.WKT.prototype.readGeometry);
+
+goog.exportProperty(
+ ol.format.WKT.prototype,
+ 'writeFeature',
+ ol.format.WKT.prototype.writeFeature);
+
+goog.exportProperty(
+ ol.format.WKT.prototype,
+ 'writeFeatures',
+ ol.format.WKT.prototype.writeFeatures);
+
+goog.exportProperty(
+ ol.format.WKT.prototype,
+ 'writeGeometry',
+ ol.format.WKT.prototype.writeGeometry);
+
+goog.exportSymbol(
+ 'ol.format.WMSCapabilities',
+ ol.format.WMSCapabilities,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.WMSCapabilities.prototype,
+ 'read',
+ ol.format.WMSCapabilities.prototype.read);
+
+goog.exportSymbol(
+ 'ol.format.WMSGetFeatureInfo',
+ ol.format.WMSGetFeatureInfo,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.WMSGetFeatureInfo.prototype,
+ 'readFeatures',
+ ol.format.WMSGetFeatureInfo.prototype.readFeatures);
+
+goog.exportSymbol(
+ 'ol.format.WMTSCapabilities',
+ ol.format.WMTSCapabilities,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.format.WMTSCapabilities.prototype,
+ 'read',
+ ol.format.WMTSCapabilities.prototype.read);
+
+goog.exportSymbol(
+ 'ol.format.filter.And',
+ ol.format.filter.And,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Bbox',
+ ol.format.filter.Bbox,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Comparison',
+ ol.format.filter.Comparison,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.ComparisonBinary',
+ ol.format.filter.ComparisonBinary,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.EqualTo',
+ ol.format.filter.EqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Filter',
+ ol.format.filter.Filter,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.GreaterThan',
+ ol.format.filter.GreaterThan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.GreaterThanOrEqualTo',
+ ol.format.filter.GreaterThanOrEqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.and',
+ ol.format.filter.and,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.or',
+ ol.format.filter.or,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.not',
+ ol.format.filter.not,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.bbox',
+ ol.format.filter.bbox,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.intersects',
+ ol.format.filter.intersects,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.within',
+ ol.format.filter.within,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.equalTo',
+ ol.format.filter.equalTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.notEqualTo',
+ ol.format.filter.notEqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.lessThan',
+ ol.format.filter.lessThan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.lessThanOrEqualTo',
+ ol.format.filter.lessThanOrEqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.greaterThan',
+ ol.format.filter.greaterThan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.greaterThanOrEqualTo',
+ ol.format.filter.greaterThanOrEqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.isNull',
+ ol.format.filter.isNull,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.between',
+ ol.format.filter.between,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.like',
+ ol.format.filter.like,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Intersects',
+ ol.format.filter.Intersects,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.IsBetween',
+ ol.format.filter.IsBetween,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.IsLike',
+ ol.format.filter.IsLike,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.IsNull',
+ ol.format.filter.IsNull,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.LessThan',
+ ol.format.filter.LessThan,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.LessThanOrEqualTo',
+ ol.format.filter.LessThanOrEqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Not',
+ ol.format.filter.Not,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.NotEqualTo',
+ ol.format.filter.NotEqualTo,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Or',
+ ol.format.filter.Or,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Spatial',
+ ol.format.filter.Spatial,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.format.filter.Within',
+ ol.format.filter.Within,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.boundingExtent',
+ ol.extent.boundingExtent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.buffer',
+ ol.extent.buffer,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.containsCoordinate',
+ ol.extent.containsCoordinate,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.containsExtent',
+ ol.extent.containsExtent,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.containsXY',
+ ol.extent.containsXY,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.createEmpty',
+ ol.extent.createEmpty,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.equals',
+ ol.extent.equals,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.extend',
+ ol.extent.extend,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getBottomLeft',
+ ol.extent.getBottomLeft,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getBottomRight',
+ ol.extent.getBottomRight,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getCenter',
+ ol.extent.getCenter,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getHeight',
+ ol.extent.getHeight,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getIntersection',
+ ol.extent.getIntersection,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getSize',
+ ol.extent.getSize,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getTopLeft',
+ ol.extent.getTopLeft,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getTopRight',
+ ol.extent.getTopRight,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.getWidth',
+ ol.extent.getWidth,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.intersects',
+ ol.extent.intersects,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.isEmpty',
+ ol.extent.isEmpty,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.extent.applyTransform',
+ ol.extent.applyTransform,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.altKeyOnly',
+ ol.events.condition.altKeyOnly,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.altShiftKeysOnly',
+ ol.events.condition.altShiftKeysOnly,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.always',
+ ol.events.condition.always,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.click',
+ ol.events.condition.click,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.never',
+ ol.events.condition.never,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.pointerMove',
+ ol.events.condition.pointerMove,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.singleClick',
+ ol.events.condition.singleClick,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.doubleClick',
+ ol.events.condition.doubleClick,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.noModifierKeys',
+ ol.events.condition.noModifierKeys,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.platformModifierKeyOnly',
+ ol.events.condition.platformModifierKeyOnly,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.shiftKeyOnly',
+ ol.events.condition.shiftKeyOnly,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.targetNotEditable',
+ ol.events.condition.targetNotEditable,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.mouseOnly',
+ ol.events.condition.mouseOnly,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.events.condition.primaryAction',
+ ol.events.condition.primaryAction,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.events.Event.prototype,
+ 'type',
+ ol.events.Event.prototype.type);
+
+goog.exportProperty(
+ ol.events.Event.prototype,
+ 'target',
+ ol.events.Event.prototype.target);
+
+goog.exportProperty(
+ ol.events.Event.prototype,
+ 'preventDefault',
+ ol.events.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.events.Event.prototype,
+ 'stopPropagation',
+ ol.events.Event.prototype.stopPropagation);
+
+goog.exportSymbol(
+ 'ol.control.Attribution',
+ ol.control.Attribution,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.Attribution.render',
+ ol.control.Attribution.render,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'getCollapsible',
+ ol.control.Attribution.prototype.getCollapsible);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'setCollapsible',
+ ol.control.Attribution.prototype.setCollapsible);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'setCollapsed',
+ ol.control.Attribution.prototype.setCollapsed);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'getCollapsed',
+ ol.control.Attribution.prototype.getCollapsed);
+
+goog.exportSymbol(
+ 'ol.control.Control',
+ ol.control.Control,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'getMap',
+ ol.control.Control.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'setMap',
+ ol.control.Control.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'setTarget',
+ ol.control.Control.prototype.setTarget);
+
+goog.exportSymbol(
+ 'ol.control.FullScreen',
+ ol.control.FullScreen,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.defaults',
+ ol.control.defaults,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.MousePosition',
+ ol.control.MousePosition,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.MousePosition.render',
+ ol.control.MousePosition.render,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'getCoordinateFormat',
+ ol.control.MousePosition.prototype.getCoordinateFormat);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'getProjection',
+ ol.control.MousePosition.prototype.getProjection);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'setCoordinateFormat',
+ ol.control.MousePosition.prototype.setCoordinateFormat);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'setProjection',
+ ol.control.MousePosition.prototype.setProjection);
+
+goog.exportSymbol(
+ 'ol.control.OverviewMap',
+ ol.control.OverviewMap,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.OverviewMap.render',
+ ol.control.OverviewMap.render,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getCollapsible',
+ ol.control.OverviewMap.prototype.getCollapsible);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'setCollapsible',
+ ol.control.OverviewMap.prototype.setCollapsible);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'setCollapsed',
+ ol.control.OverviewMap.prototype.setCollapsed);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getCollapsed',
+ ol.control.OverviewMap.prototype.getCollapsed);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getOverviewMap',
+ ol.control.OverviewMap.prototype.getOverviewMap);
+
+goog.exportSymbol(
+ 'ol.control.Rotate',
+ ol.control.Rotate,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.Rotate.render',
+ ol.control.Rotate.render,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.ScaleLine',
+ ol.control.ScaleLine,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'getUnits',
+ ol.control.ScaleLine.prototype.getUnits);
+
+goog.exportSymbol(
+ 'ol.control.ScaleLine.render',
+ ol.control.ScaleLine.render,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'setUnits',
+ ol.control.ScaleLine.prototype.setUnits);
+
+goog.exportSymbol(
+ 'ol.control.Zoom',
+ ol.control.Zoom,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.ZoomSlider',
+ ol.control.ZoomSlider,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.ZoomSlider.render',
+ ol.control.ZoomSlider.render,
+ OPENLAYERS);
+
+goog.exportSymbol(
+ 'ol.control.ZoomToExtent',
+ ol.control.ZoomToExtent,
+ OPENLAYERS);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'changed',
+ ol.Object.prototype.changed);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'dispatchEvent',
+ ol.Object.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'getRevision',
+ ol.Object.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'on',
+ ol.Object.prototype.on);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'once',
+ ol.Object.prototype.once);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'un',
+ ol.Object.prototype.un);
+
+goog.exportProperty(
+ ol.Object.prototype,
+ 'unByKey',
+ ol.Object.prototype.unByKey);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'get',
+ ol.Collection.prototype.get);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'getKeys',
+ ol.Collection.prototype.getKeys);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'getProperties',
+ ol.Collection.prototype.getProperties);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'set',
+ ol.Collection.prototype.set);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'setProperties',
+ ol.Collection.prototype.setProperties);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'unset',
+ ol.Collection.prototype.unset);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'changed',
+ ol.Collection.prototype.changed);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'dispatchEvent',
+ ol.Collection.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'getRevision',
+ ol.Collection.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'on',
+ ol.Collection.prototype.on);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'once',
+ ol.Collection.prototype.once);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'un',
+ ol.Collection.prototype.un);
+
+goog.exportProperty(
+ ol.Collection.prototype,
+ 'unByKey',
+ ol.Collection.prototype.unByKey);
+
+goog.exportProperty(
+ ol.Collection.Event.prototype,
+ 'type',
+ ol.Collection.Event.prototype.type);
+
+goog.exportProperty(
+ ol.Collection.Event.prototype,
+ 'target',
+ ol.Collection.Event.prototype.target);
+
+goog.exportProperty(
+ ol.Collection.Event.prototype,
+ 'preventDefault',
+ ol.Collection.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.Collection.Event.prototype,
+ 'stopPropagation',
+ ol.Collection.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'get',
+ ol.DeviceOrientation.prototype.get);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getKeys',
+ ol.DeviceOrientation.prototype.getKeys);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getProperties',
+ ol.DeviceOrientation.prototype.getProperties);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'set',
+ ol.DeviceOrientation.prototype.set);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'setProperties',
+ ol.DeviceOrientation.prototype.setProperties);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'unset',
+ ol.DeviceOrientation.prototype.unset);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'changed',
+ ol.DeviceOrientation.prototype.changed);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'dispatchEvent',
+ ol.DeviceOrientation.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'getRevision',
+ ol.DeviceOrientation.prototype.getRevision);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'on',
+ ol.DeviceOrientation.prototype.on);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'once',
+ ol.DeviceOrientation.prototype.once);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'un',
+ ol.DeviceOrientation.prototype.un);
+
+goog.exportProperty(
+ ol.DeviceOrientation.prototype,
+ 'unByKey',
+ ol.DeviceOrientation.prototype.unByKey);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'get',
+ ol.Feature.prototype.get);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getKeys',
+ ol.Feature.prototype.getKeys);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getProperties',
+ ol.Feature.prototype.getProperties);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'set',
+ ol.Feature.prototype.set);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'setProperties',
+ ol.Feature.prototype.setProperties);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'unset',
+ ol.Feature.prototype.unset);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'changed',
+ ol.Feature.prototype.changed);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'dispatchEvent',
+ ol.Feature.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'getRevision',
+ ol.Feature.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'on',
+ ol.Feature.prototype.on);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'once',
+ ol.Feature.prototype.once);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'un',
+ ol.Feature.prototype.un);
+
+goog.exportProperty(
+ ol.Feature.prototype,
+ 'unByKey',
+ ol.Feature.prototype.unByKey);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'get',
+ ol.Geolocation.prototype.get);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getKeys',
+ ol.Geolocation.prototype.getKeys);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getProperties',
+ ol.Geolocation.prototype.getProperties);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'set',
+ ol.Geolocation.prototype.set);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'setProperties',
+ ol.Geolocation.prototype.setProperties);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'unset',
+ ol.Geolocation.prototype.unset);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'changed',
+ ol.Geolocation.prototype.changed);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'dispatchEvent',
+ ol.Geolocation.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getRevision',
+ ol.Geolocation.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'on',
+ ol.Geolocation.prototype.on);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'once',
+ ol.Geolocation.prototype.once);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'un',
+ ol.Geolocation.prototype.un);
+
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'unByKey',
+ ol.Geolocation.prototype.unByKey);
+
+goog.exportProperty(
+ ol.ImageTile.prototype,
+ 'getTileCoord',
+ ol.ImageTile.prototype.getTileCoord);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'get',
+ ol.Map.prototype.get);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getKeys',
+ ol.Map.prototype.getKeys);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getProperties',
+ ol.Map.prototype.getProperties);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'set',
+ ol.Map.prototype.set);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'setProperties',
+ ol.Map.prototype.setProperties);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'unset',
+ ol.Map.prototype.unset);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'changed',
+ ol.Map.prototype.changed);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'dispatchEvent',
+ ol.Map.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'getRevision',
+ ol.Map.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'on',
+ ol.Map.prototype.on);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'once',
+ ol.Map.prototype.once);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'un',
+ ol.Map.prototype.un);
+
+goog.exportProperty(
+ ol.Map.prototype,
+ 'unByKey',
+ ol.Map.prototype.unByKey);
+
+goog.exportProperty(
+ ol.MapEvent.prototype,
+ 'type',
+ ol.MapEvent.prototype.type);
+
+goog.exportProperty(
+ ol.MapEvent.prototype,
+ 'target',
+ ol.MapEvent.prototype.target);
+
+goog.exportProperty(
+ ol.MapEvent.prototype,
+ 'preventDefault',
+ ol.MapEvent.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.MapEvent.prototype,
+ 'stopPropagation',
+ ol.MapEvent.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'map',
+ ol.MapBrowserEvent.prototype.map);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'frameState',
+ ol.MapBrowserEvent.prototype.frameState);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'type',
+ ol.MapBrowserEvent.prototype.type);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'target',
+ ol.MapBrowserEvent.prototype.target);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'preventDefault',
+ ol.MapBrowserEvent.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.MapBrowserEvent.prototype,
+ 'stopPropagation',
+ ol.MapBrowserEvent.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'originalEvent',
+ ol.MapBrowserPointerEvent.prototype.originalEvent);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'pixel',
+ ol.MapBrowserPointerEvent.prototype.pixel);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'coordinate',
+ ol.MapBrowserPointerEvent.prototype.coordinate);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'dragging',
+ ol.MapBrowserPointerEvent.prototype.dragging);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'preventDefault',
+ ol.MapBrowserPointerEvent.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'stopPropagation',
+ ol.MapBrowserPointerEvent.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'map',
+ ol.MapBrowserPointerEvent.prototype.map);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'frameState',
+ ol.MapBrowserPointerEvent.prototype.frameState);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'type',
+ ol.MapBrowserPointerEvent.prototype.type);
+
+goog.exportProperty(
+ ol.MapBrowserPointerEvent.prototype,
+ 'target',
+ ol.MapBrowserPointerEvent.prototype.target);
+
+goog.exportProperty(
+ ol.Object.Event.prototype,
+ 'type',
+ ol.Object.Event.prototype.type);
+
+goog.exportProperty(
+ ol.Object.Event.prototype,
+ 'target',
+ ol.Object.Event.prototype.target);
+
+goog.exportProperty(
+ ol.Object.Event.prototype,
+ 'preventDefault',
+ ol.Object.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.Object.Event.prototype,
+ 'stopPropagation',
+ ol.Object.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'get',
+ ol.Overlay.prototype.get);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getKeys',
+ ol.Overlay.prototype.getKeys);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getProperties',
+ ol.Overlay.prototype.getProperties);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'set',
+ ol.Overlay.prototype.set);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'setProperties',
+ ol.Overlay.prototype.setProperties);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'unset',
+ ol.Overlay.prototype.unset);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'changed',
+ ol.Overlay.prototype.changed);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'dispatchEvent',
+ ol.Overlay.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'getRevision',
+ ol.Overlay.prototype.getRevision);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'on',
+ ol.Overlay.prototype.on);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'once',
+ ol.Overlay.prototype.once);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'un',
+ ol.Overlay.prototype.un);
+
+goog.exportProperty(
+ ol.Overlay.prototype,
+ 'unByKey',
+ ol.Overlay.prototype.unByKey);
+
+goog.exportProperty(
+ ol.VectorTile.prototype,
+ 'getTileCoord',
+ ol.VectorTile.prototype.getTileCoord);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'get',
+ ol.View.prototype.get);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getKeys',
+ ol.View.prototype.getKeys);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getProperties',
+ ol.View.prototype.getProperties);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'set',
+ ol.View.prototype.set);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'setProperties',
+ ol.View.prototype.setProperties);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'unset',
+ ol.View.prototype.unset);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'changed',
+ ol.View.prototype.changed);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'dispatchEvent',
+ ol.View.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'getRevision',
+ ol.View.prototype.getRevision);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'on',
+ ol.View.prototype.on);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'once',
+ ol.View.prototype.once);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'un',
+ ol.View.prototype.un);
+
+goog.exportProperty(
+ ol.View.prototype,
+ 'unByKey',
+ ol.View.prototype.unByKey);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'forEachTileCoord',
+ ol.tilegrid.WMTS.prototype.forEachTileCoord);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getMaxZoom',
+ ol.tilegrid.WMTS.prototype.getMaxZoom);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getMinZoom',
+ ol.tilegrid.WMTS.prototype.getMinZoom);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getOrigin',
+ ol.tilegrid.WMTS.prototype.getOrigin);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getResolution',
+ ol.tilegrid.WMTS.prototype.getResolution);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getResolutions',
+ ol.tilegrid.WMTS.prototype.getResolutions);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getTileCoordExtent',
+ ol.tilegrid.WMTS.prototype.getTileCoordExtent);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getTileCoordForCoordAndResolution',
+ ol.tilegrid.WMTS.prototype.getTileCoordForCoordAndResolution);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getTileCoordForCoordAndZ',
+ ol.tilegrid.WMTS.prototype.getTileCoordForCoordAndZ);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getTileSize',
+ ol.tilegrid.WMTS.prototype.getTileSize);
+
+goog.exportProperty(
+ ol.tilegrid.WMTS.prototype,
+ 'getZForResolution',
+ ol.tilegrid.WMTS.prototype.getZForResolution);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getOpacity',
+ ol.style.RegularShape.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getRotateWithView',
+ ol.style.RegularShape.prototype.getRotateWithView);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getRotation',
+ ol.style.RegularShape.prototype.getRotation);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getScale',
+ ol.style.RegularShape.prototype.getScale);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'getSnapToPixel',
+ ol.style.RegularShape.prototype.getSnapToPixel);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'setOpacity',
+ ol.style.RegularShape.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'setRotation',
+ ol.style.RegularShape.prototype.setRotation);
+
+goog.exportProperty(
+ ol.style.RegularShape.prototype,
+ 'setScale',
+ ol.style.RegularShape.prototype.setScale);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getAngle',
+ ol.style.Circle.prototype.getAngle);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getFill',
+ ol.style.Circle.prototype.getFill);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getPoints',
+ ol.style.Circle.prototype.getPoints);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getRadius',
+ ol.style.Circle.prototype.getRadius);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getRadius2',
+ ol.style.Circle.prototype.getRadius2);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getStroke',
+ ol.style.Circle.prototype.getStroke);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getOpacity',
+ ol.style.Circle.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getRotateWithView',
+ ol.style.Circle.prototype.getRotateWithView);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getRotation',
+ ol.style.Circle.prototype.getRotation);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getScale',
+ ol.style.Circle.prototype.getScale);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'getSnapToPixel',
+ ol.style.Circle.prototype.getSnapToPixel);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'setOpacity',
+ ol.style.Circle.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'setRotation',
+ ol.style.Circle.prototype.setRotation);
+
+goog.exportProperty(
+ ol.style.Circle.prototype,
+ 'setScale',
+ ol.style.Circle.prototype.setScale);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getOpacity',
+ ol.style.Icon.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getRotateWithView',
+ ol.style.Icon.prototype.getRotateWithView);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getRotation',
+ ol.style.Icon.prototype.getRotation);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getScale',
+ ol.style.Icon.prototype.getScale);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'getSnapToPixel',
+ ol.style.Icon.prototype.getSnapToPixel);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'setOpacity',
+ ol.style.Icon.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'setRotation',
+ ol.style.Icon.prototype.setRotation);
+
+goog.exportProperty(
+ ol.style.Icon.prototype,
+ 'setScale',
+ ol.style.Icon.prototype.setScale);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'get',
+ ol.source.Source.prototype.get);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getKeys',
+ ol.source.Source.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getProperties',
+ ol.source.Source.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'set',
+ ol.source.Source.prototype.set);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'setProperties',
+ ol.source.Source.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'unset',
+ ol.source.Source.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'changed',
+ ol.source.Source.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'dispatchEvent',
+ ol.source.Source.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'getRevision',
+ ol.source.Source.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'on',
+ ol.source.Source.prototype.on);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'once',
+ ol.source.Source.prototype.once);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'un',
+ ol.source.Source.prototype.un);
+
+goog.exportProperty(
+ ol.source.Source.prototype,
+ 'unByKey',
+ ol.source.Source.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getAttributions',
+ ol.source.Tile.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getLogo',
+ ol.source.Tile.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getProjection',
+ ol.source.Tile.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getState',
+ ol.source.Tile.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'refresh',
+ ol.source.Tile.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'setAttributions',
+ ol.source.Tile.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'get',
+ ol.source.Tile.prototype.get);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getKeys',
+ ol.source.Tile.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getProperties',
+ ol.source.Tile.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'set',
+ ol.source.Tile.prototype.set);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'setProperties',
+ ol.source.Tile.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'unset',
+ ol.source.Tile.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'changed',
+ ol.source.Tile.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'dispatchEvent',
+ ol.source.Tile.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'getRevision',
+ ol.source.Tile.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'on',
+ ol.source.Tile.prototype.on);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'once',
+ ol.source.Tile.prototype.once);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'un',
+ ol.source.Tile.prototype.un);
+
+goog.exportProperty(
+ ol.source.Tile.prototype,
+ 'unByKey',
+ ol.source.Tile.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getTileGrid',
+ ol.source.UrlTile.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'refresh',
+ ol.source.UrlTile.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getAttributions',
+ ol.source.UrlTile.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getLogo',
+ ol.source.UrlTile.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getProjection',
+ ol.source.UrlTile.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getState',
+ ol.source.UrlTile.prototype.getState);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'setAttributions',
+ ol.source.UrlTile.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'get',
+ ol.source.UrlTile.prototype.get);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getKeys',
+ ol.source.UrlTile.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getProperties',
+ ol.source.UrlTile.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'set',
+ ol.source.UrlTile.prototype.set);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'setProperties',
+ ol.source.UrlTile.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'unset',
+ ol.source.UrlTile.prototype.unset);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'changed',
+ ol.source.UrlTile.prototype.changed);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'dispatchEvent',
+ ol.source.UrlTile.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'getRevision',
+ ol.source.UrlTile.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'on',
+ ol.source.UrlTile.prototype.on);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'once',
+ ol.source.UrlTile.prototype.once);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'un',
+ ol.source.UrlTile.prototype.un);
+
+goog.exportProperty(
+ ol.source.UrlTile.prototype,
+ 'unByKey',
+ ol.source.UrlTile.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getTileLoadFunction',
+ ol.source.TileImage.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getTileUrlFunction',
+ ol.source.TileImage.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getUrls',
+ ol.source.TileImage.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setTileLoadFunction',
+ ol.source.TileImage.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setTileUrlFunction',
+ ol.source.TileImage.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setUrl',
+ ol.source.TileImage.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setUrls',
+ ol.source.TileImage.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getTileGrid',
+ ol.source.TileImage.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'refresh',
+ ol.source.TileImage.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getAttributions',
+ ol.source.TileImage.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getLogo',
+ ol.source.TileImage.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getProjection',
+ ol.source.TileImage.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getState',
+ ol.source.TileImage.prototype.getState);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setAttributions',
+ ol.source.TileImage.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'get',
+ ol.source.TileImage.prototype.get);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getKeys',
+ ol.source.TileImage.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getProperties',
+ ol.source.TileImage.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'set',
+ ol.source.TileImage.prototype.set);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'setProperties',
+ ol.source.TileImage.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'unset',
+ ol.source.TileImage.prototype.unset);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'changed',
+ ol.source.TileImage.prototype.changed);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'dispatchEvent',
+ ol.source.TileImage.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'getRevision',
+ ol.source.TileImage.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'on',
+ ol.source.TileImage.prototype.on);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'once',
+ ol.source.TileImage.prototype.once);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'un',
+ ol.source.TileImage.prototype.un);
+
+goog.exportProperty(
+ ol.source.TileImage.prototype,
+ 'unByKey',
+ ol.source.TileImage.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.BingMaps.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setTileGridForProjection',
+ ol.source.BingMaps.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getTileLoadFunction',
+ ol.source.BingMaps.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getTileUrlFunction',
+ ol.source.BingMaps.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getUrls',
+ ol.source.BingMaps.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setTileLoadFunction',
+ ol.source.BingMaps.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setTileUrlFunction',
+ ol.source.BingMaps.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setUrl',
+ ol.source.BingMaps.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setUrls',
+ ol.source.BingMaps.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getTileGrid',
+ ol.source.BingMaps.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'refresh',
+ ol.source.BingMaps.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getAttributions',
+ ol.source.BingMaps.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getLogo',
+ ol.source.BingMaps.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getProjection',
+ ol.source.BingMaps.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getState',
+ ol.source.BingMaps.prototype.getState);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setAttributions',
+ ol.source.BingMaps.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'get',
+ ol.source.BingMaps.prototype.get);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getKeys',
+ ol.source.BingMaps.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getProperties',
+ ol.source.BingMaps.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'set',
+ ol.source.BingMaps.prototype.set);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'setProperties',
+ ol.source.BingMaps.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'unset',
+ ol.source.BingMaps.prototype.unset);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'changed',
+ ol.source.BingMaps.prototype.changed);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'dispatchEvent',
+ ol.source.BingMaps.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'getRevision',
+ ol.source.BingMaps.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'on',
+ ol.source.BingMaps.prototype.on);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'once',
+ ol.source.BingMaps.prototype.once);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'un',
+ ol.source.BingMaps.prototype.un);
+
+goog.exportProperty(
+ ol.source.BingMaps.prototype,
+ 'unByKey',
+ ol.source.BingMaps.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.XYZ.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setTileGridForProjection',
+ ol.source.XYZ.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getTileLoadFunction',
+ ol.source.XYZ.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getTileUrlFunction',
+ ol.source.XYZ.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getUrls',
+ ol.source.XYZ.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setTileLoadFunction',
+ ol.source.XYZ.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setTileUrlFunction',
+ ol.source.XYZ.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setUrl',
+ ol.source.XYZ.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setUrls',
+ ol.source.XYZ.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getTileGrid',
+ ol.source.XYZ.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'refresh',
+ ol.source.XYZ.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getAttributions',
+ ol.source.XYZ.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getLogo',
+ ol.source.XYZ.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getProjection',
+ ol.source.XYZ.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getState',
+ ol.source.XYZ.prototype.getState);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setAttributions',
+ ol.source.XYZ.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'get',
+ ol.source.XYZ.prototype.get);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getKeys',
+ ol.source.XYZ.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getProperties',
+ ol.source.XYZ.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'set',
+ ol.source.XYZ.prototype.set);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'setProperties',
+ ol.source.XYZ.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'unset',
+ ol.source.XYZ.prototype.unset);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'changed',
+ ol.source.XYZ.prototype.changed);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'dispatchEvent',
+ ol.source.XYZ.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'getRevision',
+ ol.source.XYZ.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'on',
+ ol.source.XYZ.prototype.on);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'once',
+ ol.source.XYZ.prototype.once);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'un',
+ ol.source.XYZ.prototype.un);
+
+goog.exportProperty(
+ ol.source.XYZ.prototype,
+ 'unByKey',
+ ol.source.XYZ.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.CartoDB.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setTileGridForProjection',
+ ol.source.CartoDB.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getTileLoadFunction',
+ ol.source.CartoDB.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getTileUrlFunction',
+ ol.source.CartoDB.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getUrls',
+ ol.source.CartoDB.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setTileLoadFunction',
+ ol.source.CartoDB.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setTileUrlFunction',
+ ol.source.CartoDB.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setUrl',
+ ol.source.CartoDB.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setUrls',
+ ol.source.CartoDB.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getTileGrid',
+ ol.source.CartoDB.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'refresh',
+ ol.source.CartoDB.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getAttributions',
+ ol.source.CartoDB.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getLogo',
+ ol.source.CartoDB.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getProjection',
+ ol.source.CartoDB.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getState',
+ ol.source.CartoDB.prototype.getState);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setAttributions',
+ ol.source.CartoDB.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'get',
+ ol.source.CartoDB.prototype.get);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getKeys',
+ ol.source.CartoDB.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getProperties',
+ ol.source.CartoDB.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'set',
+ ol.source.CartoDB.prototype.set);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'setProperties',
+ ol.source.CartoDB.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'unset',
+ ol.source.CartoDB.prototype.unset);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'changed',
+ ol.source.CartoDB.prototype.changed);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'dispatchEvent',
+ ol.source.CartoDB.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'getRevision',
+ ol.source.CartoDB.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'on',
+ ol.source.CartoDB.prototype.on);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'once',
+ ol.source.CartoDB.prototype.once);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'un',
+ ol.source.CartoDB.prototype.un);
+
+goog.exportProperty(
+ ol.source.CartoDB.prototype,
+ 'unByKey',
+ ol.source.CartoDB.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getAttributions',
+ ol.source.Vector.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getLogo',
+ ol.source.Vector.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getProjection',
+ ol.source.Vector.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getState',
+ ol.source.Vector.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'refresh',
+ ol.source.Vector.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'setAttributions',
+ ol.source.Vector.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'get',
+ ol.source.Vector.prototype.get);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getKeys',
+ ol.source.Vector.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getProperties',
+ ol.source.Vector.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'set',
+ ol.source.Vector.prototype.set);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'setProperties',
+ ol.source.Vector.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'unset',
+ ol.source.Vector.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'changed',
+ ol.source.Vector.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'dispatchEvent',
+ ol.source.Vector.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'getRevision',
+ ol.source.Vector.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'on',
+ ol.source.Vector.prototype.on);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'once',
+ ol.source.Vector.prototype.once);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'un',
+ ol.source.Vector.prototype.un);
+
+goog.exportProperty(
+ ol.source.Vector.prototype,
+ 'unByKey',
+ ol.source.Vector.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'addFeature',
+ ol.source.Cluster.prototype.addFeature);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'addFeatures',
+ ol.source.Cluster.prototype.addFeatures);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'clear',
+ ol.source.Cluster.prototype.clear);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'forEachFeature',
+ ol.source.Cluster.prototype.forEachFeature);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'forEachFeatureInExtent',
+ ol.source.Cluster.prototype.forEachFeatureInExtent);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'forEachFeatureIntersectingExtent',
+ ol.source.Cluster.prototype.forEachFeatureIntersectingExtent);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getFeaturesCollection',
+ ol.source.Cluster.prototype.getFeaturesCollection);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getFeatures',
+ ol.source.Cluster.prototype.getFeatures);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getFeaturesAtCoordinate',
+ ol.source.Cluster.prototype.getFeaturesAtCoordinate);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getFeaturesInExtent',
+ ol.source.Cluster.prototype.getFeaturesInExtent);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getClosestFeatureToCoordinate',
+ ol.source.Cluster.prototype.getClosestFeatureToCoordinate);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getExtent',
+ ol.source.Cluster.prototype.getExtent);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getFeatureById',
+ ol.source.Cluster.prototype.getFeatureById);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getFormat',
+ ol.source.Cluster.prototype.getFormat);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getUrl',
+ ol.source.Cluster.prototype.getUrl);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'removeFeature',
+ ol.source.Cluster.prototype.removeFeature);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getAttributions',
+ ol.source.Cluster.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getLogo',
+ ol.source.Cluster.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getProjection',
+ ol.source.Cluster.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getState',
+ ol.source.Cluster.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'refresh',
+ ol.source.Cluster.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'setAttributions',
+ ol.source.Cluster.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'get',
+ ol.source.Cluster.prototype.get);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getKeys',
+ ol.source.Cluster.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getProperties',
+ ol.source.Cluster.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'set',
+ ol.source.Cluster.prototype.set);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'setProperties',
+ ol.source.Cluster.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'unset',
+ ol.source.Cluster.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'changed',
+ ol.source.Cluster.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'dispatchEvent',
+ ol.source.Cluster.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'getRevision',
+ ol.source.Cluster.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'on',
+ ol.source.Cluster.prototype.on);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'once',
+ ol.source.Cluster.prototype.once);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'un',
+ ol.source.Cluster.prototype.un);
+
+goog.exportProperty(
+ ol.source.Cluster.prototype,
+ 'unByKey',
+ ol.source.Cluster.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getAttributions',
+ ol.source.Image.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getLogo',
+ ol.source.Image.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getProjection',
+ ol.source.Image.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getState',
+ ol.source.Image.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'refresh',
+ ol.source.Image.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'setAttributions',
+ ol.source.Image.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'get',
+ ol.source.Image.prototype.get);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getKeys',
+ ol.source.Image.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getProperties',
+ ol.source.Image.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'set',
+ ol.source.Image.prototype.set);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'setProperties',
+ ol.source.Image.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'unset',
+ ol.source.Image.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'changed',
+ ol.source.Image.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'dispatchEvent',
+ ol.source.Image.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'getRevision',
+ ol.source.Image.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'on',
+ ol.source.Image.prototype.on);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'once',
+ ol.source.Image.prototype.once);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'un',
+ ol.source.Image.prototype.un);
+
+goog.exportProperty(
+ ol.source.Image.prototype,
+ 'unByKey',
+ ol.source.Image.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Image.Event.prototype,
+ 'type',
+ ol.source.Image.Event.prototype.type);
+
+goog.exportProperty(
+ ol.source.Image.Event.prototype,
+ 'target',
+ ol.source.Image.Event.prototype.target);
+
+goog.exportProperty(
+ ol.source.Image.Event.prototype,
+ 'preventDefault',
+ ol.source.Image.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.source.Image.Event.prototype,
+ 'stopPropagation',
+ ol.source.Image.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getAttributions',
+ ol.source.ImageArcGISRest.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getLogo',
+ ol.source.ImageArcGISRest.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getProjection',
+ ol.source.ImageArcGISRest.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getState',
+ ol.source.ImageArcGISRest.prototype.getState);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'refresh',
+ ol.source.ImageArcGISRest.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'setAttributions',
+ ol.source.ImageArcGISRest.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'get',
+ ol.source.ImageArcGISRest.prototype.get);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getKeys',
+ ol.source.ImageArcGISRest.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getProperties',
+ ol.source.ImageArcGISRest.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'set',
+ ol.source.ImageArcGISRest.prototype.set);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'setProperties',
+ ol.source.ImageArcGISRest.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'unset',
+ ol.source.ImageArcGISRest.prototype.unset);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'changed',
+ ol.source.ImageArcGISRest.prototype.changed);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'dispatchEvent',
+ ol.source.ImageArcGISRest.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'getRevision',
+ ol.source.ImageArcGISRest.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'on',
+ ol.source.ImageArcGISRest.prototype.on);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'once',
+ ol.source.ImageArcGISRest.prototype.once);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'un',
+ ol.source.ImageArcGISRest.prototype.un);
+
+goog.exportProperty(
+ ol.source.ImageArcGISRest.prototype,
+ 'unByKey',
+ ol.source.ImageArcGISRest.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getAttributions',
+ ol.source.ImageCanvas.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getLogo',
+ ol.source.ImageCanvas.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getProjection',
+ ol.source.ImageCanvas.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getState',
+ ol.source.ImageCanvas.prototype.getState);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'refresh',
+ ol.source.ImageCanvas.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'setAttributions',
+ ol.source.ImageCanvas.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'get',
+ ol.source.ImageCanvas.prototype.get);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getKeys',
+ ol.source.ImageCanvas.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getProperties',
+ ol.source.ImageCanvas.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'set',
+ ol.source.ImageCanvas.prototype.set);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'setProperties',
+ ol.source.ImageCanvas.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'unset',
+ ol.source.ImageCanvas.prototype.unset);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'changed',
+ ol.source.ImageCanvas.prototype.changed);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'dispatchEvent',
+ ol.source.ImageCanvas.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'getRevision',
+ ol.source.ImageCanvas.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'on',
+ ol.source.ImageCanvas.prototype.on);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'once',
+ ol.source.ImageCanvas.prototype.once);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'un',
+ ol.source.ImageCanvas.prototype.un);
+
+goog.exportProperty(
+ ol.source.ImageCanvas.prototype,
+ 'unByKey',
+ ol.source.ImageCanvas.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getAttributions',
+ ol.source.ImageMapGuide.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getLogo',
+ ol.source.ImageMapGuide.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getProjection',
+ ol.source.ImageMapGuide.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getState',
+ ol.source.ImageMapGuide.prototype.getState);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'refresh',
+ ol.source.ImageMapGuide.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'setAttributions',
+ ol.source.ImageMapGuide.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'get',
+ ol.source.ImageMapGuide.prototype.get);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getKeys',
+ ol.source.ImageMapGuide.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getProperties',
+ ol.source.ImageMapGuide.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'set',
+ ol.source.ImageMapGuide.prototype.set);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'setProperties',
+ ol.source.ImageMapGuide.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'unset',
+ ol.source.ImageMapGuide.prototype.unset);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'changed',
+ ol.source.ImageMapGuide.prototype.changed);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'dispatchEvent',
+ ol.source.ImageMapGuide.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'getRevision',
+ ol.source.ImageMapGuide.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'on',
+ ol.source.ImageMapGuide.prototype.on);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'once',
+ ol.source.ImageMapGuide.prototype.once);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'un',
+ ol.source.ImageMapGuide.prototype.un);
+
+goog.exportProperty(
+ ol.source.ImageMapGuide.prototype,
+ 'unByKey',
+ ol.source.ImageMapGuide.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getAttributions',
+ ol.source.ImageStatic.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getLogo',
+ ol.source.ImageStatic.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getProjection',
+ ol.source.ImageStatic.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getState',
+ ol.source.ImageStatic.prototype.getState);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'refresh',
+ ol.source.ImageStatic.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'setAttributions',
+ ol.source.ImageStatic.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'get',
+ ol.source.ImageStatic.prototype.get);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getKeys',
+ ol.source.ImageStatic.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getProperties',
+ ol.source.ImageStatic.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'set',
+ ol.source.ImageStatic.prototype.set);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'setProperties',
+ ol.source.ImageStatic.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'unset',
+ ol.source.ImageStatic.prototype.unset);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'changed',
+ ol.source.ImageStatic.prototype.changed);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'dispatchEvent',
+ ol.source.ImageStatic.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'getRevision',
+ ol.source.ImageStatic.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'on',
+ ol.source.ImageStatic.prototype.on);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'once',
+ ol.source.ImageStatic.prototype.once);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'un',
+ ol.source.ImageStatic.prototype.un);
+
+goog.exportProperty(
+ ol.source.ImageStatic.prototype,
+ 'unByKey',
+ ol.source.ImageStatic.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getAttributions',
+ ol.source.ImageVector.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getLogo',
+ ol.source.ImageVector.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getProjection',
+ ol.source.ImageVector.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getState',
+ ol.source.ImageVector.prototype.getState);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'refresh',
+ ol.source.ImageVector.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'setAttributions',
+ ol.source.ImageVector.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'get',
+ ol.source.ImageVector.prototype.get);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getKeys',
+ ol.source.ImageVector.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getProperties',
+ ol.source.ImageVector.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'set',
+ ol.source.ImageVector.prototype.set);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'setProperties',
+ ol.source.ImageVector.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'unset',
+ ol.source.ImageVector.prototype.unset);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'changed',
+ ol.source.ImageVector.prototype.changed);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'dispatchEvent',
+ ol.source.ImageVector.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'getRevision',
+ ol.source.ImageVector.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'on',
+ ol.source.ImageVector.prototype.on);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'once',
+ ol.source.ImageVector.prototype.once);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'un',
+ ol.source.ImageVector.prototype.un);
+
+goog.exportProperty(
+ ol.source.ImageVector.prototype,
+ 'unByKey',
+ ol.source.ImageVector.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getAttributions',
+ ol.source.ImageWMS.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getLogo',
+ ol.source.ImageWMS.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getProjection',
+ ol.source.ImageWMS.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getState',
+ ol.source.ImageWMS.prototype.getState);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'refresh',
+ ol.source.ImageWMS.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'setAttributions',
+ ol.source.ImageWMS.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'get',
+ ol.source.ImageWMS.prototype.get);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getKeys',
+ ol.source.ImageWMS.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getProperties',
+ ol.source.ImageWMS.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'set',
+ ol.source.ImageWMS.prototype.set);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'setProperties',
+ ol.source.ImageWMS.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'unset',
+ ol.source.ImageWMS.prototype.unset);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'changed',
+ ol.source.ImageWMS.prototype.changed);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'dispatchEvent',
+ ol.source.ImageWMS.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'getRevision',
+ ol.source.ImageWMS.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'on',
+ ol.source.ImageWMS.prototype.on);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'once',
+ ol.source.ImageWMS.prototype.once);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'un',
+ ol.source.ImageWMS.prototype.un);
+
+goog.exportProperty(
+ ol.source.ImageWMS.prototype,
+ 'unByKey',
+ ol.source.ImageWMS.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.OSM.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setTileGridForProjection',
+ ol.source.OSM.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getTileLoadFunction',
+ ol.source.OSM.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getTileUrlFunction',
+ ol.source.OSM.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getUrls',
+ ol.source.OSM.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setTileLoadFunction',
+ ol.source.OSM.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setTileUrlFunction',
+ ol.source.OSM.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setUrl',
+ ol.source.OSM.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setUrls',
+ ol.source.OSM.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getTileGrid',
+ ol.source.OSM.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'refresh',
+ ol.source.OSM.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getAttributions',
+ ol.source.OSM.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getLogo',
+ ol.source.OSM.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getProjection',
+ ol.source.OSM.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getState',
+ ol.source.OSM.prototype.getState);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setAttributions',
+ ol.source.OSM.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'get',
+ ol.source.OSM.prototype.get);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getKeys',
+ ol.source.OSM.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getProperties',
+ ol.source.OSM.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'set',
+ ol.source.OSM.prototype.set);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'setProperties',
+ ol.source.OSM.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'unset',
+ ol.source.OSM.prototype.unset);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'changed',
+ ol.source.OSM.prototype.changed);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'dispatchEvent',
+ ol.source.OSM.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'getRevision',
+ ol.source.OSM.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'on',
+ ol.source.OSM.prototype.on);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'once',
+ ol.source.OSM.prototype.once);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'un',
+ ol.source.OSM.prototype.un);
+
+goog.exportProperty(
+ ol.source.OSM.prototype,
+ 'unByKey',
+ ol.source.OSM.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getAttributions',
+ ol.source.Raster.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getLogo',
+ ol.source.Raster.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getProjection',
+ ol.source.Raster.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getState',
+ ol.source.Raster.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'refresh',
+ ol.source.Raster.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'setAttributions',
+ ol.source.Raster.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'get',
+ ol.source.Raster.prototype.get);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getKeys',
+ ol.source.Raster.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getProperties',
+ ol.source.Raster.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'set',
+ ol.source.Raster.prototype.set);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'setProperties',
+ ol.source.Raster.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'unset',
+ ol.source.Raster.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'changed',
+ ol.source.Raster.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'dispatchEvent',
+ ol.source.Raster.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'getRevision',
+ ol.source.Raster.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'on',
+ ol.source.Raster.prototype.on);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'once',
+ ol.source.Raster.prototype.once);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'un',
+ ol.source.Raster.prototype.un);
+
+goog.exportProperty(
+ ol.source.Raster.prototype,
+ 'unByKey',
+ ol.source.Raster.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'type',
+ ol.source.Raster.Event.prototype.type);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'target',
+ ol.source.Raster.Event.prototype.target);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'preventDefault',
+ ol.source.Raster.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.source.Raster.Event.prototype,
+ 'stopPropagation',
+ ol.source.Raster.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.Stamen.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setTileGridForProjection',
+ ol.source.Stamen.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getTileLoadFunction',
+ ol.source.Stamen.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getTileUrlFunction',
+ ol.source.Stamen.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getUrls',
+ ol.source.Stamen.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setTileLoadFunction',
+ ol.source.Stamen.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setTileUrlFunction',
+ ol.source.Stamen.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setUrl',
+ ol.source.Stamen.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setUrls',
+ ol.source.Stamen.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getTileGrid',
+ ol.source.Stamen.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'refresh',
+ ol.source.Stamen.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getAttributions',
+ ol.source.Stamen.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getLogo',
+ ol.source.Stamen.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getProjection',
+ ol.source.Stamen.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getState',
+ ol.source.Stamen.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setAttributions',
+ ol.source.Stamen.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'get',
+ ol.source.Stamen.prototype.get);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getKeys',
+ ol.source.Stamen.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getProperties',
+ ol.source.Stamen.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'set',
+ ol.source.Stamen.prototype.set);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'setProperties',
+ ol.source.Stamen.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'unset',
+ ol.source.Stamen.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'changed',
+ ol.source.Stamen.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'dispatchEvent',
+ ol.source.Stamen.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'getRevision',
+ ol.source.Stamen.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'on',
+ ol.source.Stamen.prototype.on);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'once',
+ ol.source.Stamen.prototype.once);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'un',
+ ol.source.Stamen.prototype.un);
+
+goog.exportProperty(
+ ol.source.Stamen.prototype,
+ 'unByKey',
+ ol.source.Stamen.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Tile.Event.prototype,
+ 'type',
+ ol.source.Tile.Event.prototype.type);
+
+goog.exportProperty(
+ ol.source.Tile.Event.prototype,
+ 'target',
+ ol.source.Tile.Event.prototype.target);
+
+goog.exportProperty(
+ ol.source.Tile.Event.prototype,
+ 'preventDefault',
+ ol.source.Tile.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.source.Tile.Event.prototype,
+ 'stopPropagation',
+ ol.source.Tile.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.TileArcGISRest.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setTileGridForProjection',
+ ol.source.TileArcGISRest.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getTileLoadFunction',
+ ol.source.TileArcGISRest.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getTileUrlFunction',
+ ol.source.TileArcGISRest.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getUrls',
+ ol.source.TileArcGISRest.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setTileLoadFunction',
+ ol.source.TileArcGISRest.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setTileUrlFunction',
+ ol.source.TileArcGISRest.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setUrl',
+ ol.source.TileArcGISRest.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setUrls',
+ ol.source.TileArcGISRest.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getTileGrid',
+ ol.source.TileArcGISRest.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'refresh',
+ ol.source.TileArcGISRest.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getAttributions',
+ ol.source.TileArcGISRest.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getLogo',
+ ol.source.TileArcGISRest.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getProjection',
+ ol.source.TileArcGISRest.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getState',
+ ol.source.TileArcGISRest.prototype.getState);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setAttributions',
+ ol.source.TileArcGISRest.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'get',
+ ol.source.TileArcGISRest.prototype.get);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getKeys',
+ ol.source.TileArcGISRest.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getProperties',
+ ol.source.TileArcGISRest.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'set',
+ ol.source.TileArcGISRest.prototype.set);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'setProperties',
+ ol.source.TileArcGISRest.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'unset',
+ ol.source.TileArcGISRest.prototype.unset);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'changed',
+ ol.source.TileArcGISRest.prototype.changed);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'dispatchEvent',
+ ol.source.TileArcGISRest.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'getRevision',
+ ol.source.TileArcGISRest.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'on',
+ ol.source.TileArcGISRest.prototype.on);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'once',
+ ol.source.TileArcGISRest.prototype.once);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'un',
+ ol.source.TileArcGISRest.prototype.un);
+
+goog.exportProperty(
+ ol.source.TileArcGISRest.prototype,
+ 'unByKey',
+ ol.source.TileArcGISRest.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getTileGrid',
+ ol.source.TileDebug.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'refresh',
+ ol.source.TileDebug.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getAttributions',
+ ol.source.TileDebug.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getLogo',
+ ol.source.TileDebug.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getProjection',
+ ol.source.TileDebug.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getState',
+ ol.source.TileDebug.prototype.getState);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'setAttributions',
+ ol.source.TileDebug.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'get',
+ ol.source.TileDebug.prototype.get);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getKeys',
+ ol.source.TileDebug.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getProperties',
+ ol.source.TileDebug.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'set',
+ ol.source.TileDebug.prototype.set);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'setProperties',
+ ol.source.TileDebug.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'unset',
+ ol.source.TileDebug.prototype.unset);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'changed',
+ ol.source.TileDebug.prototype.changed);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'dispatchEvent',
+ ol.source.TileDebug.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'getRevision',
+ ol.source.TileDebug.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'on',
+ ol.source.TileDebug.prototype.on);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'once',
+ ol.source.TileDebug.prototype.once);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'un',
+ ol.source.TileDebug.prototype.un);
+
+goog.exportProperty(
+ ol.source.TileDebug.prototype,
+ 'unByKey',
+ ol.source.TileDebug.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.TileJSON.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setTileGridForProjection',
+ ol.source.TileJSON.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getTileLoadFunction',
+ ol.source.TileJSON.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getTileUrlFunction',
+ ol.source.TileJSON.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getUrls',
+ ol.source.TileJSON.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setTileLoadFunction',
+ ol.source.TileJSON.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setTileUrlFunction',
+ ol.source.TileJSON.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setUrl',
+ ol.source.TileJSON.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setUrls',
+ ol.source.TileJSON.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getTileGrid',
+ ol.source.TileJSON.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'refresh',
+ ol.source.TileJSON.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getAttributions',
+ ol.source.TileJSON.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getLogo',
+ ol.source.TileJSON.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getProjection',
+ ol.source.TileJSON.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getState',
+ ol.source.TileJSON.prototype.getState);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setAttributions',
+ ol.source.TileJSON.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'get',
+ ol.source.TileJSON.prototype.get);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getKeys',
+ ol.source.TileJSON.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getProperties',
+ ol.source.TileJSON.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'set',
+ ol.source.TileJSON.prototype.set);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'setProperties',
+ ol.source.TileJSON.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'unset',
+ ol.source.TileJSON.prototype.unset);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'changed',
+ ol.source.TileJSON.prototype.changed);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'dispatchEvent',
+ ol.source.TileJSON.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'getRevision',
+ ol.source.TileJSON.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'on',
+ ol.source.TileJSON.prototype.on);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'once',
+ ol.source.TileJSON.prototype.once);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'un',
+ ol.source.TileJSON.prototype.un);
+
+goog.exportProperty(
+ ol.source.TileJSON.prototype,
+ 'unByKey',
+ ol.source.TileJSON.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getTileGrid',
+ ol.source.TileUTFGrid.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'refresh',
+ ol.source.TileUTFGrid.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getAttributions',
+ ol.source.TileUTFGrid.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getLogo',
+ ol.source.TileUTFGrid.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getProjection',
+ ol.source.TileUTFGrid.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getState',
+ ol.source.TileUTFGrid.prototype.getState);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'setAttributions',
+ ol.source.TileUTFGrid.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'get',
+ ol.source.TileUTFGrid.prototype.get);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getKeys',
+ ol.source.TileUTFGrid.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getProperties',
+ ol.source.TileUTFGrid.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'set',
+ ol.source.TileUTFGrid.prototype.set);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'setProperties',
+ ol.source.TileUTFGrid.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'unset',
+ ol.source.TileUTFGrid.prototype.unset);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'changed',
+ ol.source.TileUTFGrid.prototype.changed);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'dispatchEvent',
+ ol.source.TileUTFGrid.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'getRevision',
+ ol.source.TileUTFGrid.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'on',
+ ol.source.TileUTFGrid.prototype.on);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'once',
+ ol.source.TileUTFGrid.prototype.once);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'un',
+ ol.source.TileUTFGrid.prototype.un);
+
+goog.exportProperty(
+ ol.source.TileUTFGrid.prototype,
+ 'unByKey',
+ ol.source.TileUTFGrid.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.TileWMS.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setTileGridForProjection',
+ ol.source.TileWMS.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getTileLoadFunction',
+ ol.source.TileWMS.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getTileUrlFunction',
+ ol.source.TileWMS.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getUrls',
+ ol.source.TileWMS.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setTileLoadFunction',
+ ol.source.TileWMS.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setTileUrlFunction',
+ ol.source.TileWMS.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setUrl',
+ ol.source.TileWMS.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setUrls',
+ ol.source.TileWMS.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getTileGrid',
+ ol.source.TileWMS.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'refresh',
+ ol.source.TileWMS.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getAttributions',
+ ol.source.TileWMS.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getLogo',
+ ol.source.TileWMS.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getProjection',
+ ol.source.TileWMS.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getState',
+ ol.source.TileWMS.prototype.getState);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setAttributions',
+ ol.source.TileWMS.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'get',
+ ol.source.TileWMS.prototype.get);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getKeys',
+ ol.source.TileWMS.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getProperties',
+ ol.source.TileWMS.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'set',
+ ol.source.TileWMS.prototype.set);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'setProperties',
+ ol.source.TileWMS.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'unset',
+ ol.source.TileWMS.prototype.unset);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'changed',
+ ol.source.TileWMS.prototype.changed);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'dispatchEvent',
+ ol.source.TileWMS.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'getRevision',
+ ol.source.TileWMS.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'on',
+ ol.source.TileWMS.prototype.on);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'once',
+ ol.source.TileWMS.prototype.once);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'un',
+ ol.source.TileWMS.prototype.un);
+
+goog.exportProperty(
+ ol.source.TileWMS.prototype,
+ 'unByKey',
+ ol.source.TileWMS.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Vector.Event.prototype,
+ 'type',
+ ol.source.Vector.Event.prototype.type);
+
+goog.exportProperty(
+ ol.source.Vector.Event.prototype,
+ 'target',
+ ol.source.Vector.Event.prototype.target);
+
+goog.exportProperty(
+ ol.source.Vector.Event.prototype,
+ 'preventDefault',
+ ol.source.Vector.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.source.Vector.Event.prototype,
+ 'stopPropagation',
+ ol.source.Vector.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getTileLoadFunction',
+ ol.source.VectorTile.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getTileUrlFunction',
+ ol.source.VectorTile.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getUrls',
+ ol.source.VectorTile.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'setTileLoadFunction',
+ ol.source.VectorTile.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'setTileUrlFunction',
+ ol.source.VectorTile.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'setUrl',
+ ol.source.VectorTile.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'setUrls',
+ ol.source.VectorTile.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getTileGrid',
+ ol.source.VectorTile.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'refresh',
+ ol.source.VectorTile.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getAttributions',
+ ol.source.VectorTile.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getLogo',
+ ol.source.VectorTile.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getProjection',
+ ol.source.VectorTile.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getState',
+ ol.source.VectorTile.prototype.getState);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'setAttributions',
+ ol.source.VectorTile.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'get',
+ ol.source.VectorTile.prototype.get);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getKeys',
+ ol.source.VectorTile.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getProperties',
+ ol.source.VectorTile.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'set',
+ ol.source.VectorTile.prototype.set);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'setProperties',
+ ol.source.VectorTile.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'unset',
+ ol.source.VectorTile.prototype.unset);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'changed',
+ ol.source.VectorTile.prototype.changed);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'dispatchEvent',
+ ol.source.VectorTile.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'getRevision',
+ ol.source.VectorTile.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'on',
+ ol.source.VectorTile.prototype.on);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'once',
+ ol.source.VectorTile.prototype.once);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'un',
+ ol.source.VectorTile.prototype.un);
+
+goog.exportProperty(
+ ol.source.VectorTile.prototype,
+ 'unByKey',
+ ol.source.VectorTile.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.WMTS.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setTileGridForProjection',
+ ol.source.WMTS.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getTileLoadFunction',
+ ol.source.WMTS.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getTileUrlFunction',
+ ol.source.WMTS.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getUrls',
+ ol.source.WMTS.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setTileLoadFunction',
+ ol.source.WMTS.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setTileUrlFunction',
+ ol.source.WMTS.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setUrl',
+ ol.source.WMTS.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setUrls',
+ ol.source.WMTS.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getTileGrid',
+ ol.source.WMTS.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'refresh',
+ ol.source.WMTS.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getAttributions',
+ ol.source.WMTS.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getLogo',
+ ol.source.WMTS.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getProjection',
+ ol.source.WMTS.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getState',
+ ol.source.WMTS.prototype.getState);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setAttributions',
+ ol.source.WMTS.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'get',
+ ol.source.WMTS.prototype.get);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getKeys',
+ ol.source.WMTS.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getProperties',
+ ol.source.WMTS.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'set',
+ ol.source.WMTS.prototype.set);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'setProperties',
+ ol.source.WMTS.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'unset',
+ ol.source.WMTS.prototype.unset);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'changed',
+ ol.source.WMTS.prototype.changed);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'dispatchEvent',
+ ol.source.WMTS.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'getRevision',
+ ol.source.WMTS.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'on',
+ ol.source.WMTS.prototype.on);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'once',
+ ol.source.WMTS.prototype.once);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'un',
+ ol.source.WMTS.prototype.un);
+
+goog.exportProperty(
+ ol.source.WMTS.prototype,
+ 'unByKey',
+ ol.source.WMTS.prototype.unByKey);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setRenderReprojectionEdges',
+ ol.source.Zoomify.prototype.setRenderReprojectionEdges);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setTileGridForProjection',
+ ol.source.Zoomify.prototype.setTileGridForProjection);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getTileLoadFunction',
+ ol.source.Zoomify.prototype.getTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getTileUrlFunction',
+ ol.source.Zoomify.prototype.getTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getUrls',
+ ol.source.Zoomify.prototype.getUrls);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setTileLoadFunction',
+ ol.source.Zoomify.prototype.setTileLoadFunction);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setTileUrlFunction',
+ ol.source.Zoomify.prototype.setTileUrlFunction);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setUrl',
+ ol.source.Zoomify.prototype.setUrl);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setUrls',
+ ol.source.Zoomify.prototype.setUrls);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getTileGrid',
+ ol.source.Zoomify.prototype.getTileGrid);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'refresh',
+ ol.source.Zoomify.prototype.refresh);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getAttributions',
+ ol.source.Zoomify.prototype.getAttributions);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getLogo',
+ ol.source.Zoomify.prototype.getLogo);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getProjection',
+ ol.source.Zoomify.prototype.getProjection);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getState',
+ ol.source.Zoomify.prototype.getState);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setAttributions',
+ ol.source.Zoomify.prototype.setAttributions);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'get',
+ ol.source.Zoomify.prototype.get);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getKeys',
+ ol.source.Zoomify.prototype.getKeys);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getProperties',
+ ol.source.Zoomify.prototype.getProperties);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'set',
+ ol.source.Zoomify.prototype.set);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'setProperties',
+ ol.source.Zoomify.prototype.setProperties);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'unset',
+ ol.source.Zoomify.prototype.unset);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'changed',
+ ol.source.Zoomify.prototype.changed);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'dispatchEvent',
+ ol.source.Zoomify.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'getRevision',
+ ol.source.Zoomify.prototype.getRevision);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'on',
+ ol.source.Zoomify.prototype.on);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'once',
+ ol.source.Zoomify.prototype.once);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'un',
+ ol.source.Zoomify.prototype.un);
+
+goog.exportProperty(
+ ol.source.Zoomify.prototype,
+ 'unByKey',
+ ol.source.Zoomify.prototype.unByKey);
+
+goog.exportProperty(
+ ol.reproj.Tile.prototype,
+ 'getTileCoord',
+ ol.reproj.Tile.prototype.getTileCoord);
+
+goog.exportProperty(
+ ol.reproj.Tile.prototype,
+ 'load',
+ ol.reproj.Tile.prototype.load);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'changed',
+ ol.renderer.Layer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'dispatchEvent',
+ ol.renderer.Layer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'getRevision',
+ ol.renderer.Layer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'on',
+ ol.renderer.Layer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'once',
+ ol.renderer.Layer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'un',
+ ol.renderer.Layer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.Layer.prototype,
+ 'unByKey',
+ ol.renderer.Layer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'changed',
+ ol.renderer.webgl.Layer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'dispatchEvent',
+ ol.renderer.webgl.Layer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'getRevision',
+ ol.renderer.webgl.Layer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'on',
+ ol.renderer.webgl.Layer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'once',
+ ol.renderer.webgl.Layer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'un',
+ ol.renderer.webgl.Layer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.webgl.Layer.prototype,
+ 'unByKey',
+ ol.renderer.webgl.Layer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'changed',
+ ol.renderer.webgl.ImageLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.webgl.ImageLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'getRevision',
+ ol.renderer.webgl.ImageLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'on',
+ ol.renderer.webgl.ImageLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'once',
+ ol.renderer.webgl.ImageLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'un',
+ ol.renderer.webgl.ImageLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.webgl.ImageLayer.prototype,
+ 'unByKey',
+ ol.renderer.webgl.ImageLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'changed',
+ ol.renderer.webgl.TileLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.webgl.TileLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'getRevision',
+ ol.renderer.webgl.TileLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'on',
+ ol.renderer.webgl.TileLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'once',
+ ol.renderer.webgl.TileLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'un',
+ ol.renderer.webgl.TileLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.webgl.TileLayer.prototype,
+ 'unByKey',
+ ol.renderer.webgl.TileLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'changed',
+ ol.renderer.webgl.VectorLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.webgl.VectorLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'getRevision',
+ ol.renderer.webgl.VectorLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'on',
+ ol.renderer.webgl.VectorLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'once',
+ ol.renderer.webgl.VectorLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'un',
+ ol.renderer.webgl.VectorLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.webgl.VectorLayer.prototype,
+ 'unByKey',
+ ol.renderer.webgl.VectorLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'changed',
+ ol.renderer.canvas.Layer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'dispatchEvent',
+ ol.renderer.canvas.Layer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'getRevision',
+ ol.renderer.canvas.Layer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'on',
+ ol.renderer.canvas.Layer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'once',
+ ol.renderer.canvas.Layer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'un',
+ ol.renderer.canvas.Layer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.canvas.Layer.prototype,
+ 'unByKey',
+ ol.renderer.canvas.Layer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'changed',
+ ol.renderer.canvas.IntermediateCanvas.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'dispatchEvent',
+ ol.renderer.canvas.IntermediateCanvas.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'getRevision',
+ ol.renderer.canvas.IntermediateCanvas.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'on',
+ ol.renderer.canvas.IntermediateCanvas.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'once',
+ ol.renderer.canvas.IntermediateCanvas.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'un',
+ ol.renderer.canvas.IntermediateCanvas.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.canvas.IntermediateCanvas.prototype,
+ 'unByKey',
+ ol.renderer.canvas.IntermediateCanvas.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'changed',
+ ol.renderer.canvas.ImageLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.canvas.ImageLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'getRevision',
+ ol.renderer.canvas.ImageLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'on',
+ ol.renderer.canvas.ImageLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'once',
+ ol.renderer.canvas.ImageLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'un',
+ ol.renderer.canvas.ImageLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.canvas.ImageLayer.prototype,
+ 'unByKey',
+ ol.renderer.canvas.ImageLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'changed',
+ ol.renderer.canvas.TileLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.canvas.TileLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'getRevision',
+ ol.renderer.canvas.TileLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'on',
+ ol.renderer.canvas.TileLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'once',
+ ol.renderer.canvas.TileLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'un',
+ ol.renderer.canvas.TileLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.canvas.TileLayer.prototype,
+ 'unByKey',
+ ol.renderer.canvas.TileLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'changed',
+ ol.renderer.canvas.VectorLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.canvas.VectorLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'getRevision',
+ ol.renderer.canvas.VectorLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'on',
+ ol.renderer.canvas.VectorLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'once',
+ ol.renderer.canvas.VectorLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'un',
+ ol.renderer.canvas.VectorLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorLayer.prototype,
+ 'unByKey',
+ ol.renderer.canvas.VectorLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'changed',
+ ol.renderer.canvas.VectorTileLayer.prototype.changed);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'dispatchEvent',
+ ol.renderer.canvas.VectorTileLayer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'getRevision',
+ ol.renderer.canvas.VectorTileLayer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'on',
+ ol.renderer.canvas.VectorTileLayer.prototype.on);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'once',
+ ol.renderer.canvas.VectorTileLayer.prototype.once);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'un',
+ ol.renderer.canvas.VectorTileLayer.prototype.un);
+
+goog.exportProperty(
+ ol.renderer.canvas.VectorTileLayer.prototype,
+ 'unByKey',
+ ol.renderer.canvas.VectorTileLayer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'type',
+ ol.render.Event.prototype.type);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'target',
+ ol.render.Event.prototype.target);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'preventDefault',
+ ol.render.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.render.Event.prototype,
+ 'stopPropagation',
+ ol.render.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.pointer.PointerEvent.prototype,
+ 'type',
+ ol.pointer.PointerEvent.prototype.type);
+
+goog.exportProperty(
+ ol.pointer.PointerEvent.prototype,
+ 'target',
+ ol.pointer.PointerEvent.prototype.target);
+
+goog.exportProperty(
+ ol.pointer.PointerEvent.prototype,
+ 'preventDefault',
+ ol.pointer.PointerEvent.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.pointer.PointerEvent.prototype,
+ 'stopPropagation',
+ ol.pointer.PointerEvent.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'get',
+ ol.layer.Base.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getKeys',
+ ol.layer.Base.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getProperties',
+ ol.layer.Base.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'set',
+ ol.layer.Base.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'setProperties',
+ ol.layer.Base.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'unset',
+ ol.layer.Base.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'changed',
+ ol.layer.Base.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'dispatchEvent',
+ ol.layer.Base.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'getRevision',
+ ol.layer.Base.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'on',
+ ol.layer.Base.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'once',
+ ol.layer.Base.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'un',
+ ol.layer.Base.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Base.prototype,
+ 'unByKey',
+ ol.layer.Base.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getExtent',
+ ol.layer.Group.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getMaxResolution',
+ ol.layer.Group.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getMinResolution',
+ ol.layer.Group.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getOpacity',
+ ol.layer.Group.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getVisible',
+ ol.layer.Group.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getZIndex',
+ ol.layer.Group.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setExtent',
+ ol.layer.Group.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setMaxResolution',
+ ol.layer.Group.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setMinResolution',
+ ol.layer.Group.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setOpacity',
+ ol.layer.Group.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setVisible',
+ ol.layer.Group.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setZIndex',
+ ol.layer.Group.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'get',
+ ol.layer.Group.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getKeys',
+ ol.layer.Group.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getProperties',
+ ol.layer.Group.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'set',
+ ol.layer.Group.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'setProperties',
+ ol.layer.Group.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'unset',
+ ol.layer.Group.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'changed',
+ ol.layer.Group.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'dispatchEvent',
+ ol.layer.Group.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'getRevision',
+ ol.layer.Group.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'on',
+ ol.layer.Group.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'once',
+ ol.layer.Group.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'un',
+ ol.layer.Group.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Group.prototype,
+ 'unByKey',
+ ol.layer.Group.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getExtent',
+ ol.layer.Layer.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getMaxResolution',
+ ol.layer.Layer.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getMinResolution',
+ ol.layer.Layer.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getOpacity',
+ ol.layer.Layer.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getVisible',
+ ol.layer.Layer.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getZIndex',
+ ol.layer.Layer.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setExtent',
+ ol.layer.Layer.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setMaxResolution',
+ ol.layer.Layer.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setMinResolution',
+ ol.layer.Layer.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setOpacity',
+ ol.layer.Layer.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setVisible',
+ ol.layer.Layer.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setZIndex',
+ ol.layer.Layer.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'get',
+ ol.layer.Layer.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getKeys',
+ ol.layer.Layer.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getProperties',
+ ol.layer.Layer.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'set',
+ ol.layer.Layer.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'setProperties',
+ ol.layer.Layer.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'unset',
+ ol.layer.Layer.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'changed',
+ ol.layer.Layer.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'dispatchEvent',
+ ol.layer.Layer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'getRevision',
+ ol.layer.Layer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'on',
+ ol.layer.Layer.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'once',
+ ol.layer.Layer.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'un',
+ ol.layer.Layer.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Layer.prototype,
+ 'unByKey',
+ ol.layer.Layer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setMap',
+ ol.layer.Vector.prototype.setMap);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setSource',
+ ol.layer.Vector.prototype.setSource);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getExtent',
+ ol.layer.Vector.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getMaxResolution',
+ ol.layer.Vector.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getMinResolution',
+ ol.layer.Vector.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getOpacity',
+ ol.layer.Vector.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getVisible',
+ ol.layer.Vector.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getZIndex',
+ ol.layer.Vector.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setExtent',
+ ol.layer.Vector.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setMaxResolution',
+ ol.layer.Vector.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setMinResolution',
+ ol.layer.Vector.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setOpacity',
+ ol.layer.Vector.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setVisible',
+ ol.layer.Vector.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setZIndex',
+ ol.layer.Vector.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'get',
+ ol.layer.Vector.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getKeys',
+ ol.layer.Vector.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getProperties',
+ ol.layer.Vector.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'set',
+ ol.layer.Vector.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'setProperties',
+ ol.layer.Vector.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'unset',
+ ol.layer.Vector.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'changed',
+ ol.layer.Vector.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'dispatchEvent',
+ ol.layer.Vector.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'getRevision',
+ ol.layer.Vector.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'on',
+ ol.layer.Vector.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'once',
+ ol.layer.Vector.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'un',
+ ol.layer.Vector.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Vector.prototype,
+ 'unByKey',
+ ol.layer.Vector.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getSource',
+ ol.layer.Heatmap.prototype.getSource);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getStyle',
+ ol.layer.Heatmap.prototype.getStyle);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getStyleFunction',
+ ol.layer.Heatmap.prototype.getStyleFunction);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setStyle',
+ ol.layer.Heatmap.prototype.setStyle);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setMap',
+ ol.layer.Heatmap.prototype.setMap);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setSource',
+ ol.layer.Heatmap.prototype.setSource);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getExtent',
+ ol.layer.Heatmap.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getMaxResolution',
+ ol.layer.Heatmap.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getMinResolution',
+ ol.layer.Heatmap.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getOpacity',
+ ol.layer.Heatmap.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getVisible',
+ ol.layer.Heatmap.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getZIndex',
+ ol.layer.Heatmap.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setExtent',
+ ol.layer.Heatmap.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setMaxResolution',
+ ol.layer.Heatmap.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setMinResolution',
+ ol.layer.Heatmap.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setOpacity',
+ ol.layer.Heatmap.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setVisible',
+ ol.layer.Heatmap.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setZIndex',
+ ol.layer.Heatmap.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'get',
+ ol.layer.Heatmap.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getKeys',
+ ol.layer.Heatmap.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getProperties',
+ ol.layer.Heatmap.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'set',
+ ol.layer.Heatmap.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'setProperties',
+ ol.layer.Heatmap.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'unset',
+ ol.layer.Heatmap.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'changed',
+ ol.layer.Heatmap.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'dispatchEvent',
+ ol.layer.Heatmap.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'getRevision',
+ ol.layer.Heatmap.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'on',
+ ol.layer.Heatmap.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'once',
+ ol.layer.Heatmap.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'un',
+ ol.layer.Heatmap.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Heatmap.prototype,
+ 'unByKey',
+ ol.layer.Heatmap.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setMap',
+ ol.layer.Image.prototype.setMap);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setSource',
+ ol.layer.Image.prototype.setSource);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getExtent',
+ ol.layer.Image.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getMaxResolution',
+ ol.layer.Image.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getMinResolution',
+ ol.layer.Image.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getOpacity',
+ ol.layer.Image.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getVisible',
+ ol.layer.Image.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getZIndex',
+ ol.layer.Image.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setExtent',
+ ol.layer.Image.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setMaxResolution',
+ ol.layer.Image.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setMinResolution',
+ ol.layer.Image.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setOpacity',
+ ol.layer.Image.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setVisible',
+ ol.layer.Image.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setZIndex',
+ ol.layer.Image.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'get',
+ ol.layer.Image.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getKeys',
+ ol.layer.Image.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getProperties',
+ ol.layer.Image.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'set',
+ ol.layer.Image.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'setProperties',
+ ol.layer.Image.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'unset',
+ ol.layer.Image.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'changed',
+ ol.layer.Image.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'dispatchEvent',
+ ol.layer.Image.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'getRevision',
+ ol.layer.Image.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'on',
+ ol.layer.Image.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'once',
+ ol.layer.Image.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'un',
+ ol.layer.Image.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Image.prototype,
+ 'unByKey',
+ ol.layer.Image.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setMap',
+ ol.layer.Tile.prototype.setMap);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setSource',
+ ol.layer.Tile.prototype.setSource);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getExtent',
+ ol.layer.Tile.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getMaxResolution',
+ ol.layer.Tile.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getMinResolution',
+ ol.layer.Tile.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getOpacity',
+ ol.layer.Tile.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getVisible',
+ ol.layer.Tile.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getZIndex',
+ ol.layer.Tile.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setExtent',
+ ol.layer.Tile.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setMaxResolution',
+ ol.layer.Tile.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setMinResolution',
+ ol.layer.Tile.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setOpacity',
+ ol.layer.Tile.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setVisible',
+ ol.layer.Tile.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setZIndex',
+ ol.layer.Tile.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'get',
+ ol.layer.Tile.prototype.get);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getKeys',
+ ol.layer.Tile.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getProperties',
+ ol.layer.Tile.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'set',
+ ol.layer.Tile.prototype.set);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'setProperties',
+ ol.layer.Tile.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'unset',
+ ol.layer.Tile.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'changed',
+ ol.layer.Tile.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'dispatchEvent',
+ ol.layer.Tile.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'getRevision',
+ ol.layer.Tile.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'on',
+ ol.layer.Tile.prototype.on);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'once',
+ ol.layer.Tile.prototype.once);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'un',
+ ol.layer.Tile.prototype.un);
+
+goog.exportProperty(
+ ol.layer.Tile.prototype,
+ 'unByKey',
+ ol.layer.Tile.prototype.unByKey);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getSource',
+ ol.layer.VectorTile.prototype.getSource);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getStyle',
+ ol.layer.VectorTile.prototype.getStyle);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getStyleFunction',
+ ol.layer.VectorTile.prototype.getStyleFunction);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setStyle',
+ ol.layer.VectorTile.prototype.setStyle);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setMap',
+ ol.layer.VectorTile.prototype.setMap);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setSource',
+ ol.layer.VectorTile.prototype.setSource);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getExtent',
+ ol.layer.VectorTile.prototype.getExtent);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getMaxResolution',
+ ol.layer.VectorTile.prototype.getMaxResolution);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getMinResolution',
+ ol.layer.VectorTile.prototype.getMinResolution);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getOpacity',
+ ol.layer.VectorTile.prototype.getOpacity);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getVisible',
+ ol.layer.VectorTile.prototype.getVisible);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getZIndex',
+ ol.layer.VectorTile.prototype.getZIndex);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setExtent',
+ ol.layer.VectorTile.prototype.setExtent);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setMaxResolution',
+ ol.layer.VectorTile.prototype.setMaxResolution);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setMinResolution',
+ ol.layer.VectorTile.prototype.setMinResolution);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setOpacity',
+ ol.layer.VectorTile.prototype.setOpacity);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setVisible',
+ ol.layer.VectorTile.prototype.setVisible);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setZIndex',
+ ol.layer.VectorTile.prototype.setZIndex);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'get',
+ ol.layer.VectorTile.prototype.get);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getKeys',
+ ol.layer.VectorTile.prototype.getKeys);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getProperties',
+ ol.layer.VectorTile.prototype.getProperties);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'set',
+ ol.layer.VectorTile.prototype.set);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'setProperties',
+ ol.layer.VectorTile.prototype.setProperties);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'unset',
+ ol.layer.VectorTile.prototype.unset);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'changed',
+ ol.layer.VectorTile.prototype.changed);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'dispatchEvent',
+ ol.layer.VectorTile.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'getRevision',
+ ol.layer.VectorTile.prototype.getRevision);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'on',
+ ol.layer.VectorTile.prototype.on);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'once',
+ ol.layer.VectorTile.prototype.once);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'un',
+ ol.layer.VectorTile.prototype.un);
+
+goog.exportProperty(
+ ol.layer.VectorTile.prototype,
+ 'unByKey',
+ ol.layer.VectorTile.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'get',
+ ol.interaction.Interaction.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'getKeys',
+ ol.interaction.Interaction.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'getProperties',
+ ol.interaction.Interaction.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'set',
+ ol.interaction.Interaction.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'setProperties',
+ ol.interaction.Interaction.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'unset',
+ ol.interaction.Interaction.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'changed',
+ ol.interaction.Interaction.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'dispatchEvent',
+ ol.interaction.Interaction.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'getRevision',
+ ol.interaction.Interaction.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'on',
+ ol.interaction.Interaction.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'once',
+ ol.interaction.Interaction.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'un',
+ ol.interaction.Interaction.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Interaction.prototype,
+ 'unByKey',
+ ol.interaction.Interaction.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'getActive',
+ ol.interaction.DoubleClickZoom.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'getMap',
+ ol.interaction.DoubleClickZoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'setActive',
+ ol.interaction.DoubleClickZoom.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'get',
+ ol.interaction.DoubleClickZoom.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'getKeys',
+ ol.interaction.DoubleClickZoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'getProperties',
+ ol.interaction.DoubleClickZoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'set',
+ ol.interaction.DoubleClickZoom.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'setProperties',
+ ol.interaction.DoubleClickZoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'unset',
+ ol.interaction.DoubleClickZoom.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'changed',
+ ol.interaction.DoubleClickZoom.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'dispatchEvent',
+ ol.interaction.DoubleClickZoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'getRevision',
+ ol.interaction.DoubleClickZoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'on',
+ ol.interaction.DoubleClickZoom.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'once',
+ ol.interaction.DoubleClickZoom.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'un',
+ ol.interaction.DoubleClickZoom.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DoubleClickZoom.prototype,
+ 'unByKey',
+ ol.interaction.DoubleClickZoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'getActive',
+ ol.interaction.DragAndDrop.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'getMap',
+ ol.interaction.DragAndDrop.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'setActive',
+ ol.interaction.DragAndDrop.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'get',
+ ol.interaction.DragAndDrop.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'getKeys',
+ ol.interaction.DragAndDrop.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'getProperties',
+ ol.interaction.DragAndDrop.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'set',
+ ol.interaction.DragAndDrop.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'setProperties',
+ ol.interaction.DragAndDrop.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'unset',
+ ol.interaction.DragAndDrop.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'changed',
+ ol.interaction.DragAndDrop.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'dispatchEvent',
+ ol.interaction.DragAndDrop.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'getRevision',
+ ol.interaction.DragAndDrop.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'on',
+ ol.interaction.DragAndDrop.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'once',
+ ol.interaction.DragAndDrop.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'un',
+ ol.interaction.DragAndDrop.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.prototype,
+ 'unByKey',
+ ol.interaction.DragAndDrop.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'type',
+ ol.interaction.DragAndDrop.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'target',
+ ol.interaction.DragAndDrop.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'preventDefault',
+ ol.interaction.DragAndDrop.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.DragAndDrop.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.DragAndDrop.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'getActive',
+ ol.interaction.Pointer.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'getMap',
+ ol.interaction.Pointer.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'setActive',
+ ol.interaction.Pointer.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'get',
+ ol.interaction.Pointer.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'getKeys',
+ ol.interaction.Pointer.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'getProperties',
+ ol.interaction.Pointer.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'set',
+ ol.interaction.Pointer.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'setProperties',
+ ol.interaction.Pointer.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'unset',
+ ol.interaction.Pointer.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'changed',
+ ol.interaction.Pointer.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'dispatchEvent',
+ ol.interaction.Pointer.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'getRevision',
+ ol.interaction.Pointer.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'on',
+ ol.interaction.Pointer.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'once',
+ ol.interaction.Pointer.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'un',
+ ol.interaction.Pointer.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Pointer.prototype,
+ 'unByKey',
+ ol.interaction.Pointer.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'getActive',
+ ol.interaction.DragBox.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'getMap',
+ ol.interaction.DragBox.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'setActive',
+ ol.interaction.DragBox.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'get',
+ ol.interaction.DragBox.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'getKeys',
+ ol.interaction.DragBox.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'getProperties',
+ ol.interaction.DragBox.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'set',
+ ol.interaction.DragBox.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'setProperties',
+ ol.interaction.DragBox.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'unset',
+ ol.interaction.DragBox.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'changed',
+ ol.interaction.DragBox.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'dispatchEvent',
+ ol.interaction.DragBox.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'getRevision',
+ ol.interaction.DragBox.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'on',
+ ol.interaction.DragBox.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'once',
+ ol.interaction.DragBox.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'un',
+ ol.interaction.DragBox.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DragBox.prototype,
+ 'unByKey',
+ ol.interaction.DragBox.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragBox.Event.prototype,
+ 'type',
+ ol.interaction.DragBox.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.DragBox.Event.prototype,
+ 'target',
+ ol.interaction.DragBox.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.DragBox.Event.prototype,
+ 'preventDefault',
+ ol.interaction.DragBox.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.DragBox.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.DragBox.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'getActive',
+ ol.interaction.DragPan.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'getMap',
+ ol.interaction.DragPan.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'setActive',
+ ol.interaction.DragPan.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'get',
+ ol.interaction.DragPan.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'getKeys',
+ ol.interaction.DragPan.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'getProperties',
+ ol.interaction.DragPan.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'set',
+ ol.interaction.DragPan.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'setProperties',
+ ol.interaction.DragPan.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'unset',
+ ol.interaction.DragPan.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'changed',
+ ol.interaction.DragPan.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'dispatchEvent',
+ ol.interaction.DragPan.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'getRevision',
+ ol.interaction.DragPan.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'on',
+ ol.interaction.DragPan.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'once',
+ ol.interaction.DragPan.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'un',
+ ol.interaction.DragPan.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DragPan.prototype,
+ 'unByKey',
+ ol.interaction.DragPan.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'getActive',
+ ol.interaction.DragRotate.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'getMap',
+ ol.interaction.DragRotate.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'setActive',
+ ol.interaction.DragRotate.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'get',
+ ol.interaction.DragRotate.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'getKeys',
+ ol.interaction.DragRotate.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'getProperties',
+ ol.interaction.DragRotate.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'set',
+ ol.interaction.DragRotate.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'setProperties',
+ ol.interaction.DragRotate.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'unset',
+ ol.interaction.DragRotate.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'changed',
+ ol.interaction.DragRotate.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'dispatchEvent',
+ ol.interaction.DragRotate.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'getRevision',
+ ol.interaction.DragRotate.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'on',
+ ol.interaction.DragRotate.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'once',
+ ol.interaction.DragRotate.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'un',
+ ol.interaction.DragRotate.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DragRotate.prototype,
+ 'unByKey',
+ ol.interaction.DragRotate.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'getActive',
+ ol.interaction.DragRotateAndZoom.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'getMap',
+ ol.interaction.DragRotateAndZoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'setActive',
+ ol.interaction.DragRotateAndZoom.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'get',
+ ol.interaction.DragRotateAndZoom.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'getKeys',
+ ol.interaction.DragRotateAndZoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'getProperties',
+ ol.interaction.DragRotateAndZoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'set',
+ ol.interaction.DragRotateAndZoom.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'setProperties',
+ ol.interaction.DragRotateAndZoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'unset',
+ ol.interaction.DragRotateAndZoom.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'changed',
+ ol.interaction.DragRotateAndZoom.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'dispatchEvent',
+ ol.interaction.DragRotateAndZoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'getRevision',
+ ol.interaction.DragRotateAndZoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'on',
+ ol.interaction.DragRotateAndZoom.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'once',
+ ol.interaction.DragRotateAndZoom.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'un',
+ ol.interaction.DragRotateAndZoom.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DragRotateAndZoom.prototype,
+ 'unByKey',
+ ol.interaction.DragRotateAndZoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'getGeometry',
+ ol.interaction.DragZoom.prototype.getGeometry);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'getActive',
+ ol.interaction.DragZoom.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'getMap',
+ ol.interaction.DragZoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'setActive',
+ ol.interaction.DragZoom.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'get',
+ ol.interaction.DragZoom.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'getKeys',
+ ol.interaction.DragZoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'getProperties',
+ ol.interaction.DragZoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'set',
+ ol.interaction.DragZoom.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'setProperties',
+ ol.interaction.DragZoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'unset',
+ ol.interaction.DragZoom.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'changed',
+ ol.interaction.DragZoom.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'dispatchEvent',
+ ol.interaction.DragZoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'getRevision',
+ ol.interaction.DragZoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'on',
+ ol.interaction.DragZoom.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'once',
+ ol.interaction.DragZoom.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'un',
+ ol.interaction.DragZoom.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.DragZoom.prototype,
+ 'unByKey',
+ ol.interaction.DragZoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'getActive',
+ ol.interaction.Draw.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'getMap',
+ ol.interaction.Draw.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'setActive',
+ ol.interaction.Draw.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'get',
+ ol.interaction.Draw.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'getKeys',
+ ol.interaction.Draw.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'getProperties',
+ ol.interaction.Draw.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'set',
+ ol.interaction.Draw.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'setProperties',
+ ol.interaction.Draw.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'unset',
+ ol.interaction.Draw.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'changed',
+ ol.interaction.Draw.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'dispatchEvent',
+ ol.interaction.Draw.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'getRevision',
+ ol.interaction.Draw.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'on',
+ ol.interaction.Draw.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'once',
+ ol.interaction.Draw.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'un',
+ ol.interaction.Draw.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Draw.prototype,
+ 'unByKey',
+ ol.interaction.Draw.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Draw.Event.prototype,
+ 'type',
+ ol.interaction.Draw.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.Draw.Event.prototype,
+ 'target',
+ ol.interaction.Draw.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.Draw.Event.prototype,
+ 'preventDefault',
+ ol.interaction.Draw.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.Draw.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.Draw.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'getActive',
+ ol.interaction.Extent.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'getMap',
+ ol.interaction.Extent.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'setActive',
+ ol.interaction.Extent.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'get',
+ ol.interaction.Extent.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'getKeys',
+ ol.interaction.Extent.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'getProperties',
+ ol.interaction.Extent.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'set',
+ ol.interaction.Extent.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'setProperties',
+ ol.interaction.Extent.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'unset',
+ ol.interaction.Extent.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'changed',
+ ol.interaction.Extent.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'dispatchEvent',
+ ol.interaction.Extent.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'getRevision',
+ ol.interaction.Extent.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'on',
+ ol.interaction.Extent.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'once',
+ ol.interaction.Extent.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'un',
+ ol.interaction.Extent.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Extent.prototype,
+ 'unByKey',
+ ol.interaction.Extent.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Extent.Event.prototype,
+ 'type',
+ ol.interaction.Extent.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.Extent.Event.prototype,
+ 'target',
+ ol.interaction.Extent.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.Extent.Event.prototype,
+ 'preventDefault',
+ ol.interaction.Extent.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.Extent.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.Extent.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'getActive',
+ ol.interaction.KeyboardPan.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'getMap',
+ ol.interaction.KeyboardPan.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'setActive',
+ ol.interaction.KeyboardPan.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'get',
+ ol.interaction.KeyboardPan.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'getKeys',
+ ol.interaction.KeyboardPan.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'getProperties',
+ ol.interaction.KeyboardPan.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'set',
+ ol.interaction.KeyboardPan.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'setProperties',
+ ol.interaction.KeyboardPan.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'unset',
+ ol.interaction.KeyboardPan.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'changed',
+ ol.interaction.KeyboardPan.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'dispatchEvent',
+ ol.interaction.KeyboardPan.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'getRevision',
+ ol.interaction.KeyboardPan.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'on',
+ ol.interaction.KeyboardPan.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'once',
+ ol.interaction.KeyboardPan.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'un',
+ ol.interaction.KeyboardPan.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.KeyboardPan.prototype,
+ 'unByKey',
+ ol.interaction.KeyboardPan.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'getActive',
+ ol.interaction.KeyboardZoom.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'getMap',
+ ol.interaction.KeyboardZoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'setActive',
+ ol.interaction.KeyboardZoom.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'get',
+ ol.interaction.KeyboardZoom.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'getKeys',
+ ol.interaction.KeyboardZoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'getProperties',
+ ol.interaction.KeyboardZoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'set',
+ ol.interaction.KeyboardZoom.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'setProperties',
+ ol.interaction.KeyboardZoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'unset',
+ ol.interaction.KeyboardZoom.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'changed',
+ ol.interaction.KeyboardZoom.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'dispatchEvent',
+ ol.interaction.KeyboardZoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'getRevision',
+ ol.interaction.KeyboardZoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'on',
+ ol.interaction.KeyboardZoom.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'once',
+ ol.interaction.KeyboardZoom.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'un',
+ ol.interaction.KeyboardZoom.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.KeyboardZoom.prototype,
+ 'unByKey',
+ ol.interaction.KeyboardZoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'getActive',
+ ol.interaction.Modify.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'getMap',
+ ol.interaction.Modify.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'setActive',
+ ol.interaction.Modify.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'get',
+ ol.interaction.Modify.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'getKeys',
+ ol.interaction.Modify.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'getProperties',
+ ol.interaction.Modify.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'set',
+ ol.interaction.Modify.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'setProperties',
+ ol.interaction.Modify.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'unset',
+ ol.interaction.Modify.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'changed',
+ ol.interaction.Modify.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'dispatchEvent',
+ ol.interaction.Modify.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'getRevision',
+ ol.interaction.Modify.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'on',
+ ol.interaction.Modify.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'once',
+ ol.interaction.Modify.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'un',
+ ol.interaction.Modify.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Modify.prototype,
+ 'unByKey',
+ ol.interaction.Modify.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Modify.Event.prototype,
+ 'type',
+ ol.interaction.Modify.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.Modify.Event.prototype,
+ 'target',
+ ol.interaction.Modify.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.Modify.Event.prototype,
+ 'preventDefault',
+ ol.interaction.Modify.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.Modify.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.Modify.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'getActive',
+ ol.interaction.MouseWheelZoom.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'getMap',
+ ol.interaction.MouseWheelZoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'setActive',
+ ol.interaction.MouseWheelZoom.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'get',
+ ol.interaction.MouseWheelZoom.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'getKeys',
+ ol.interaction.MouseWheelZoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'getProperties',
+ ol.interaction.MouseWheelZoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'set',
+ ol.interaction.MouseWheelZoom.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'setProperties',
+ ol.interaction.MouseWheelZoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'unset',
+ ol.interaction.MouseWheelZoom.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'changed',
+ ol.interaction.MouseWheelZoom.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'dispatchEvent',
+ ol.interaction.MouseWheelZoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'getRevision',
+ ol.interaction.MouseWheelZoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'on',
+ ol.interaction.MouseWheelZoom.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'once',
+ ol.interaction.MouseWheelZoom.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'un',
+ ol.interaction.MouseWheelZoom.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.MouseWheelZoom.prototype,
+ 'unByKey',
+ ol.interaction.MouseWheelZoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'getActive',
+ ol.interaction.PinchRotate.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'getMap',
+ ol.interaction.PinchRotate.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'setActive',
+ ol.interaction.PinchRotate.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'get',
+ ol.interaction.PinchRotate.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'getKeys',
+ ol.interaction.PinchRotate.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'getProperties',
+ ol.interaction.PinchRotate.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'set',
+ ol.interaction.PinchRotate.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'setProperties',
+ ol.interaction.PinchRotate.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'unset',
+ ol.interaction.PinchRotate.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'changed',
+ ol.interaction.PinchRotate.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'dispatchEvent',
+ ol.interaction.PinchRotate.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'getRevision',
+ ol.interaction.PinchRotate.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'on',
+ ol.interaction.PinchRotate.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'once',
+ ol.interaction.PinchRotate.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'un',
+ ol.interaction.PinchRotate.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.PinchRotate.prototype,
+ 'unByKey',
+ ol.interaction.PinchRotate.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'getActive',
+ ol.interaction.PinchZoom.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'getMap',
+ ol.interaction.PinchZoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'setActive',
+ ol.interaction.PinchZoom.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'get',
+ ol.interaction.PinchZoom.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'getKeys',
+ ol.interaction.PinchZoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'getProperties',
+ ol.interaction.PinchZoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'set',
+ ol.interaction.PinchZoom.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'setProperties',
+ ol.interaction.PinchZoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'unset',
+ ol.interaction.PinchZoom.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'changed',
+ ol.interaction.PinchZoom.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'dispatchEvent',
+ ol.interaction.PinchZoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'getRevision',
+ ol.interaction.PinchZoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'on',
+ ol.interaction.PinchZoom.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'once',
+ ol.interaction.PinchZoom.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'un',
+ ol.interaction.PinchZoom.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.PinchZoom.prototype,
+ 'unByKey',
+ ol.interaction.PinchZoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getActive',
+ ol.interaction.Select.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getMap',
+ ol.interaction.Select.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'setActive',
+ ol.interaction.Select.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'get',
+ ol.interaction.Select.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getKeys',
+ ol.interaction.Select.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getProperties',
+ ol.interaction.Select.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'set',
+ ol.interaction.Select.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'setProperties',
+ ol.interaction.Select.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'unset',
+ ol.interaction.Select.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'changed',
+ ol.interaction.Select.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'dispatchEvent',
+ ol.interaction.Select.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'getRevision',
+ ol.interaction.Select.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'on',
+ ol.interaction.Select.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'once',
+ ol.interaction.Select.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'un',
+ ol.interaction.Select.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Select.prototype,
+ 'unByKey',
+ ol.interaction.Select.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'type',
+ ol.interaction.Select.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'target',
+ ol.interaction.Select.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'preventDefault',
+ ol.interaction.Select.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.Select.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.Select.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'getActive',
+ ol.interaction.Snap.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'getMap',
+ ol.interaction.Snap.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'setActive',
+ ol.interaction.Snap.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'get',
+ ol.interaction.Snap.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'getKeys',
+ ol.interaction.Snap.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'getProperties',
+ ol.interaction.Snap.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'set',
+ ol.interaction.Snap.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'setProperties',
+ ol.interaction.Snap.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'unset',
+ ol.interaction.Snap.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'changed',
+ ol.interaction.Snap.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'dispatchEvent',
+ ol.interaction.Snap.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'getRevision',
+ ol.interaction.Snap.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'on',
+ ol.interaction.Snap.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'once',
+ ol.interaction.Snap.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'un',
+ ol.interaction.Snap.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Snap.prototype,
+ 'unByKey',
+ ol.interaction.Snap.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'getActive',
+ ol.interaction.Translate.prototype.getActive);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'getMap',
+ ol.interaction.Translate.prototype.getMap);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'setActive',
+ ol.interaction.Translate.prototype.setActive);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'get',
+ ol.interaction.Translate.prototype.get);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'getKeys',
+ ol.interaction.Translate.prototype.getKeys);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'getProperties',
+ ol.interaction.Translate.prototype.getProperties);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'set',
+ ol.interaction.Translate.prototype.set);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'setProperties',
+ ol.interaction.Translate.prototype.setProperties);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'unset',
+ ol.interaction.Translate.prototype.unset);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'changed',
+ ol.interaction.Translate.prototype.changed);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'dispatchEvent',
+ ol.interaction.Translate.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'getRevision',
+ ol.interaction.Translate.prototype.getRevision);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'on',
+ ol.interaction.Translate.prototype.on);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'once',
+ ol.interaction.Translate.prototype.once);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'un',
+ ol.interaction.Translate.prototype.un);
+
+goog.exportProperty(
+ ol.interaction.Translate.prototype,
+ 'unByKey',
+ ol.interaction.Translate.prototype.unByKey);
+
+goog.exportProperty(
+ ol.interaction.Translate.Event.prototype,
+ 'type',
+ ol.interaction.Translate.Event.prototype.type);
+
+goog.exportProperty(
+ ol.interaction.Translate.Event.prototype,
+ 'target',
+ ol.interaction.Translate.Event.prototype.target);
+
+goog.exportProperty(
+ ol.interaction.Translate.Event.prototype,
+ 'preventDefault',
+ ol.interaction.Translate.Event.prototype.preventDefault);
+
+goog.exportProperty(
+ ol.interaction.Translate.Event.prototype,
+ 'stopPropagation',
+ ol.interaction.Translate.Event.prototype.stopPropagation);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'get',
+ ol.geom.Geometry.prototype.get);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'getKeys',
+ ol.geom.Geometry.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'getProperties',
+ ol.geom.Geometry.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'set',
+ ol.geom.Geometry.prototype.set);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'setProperties',
+ ol.geom.Geometry.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'unset',
+ ol.geom.Geometry.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'changed',
+ ol.geom.Geometry.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'dispatchEvent',
+ ol.geom.Geometry.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'getRevision',
+ ol.geom.Geometry.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'on',
+ ol.geom.Geometry.prototype.on);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'once',
+ ol.geom.Geometry.prototype.once);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'un',
+ ol.geom.Geometry.prototype.un);
+
+goog.exportProperty(
+ ol.geom.Geometry.prototype,
+ 'unByKey',
+ ol.geom.Geometry.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getClosestPoint',
+ ol.geom.SimpleGeometry.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'intersectsCoordinate',
+ ol.geom.SimpleGeometry.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getExtent',
+ ol.geom.SimpleGeometry.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'rotate',
+ ol.geom.SimpleGeometry.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'scale',
+ ol.geom.SimpleGeometry.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'simplify',
+ ol.geom.SimpleGeometry.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'transform',
+ ol.geom.SimpleGeometry.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'get',
+ ol.geom.SimpleGeometry.prototype.get);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getKeys',
+ ol.geom.SimpleGeometry.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getProperties',
+ ol.geom.SimpleGeometry.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'set',
+ ol.geom.SimpleGeometry.prototype.set);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'setProperties',
+ ol.geom.SimpleGeometry.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'unset',
+ ol.geom.SimpleGeometry.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'changed',
+ ol.geom.SimpleGeometry.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'dispatchEvent',
+ ol.geom.SimpleGeometry.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'getRevision',
+ ol.geom.SimpleGeometry.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'on',
+ ol.geom.SimpleGeometry.prototype.on);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'once',
+ ol.geom.SimpleGeometry.prototype.once);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'un',
+ ol.geom.SimpleGeometry.prototype.un);
+
+goog.exportProperty(
+ ol.geom.SimpleGeometry.prototype,
+ 'unByKey',
+ ol.geom.SimpleGeometry.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getFirstCoordinate',
+ ol.geom.Circle.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getLastCoordinate',
+ ol.geom.Circle.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getLayout',
+ ol.geom.Circle.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'rotate',
+ ol.geom.Circle.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'scale',
+ ol.geom.Circle.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getClosestPoint',
+ ol.geom.Circle.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'intersectsCoordinate',
+ ol.geom.Circle.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getExtent',
+ ol.geom.Circle.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'simplify',
+ ol.geom.Circle.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'get',
+ ol.geom.Circle.prototype.get);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getKeys',
+ ol.geom.Circle.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getProperties',
+ ol.geom.Circle.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'set',
+ ol.geom.Circle.prototype.set);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'setProperties',
+ ol.geom.Circle.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'unset',
+ ol.geom.Circle.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'changed',
+ ol.geom.Circle.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'dispatchEvent',
+ ol.geom.Circle.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'getRevision',
+ ol.geom.Circle.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'on',
+ ol.geom.Circle.prototype.on);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'once',
+ ol.geom.Circle.prototype.once);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'un',
+ ol.geom.Circle.prototype.un);
+
+goog.exportProperty(
+ ol.geom.Circle.prototype,
+ 'unByKey',
+ ol.geom.Circle.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getClosestPoint',
+ ol.geom.GeometryCollection.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'intersectsCoordinate',
+ ol.geom.GeometryCollection.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getExtent',
+ ol.geom.GeometryCollection.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'rotate',
+ ol.geom.GeometryCollection.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'scale',
+ ol.geom.GeometryCollection.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'simplify',
+ ol.geom.GeometryCollection.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'transform',
+ ol.geom.GeometryCollection.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'get',
+ ol.geom.GeometryCollection.prototype.get);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getKeys',
+ ol.geom.GeometryCollection.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getProperties',
+ ol.geom.GeometryCollection.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'set',
+ ol.geom.GeometryCollection.prototype.set);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'setProperties',
+ ol.geom.GeometryCollection.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'unset',
+ ol.geom.GeometryCollection.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'changed',
+ ol.geom.GeometryCollection.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'dispatchEvent',
+ ol.geom.GeometryCollection.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'getRevision',
+ ol.geom.GeometryCollection.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'on',
+ ol.geom.GeometryCollection.prototype.on);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'once',
+ ol.geom.GeometryCollection.prototype.once);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'un',
+ ol.geom.GeometryCollection.prototype.un);
+
+goog.exportProperty(
+ ol.geom.GeometryCollection.prototype,
+ 'unByKey',
+ ol.geom.GeometryCollection.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getFirstCoordinate',
+ ol.geom.LinearRing.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getLastCoordinate',
+ ol.geom.LinearRing.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getLayout',
+ ol.geom.LinearRing.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'rotate',
+ ol.geom.LinearRing.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'scale',
+ ol.geom.LinearRing.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getClosestPoint',
+ ol.geom.LinearRing.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'intersectsCoordinate',
+ ol.geom.LinearRing.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getExtent',
+ ol.geom.LinearRing.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'simplify',
+ ol.geom.LinearRing.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'transform',
+ ol.geom.LinearRing.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'get',
+ ol.geom.LinearRing.prototype.get);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getKeys',
+ ol.geom.LinearRing.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getProperties',
+ ol.geom.LinearRing.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'set',
+ ol.geom.LinearRing.prototype.set);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'setProperties',
+ ol.geom.LinearRing.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'unset',
+ ol.geom.LinearRing.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'changed',
+ ol.geom.LinearRing.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'dispatchEvent',
+ ol.geom.LinearRing.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'getRevision',
+ ol.geom.LinearRing.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'on',
+ ol.geom.LinearRing.prototype.on);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'once',
+ ol.geom.LinearRing.prototype.once);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'un',
+ ol.geom.LinearRing.prototype.un);
+
+goog.exportProperty(
+ ol.geom.LinearRing.prototype,
+ 'unByKey',
+ ol.geom.LinearRing.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getFirstCoordinate',
+ ol.geom.LineString.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getLastCoordinate',
+ ol.geom.LineString.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getLayout',
+ ol.geom.LineString.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'rotate',
+ ol.geom.LineString.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'scale',
+ ol.geom.LineString.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getClosestPoint',
+ ol.geom.LineString.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'intersectsCoordinate',
+ ol.geom.LineString.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getExtent',
+ ol.geom.LineString.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'simplify',
+ ol.geom.LineString.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'transform',
+ ol.geom.LineString.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'get',
+ ol.geom.LineString.prototype.get);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getKeys',
+ ol.geom.LineString.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getProperties',
+ ol.geom.LineString.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'set',
+ ol.geom.LineString.prototype.set);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'setProperties',
+ ol.geom.LineString.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'unset',
+ ol.geom.LineString.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'changed',
+ ol.geom.LineString.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'dispatchEvent',
+ ol.geom.LineString.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'getRevision',
+ ol.geom.LineString.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'on',
+ ol.geom.LineString.prototype.on);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'once',
+ ol.geom.LineString.prototype.once);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'un',
+ ol.geom.LineString.prototype.un);
+
+goog.exportProperty(
+ ol.geom.LineString.prototype,
+ 'unByKey',
+ ol.geom.LineString.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getFirstCoordinate',
+ ol.geom.MultiLineString.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getLastCoordinate',
+ ol.geom.MultiLineString.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getLayout',
+ ol.geom.MultiLineString.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'rotate',
+ ol.geom.MultiLineString.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'scale',
+ ol.geom.MultiLineString.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getClosestPoint',
+ ol.geom.MultiLineString.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'intersectsCoordinate',
+ ol.geom.MultiLineString.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getExtent',
+ ol.geom.MultiLineString.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'simplify',
+ ol.geom.MultiLineString.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'transform',
+ ol.geom.MultiLineString.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'get',
+ ol.geom.MultiLineString.prototype.get);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getKeys',
+ ol.geom.MultiLineString.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getProperties',
+ ol.geom.MultiLineString.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'set',
+ ol.geom.MultiLineString.prototype.set);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'setProperties',
+ ol.geom.MultiLineString.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'unset',
+ ol.geom.MultiLineString.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'changed',
+ ol.geom.MultiLineString.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'dispatchEvent',
+ ol.geom.MultiLineString.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'getRevision',
+ ol.geom.MultiLineString.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'on',
+ ol.geom.MultiLineString.prototype.on);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'once',
+ ol.geom.MultiLineString.prototype.once);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'un',
+ ol.geom.MultiLineString.prototype.un);
+
+goog.exportProperty(
+ ol.geom.MultiLineString.prototype,
+ 'unByKey',
+ ol.geom.MultiLineString.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getFirstCoordinate',
+ ol.geom.MultiPoint.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getLastCoordinate',
+ ol.geom.MultiPoint.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getLayout',
+ ol.geom.MultiPoint.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'rotate',
+ ol.geom.MultiPoint.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'scale',
+ ol.geom.MultiPoint.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getClosestPoint',
+ ol.geom.MultiPoint.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'intersectsCoordinate',
+ ol.geom.MultiPoint.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getExtent',
+ ol.geom.MultiPoint.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'simplify',
+ ol.geom.MultiPoint.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'transform',
+ ol.geom.MultiPoint.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'get',
+ ol.geom.MultiPoint.prototype.get);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getKeys',
+ ol.geom.MultiPoint.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getProperties',
+ ol.geom.MultiPoint.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'set',
+ ol.geom.MultiPoint.prototype.set);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'setProperties',
+ ol.geom.MultiPoint.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'unset',
+ ol.geom.MultiPoint.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'changed',
+ ol.geom.MultiPoint.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'dispatchEvent',
+ ol.geom.MultiPoint.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'getRevision',
+ ol.geom.MultiPoint.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'on',
+ ol.geom.MultiPoint.prototype.on);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'once',
+ ol.geom.MultiPoint.prototype.once);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'un',
+ ol.geom.MultiPoint.prototype.un);
+
+goog.exportProperty(
+ ol.geom.MultiPoint.prototype,
+ 'unByKey',
+ ol.geom.MultiPoint.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getFirstCoordinate',
+ ol.geom.MultiPolygon.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getLastCoordinate',
+ ol.geom.MultiPolygon.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getLayout',
+ ol.geom.MultiPolygon.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'rotate',
+ ol.geom.MultiPolygon.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'scale',
+ ol.geom.MultiPolygon.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getClosestPoint',
+ ol.geom.MultiPolygon.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'intersectsCoordinate',
+ ol.geom.MultiPolygon.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getExtent',
+ ol.geom.MultiPolygon.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'simplify',
+ ol.geom.MultiPolygon.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'transform',
+ ol.geom.MultiPolygon.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'get',
+ ol.geom.MultiPolygon.prototype.get);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getKeys',
+ ol.geom.MultiPolygon.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getProperties',
+ ol.geom.MultiPolygon.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'set',
+ ol.geom.MultiPolygon.prototype.set);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'setProperties',
+ ol.geom.MultiPolygon.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'unset',
+ ol.geom.MultiPolygon.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'changed',
+ ol.geom.MultiPolygon.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'dispatchEvent',
+ ol.geom.MultiPolygon.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'getRevision',
+ ol.geom.MultiPolygon.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'on',
+ ol.geom.MultiPolygon.prototype.on);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'once',
+ ol.geom.MultiPolygon.prototype.once);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'un',
+ ol.geom.MultiPolygon.prototype.un);
+
+goog.exportProperty(
+ ol.geom.MultiPolygon.prototype,
+ 'unByKey',
+ ol.geom.MultiPolygon.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getFirstCoordinate',
+ ol.geom.Point.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getLastCoordinate',
+ ol.geom.Point.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getLayout',
+ ol.geom.Point.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'rotate',
+ ol.geom.Point.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'scale',
+ ol.geom.Point.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getClosestPoint',
+ ol.geom.Point.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'intersectsCoordinate',
+ ol.geom.Point.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getExtent',
+ ol.geom.Point.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'simplify',
+ ol.geom.Point.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'transform',
+ ol.geom.Point.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'get',
+ ol.geom.Point.prototype.get);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getKeys',
+ ol.geom.Point.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getProperties',
+ ol.geom.Point.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'set',
+ ol.geom.Point.prototype.set);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'setProperties',
+ ol.geom.Point.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'unset',
+ ol.geom.Point.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'changed',
+ ol.geom.Point.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'dispatchEvent',
+ ol.geom.Point.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'getRevision',
+ ol.geom.Point.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'on',
+ ol.geom.Point.prototype.on);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'once',
+ ol.geom.Point.prototype.once);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'un',
+ ol.geom.Point.prototype.un);
+
+goog.exportProperty(
+ ol.geom.Point.prototype,
+ 'unByKey',
+ ol.geom.Point.prototype.unByKey);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getFirstCoordinate',
+ ol.geom.Polygon.prototype.getFirstCoordinate);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getLastCoordinate',
+ ol.geom.Polygon.prototype.getLastCoordinate);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getLayout',
+ ol.geom.Polygon.prototype.getLayout);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'rotate',
+ ol.geom.Polygon.prototype.rotate);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'scale',
+ ol.geom.Polygon.prototype.scale);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getClosestPoint',
+ ol.geom.Polygon.prototype.getClosestPoint);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'intersectsCoordinate',
+ ol.geom.Polygon.prototype.intersectsCoordinate);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getExtent',
+ ol.geom.Polygon.prototype.getExtent);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'simplify',
+ ol.geom.Polygon.prototype.simplify);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'transform',
+ ol.geom.Polygon.prototype.transform);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'get',
+ ol.geom.Polygon.prototype.get);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getKeys',
+ ol.geom.Polygon.prototype.getKeys);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getProperties',
+ ol.geom.Polygon.prototype.getProperties);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'set',
+ ol.geom.Polygon.prototype.set);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'setProperties',
+ ol.geom.Polygon.prototype.setProperties);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'unset',
+ ol.geom.Polygon.prototype.unset);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'changed',
+ ol.geom.Polygon.prototype.changed);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'dispatchEvent',
+ ol.geom.Polygon.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'getRevision',
+ ol.geom.Polygon.prototype.getRevision);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'on',
+ ol.geom.Polygon.prototype.on);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'once',
+ ol.geom.Polygon.prototype.once);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'un',
+ ol.geom.Polygon.prototype.un);
+
+goog.exportProperty(
+ ol.geom.Polygon.prototype,
+ 'unByKey',
+ ol.geom.Polygon.prototype.unByKey);
+
+goog.exportProperty(
+ ol.format.GML.prototype,
+ 'readFeatures',
+ ol.format.GML.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.GML2.prototype,
+ 'readFeatures',
+ ol.format.GML2.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.format.GML3.prototype,
+ 'readFeatures',
+ ol.format.GML3.prototype.readFeatures);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'get',
+ ol.control.Control.prototype.get);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'getKeys',
+ ol.control.Control.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'getProperties',
+ ol.control.Control.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'set',
+ ol.control.Control.prototype.set);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'setProperties',
+ ol.control.Control.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'unset',
+ ol.control.Control.prototype.unset);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'changed',
+ ol.control.Control.prototype.changed);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'dispatchEvent',
+ ol.control.Control.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'getRevision',
+ ol.control.Control.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'on',
+ ol.control.Control.prototype.on);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'once',
+ ol.control.Control.prototype.once);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'un',
+ ol.control.Control.prototype.un);
+
+goog.exportProperty(
+ ol.control.Control.prototype,
+ 'unByKey',
+ ol.control.Control.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'getMap',
+ ol.control.Attribution.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'setMap',
+ ol.control.Attribution.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'setTarget',
+ ol.control.Attribution.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'get',
+ ol.control.Attribution.prototype.get);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'getKeys',
+ ol.control.Attribution.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'getProperties',
+ ol.control.Attribution.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'set',
+ ol.control.Attribution.prototype.set);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'setProperties',
+ ol.control.Attribution.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'unset',
+ ol.control.Attribution.prototype.unset);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'changed',
+ ol.control.Attribution.prototype.changed);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'dispatchEvent',
+ ol.control.Attribution.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'getRevision',
+ ol.control.Attribution.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'on',
+ ol.control.Attribution.prototype.on);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'once',
+ ol.control.Attribution.prototype.once);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'un',
+ ol.control.Attribution.prototype.un);
+
+goog.exportProperty(
+ ol.control.Attribution.prototype,
+ 'unByKey',
+ ol.control.Attribution.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'getMap',
+ ol.control.FullScreen.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'setMap',
+ ol.control.FullScreen.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'setTarget',
+ ol.control.FullScreen.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'get',
+ ol.control.FullScreen.prototype.get);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'getKeys',
+ ol.control.FullScreen.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'getProperties',
+ ol.control.FullScreen.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'set',
+ ol.control.FullScreen.prototype.set);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'setProperties',
+ ol.control.FullScreen.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'unset',
+ ol.control.FullScreen.prototype.unset);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'changed',
+ ol.control.FullScreen.prototype.changed);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'dispatchEvent',
+ ol.control.FullScreen.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'getRevision',
+ ol.control.FullScreen.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'on',
+ ol.control.FullScreen.prototype.on);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'once',
+ ol.control.FullScreen.prototype.once);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'un',
+ ol.control.FullScreen.prototype.un);
+
+goog.exportProperty(
+ ol.control.FullScreen.prototype,
+ 'unByKey',
+ ol.control.FullScreen.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'getMap',
+ ol.control.MousePosition.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'setMap',
+ ol.control.MousePosition.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'setTarget',
+ ol.control.MousePosition.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'get',
+ ol.control.MousePosition.prototype.get);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'getKeys',
+ ol.control.MousePosition.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'getProperties',
+ ol.control.MousePosition.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'set',
+ ol.control.MousePosition.prototype.set);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'setProperties',
+ ol.control.MousePosition.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'unset',
+ ol.control.MousePosition.prototype.unset);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'changed',
+ ol.control.MousePosition.prototype.changed);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'dispatchEvent',
+ ol.control.MousePosition.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'getRevision',
+ ol.control.MousePosition.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'on',
+ ol.control.MousePosition.prototype.on);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'once',
+ ol.control.MousePosition.prototype.once);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'un',
+ ol.control.MousePosition.prototype.un);
+
+goog.exportProperty(
+ ol.control.MousePosition.prototype,
+ 'unByKey',
+ ol.control.MousePosition.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getMap',
+ ol.control.OverviewMap.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'setMap',
+ ol.control.OverviewMap.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'setTarget',
+ ol.control.OverviewMap.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'get',
+ ol.control.OverviewMap.prototype.get);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getKeys',
+ ol.control.OverviewMap.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getProperties',
+ ol.control.OverviewMap.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'set',
+ ol.control.OverviewMap.prototype.set);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'setProperties',
+ ol.control.OverviewMap.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'unset',
+ ol.control.OverviewMap.prototype.unset);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'changed',
+ ol.control.OverviewMap.prototype.changed);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'dispatchEvent',
+ ol.control.OverviewMap.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'getRevision',
+ ol.control.OverviewMap.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'on',
+ ol.control.OverviewMap.prototype.on);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'once',
+ ol.control.OverviewMap.prototype.once);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'un',
+ ol.control.OverviewMap.prototype.un);
+
+goog.exportProperty(
+ ol.control.OverviewMap.prototype,
+ 'unByKey',
+ ol.control.OverviewMap.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'getMap',
+ ol.control.Rotate.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'setMap',
+ ol.control.Rotate.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'setTarget',
+ ol.control.Rotate.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'get',
+ ol.control.Rotate.prototype.get);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'getKeys',
+ ol.control.Rotate.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'getProperties',
+ ol.control.Rotate.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'set',
+ ol.control.Rotate.prototype.set);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'setProperties',
+ ol.control.Rotate.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'unset',
+ ol.control.Rotate.prototype.unset);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'changed',
+ ol.control.Rotate.prototype.changed);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'dispatchEvent',
+ ol.control.Rotate.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'getRevision',
+ ol.control.Rotate.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'on',
+ ol.control.Rotate.prototype.on);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'once',
+ ol.control.Rotate.prototype.once);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'un',
+ ol.control.Rotate.prototype.un);
+
+goog.exportProperty(
+ ol.control.Rotate.prototype,
+ 'unByKey',
+ ol.control.Rotate.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'getMap',
+ ol.control.ScaleLine.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'setMap',
+ ol.control.ScaleLine.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'setTarget',
+ ol.control.ScaleLine.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'get',
+ ol.control.ScaleLine.prototype.get);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'getKeys',
+ ol.control.ScaleLine.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'getProperties',
+ ol.control.ScaleLine.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'set',
+ ol.control.ScaleLine.prototype.set);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'setProperties',
+ ol.control.ScaleLine.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'unset',
+ ol.control.ScaleLine.prototype.unset);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'changed',
+ ol.control.ScaleLine.prototype.changed);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'dispatchEvent',
+ ol.control.ScaleLine.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'getRevision',
+ ol.control.ScaleLine.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'on',
+ ol.control.ScaleLine.prototype.on);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'once',
+ ol.control.ScaleLine.prototype.once);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'un',
+ ol.control.ScaleLine.prototype.un);
+
+goog.exportProperty(
+ ol.control.ScaleLine.prototype,
+ 'unByKey',
+ ol.control.ScaleLine.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'getMap',
+ ol.control.Zoom.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'setMap',
+ ol.control.Zoom.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'setTarget',
+ ol.control.Zoom.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'get',
+ ol.control.Zoom.prototype.get);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'getKeys',
+ ol.control.Zoom.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'getProperties',
+ ol.control.Zoom.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'set',
+ ol.control.Zoom.prototype.set);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'setProperties',
+ ol.control.Zoom.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'unset',
+ ol.control.Zoom.prototype.unset);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'changed',
+ ol.control.Zoom.prototype.changed);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'dispatchEvent',
+ ol.control.Zoom.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'getRevision',
+ ol.control.Zoom.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'on',
+ ol.control.Zoom.prototype.on);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'once',
+ ol.control.Zoom.prototype.once);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'un',
+ ol.control.Zoom.prototype.un);
+
+goog.exportProperty(
+ ol.control.Zoom.prototype,
+ 'unByKey',
+ ol.control.Zoom.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'getMap',
+ ol.control.ZoomSlider.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'setMap',
+ ol.control.ZoomSlider.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'setTarget',
+ ol.control.ZoomSlider.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'get',
+ ol.control.ZoomSlider.prototype.get);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'getKeys',
+ ol.control.ZoomSlider.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'getProperties',
+ ol.control.ZoomSlider.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'set',
+ ol.control.ZoomSlider.prototype.set);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'setProperties',
+ ol.control.ZoomSlider.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'unset',
+ ol.control.ZoomSlider.prototype.unset);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'changed',
+ ol.control.ZoomSlider.prototype.changed);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'dispatchEvent',
+ ol.control.ZoomSlider.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'getRevision',
+ ol.control.ZoomSlider.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'on',
+ ol.control.ZoomSlider.prototype.on);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'once',
+ ol.control.ZoomSlider.prototype.once);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'un',
+ ol.control.ZoomSlider.prototype.un);
+
+goog.exportProperty(
+ ol.control.ZoomSlider.prototype,
+ 'unByKey',
+ ol.control.ZoomSlider.prototype.unByKey);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'getMap',
+ ol.control.ZoomToExtent.prototype.getMap);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'setMap',
+ ol.control.ZoomToExtent.prototype.setMap);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'setTarget',
+ ol.control.ZoomToExtent.prototype.setTarget);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'get',
+ ol.control.ZoomToExtent.prototype.get);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'getKeys',
+ ol.control.ZoomToExtent.prototype.getKeys);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'getProperties',
+ ol.control.ZoomToExtent.prototype.getProperties);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'set',
+ ol.control.ZoomToExtent.prototype.set);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'setProperties',
+ ol.control.ZoomToExtent.prototype.setProperties);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'unset',
+ ol.control.ZoomToExtent.prototype.unset);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'changed',
+ ol.control.ZoomToExtent.prototype.changed);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'dispatchEvent',
+ ol.control.ZoomToExtent.prototype.dispatchEvent);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'getRevision',
+ ol.control.ZoomToExtent.prototype.getRevision);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'on',
+ ol.control.ZoomToExtent.prototype.on);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'once',
+ ol.control.ZoomToExtent.prototype.once);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'un',
+ ol.control.ZoomToExtent.prototype.un);
+
+goog.exportProperty(
+ ol.control.ZoomToExtent.prototype,
+ 'unByKey',
+ ol.control.ZoomToExtent.prototype.unByKey);
+ol.VERSION = 'v3.20.1';
+OPENLAYERS.ol = ol;
+
+ return OPENLAYERS.ol;
+}));
diff --git a/app/assets/javascripts/OpenLayers/ol.js b/app/assets/javascripts/OpenLayers/ol.js
new file mode 100644
index 000000000..616099c36
--- /dev/null
+++ b/app/assets/javascripts/OpenLayers/ol.js
@@ -0,0 +1,1008 @@
+// OpenLayers 3. See https://openlayers.org/
+// License: https://raw.githubusercontent.com/openlayers/ol3/master/LICENSE.md
+// Version: v3.20.1
+;(function (root, factory) {
+ if (typeof exports === "object") {
+ module.exports = factory();
+ } else if (typeof define === "function" && define.amd) {
+ define([], factory);
+ } else {
+ root.ol = factory();
+ }
+}(this, function () {
+ var OPENLAYERS = {};
+ var k,aa=this;function t(a,b){var c=a.split("."),d=OPENLAYERS||aa;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d[e]?d=d[e]:d=d[e]={}:d[e]=b};var ba,da;function v(a,b){a.prototype=Object.create(b.prototype);a.prototype.constructor=a}function ea(){}function x(a){return a.ao||(a.ao=++fa)}var fa=0;function ga(a){this.message="Assertion failed. See https://openlayers.org/en/v3.20.1/doc/errors/#"+a+" for details.";this.code=a;this.name="AssertionError"}v(ga,Error);function ha(a,b){if(!a)throw new ga(b);};function ia(a,b,c){return Math.min(Math.max(a,b),c)}var ja=function(){var a;"cosh"in Math?a=Math.cosh:a=function(a){a=Math.exp(a);return(a+1/a)/2};return a}();function ka(a){ha(0<a,29);return Math.pow(2,Math.ceil(Math.log(a)/Math.LN2))}function la(a,b,c,d,e,f){var g=e-c,h=f-d;if(0!==g||0!==h){var l=((a-c)*g+(b-d)*h)/(g*g+h*h);1<l?(c=e,d=f):0<l&&(c+=g*l,d+=h*l)}return ma(a,b,c,d)}function ma(a,b,c,d){a=c-a;b=d-b;return a*a+b*b}function na(a){return a*Math.PI/180}
+function oa(a,b){var c=a%b;return 0>c*b?c+b:c}function pa(a,b,c){return a+c*(b-a)};function qa(a){return function(b){if(b)return[ia(b[0],a[0],a[2]),ia(b[1],a[1],a[3])]}}function ra(a){return a};function sa(a,b,c){this.center=a;this.resolution=b;this.rotation=c};var ta="function"===typeof Object.assign?Object.assign:function(a,b){if(!a||!a)throw new TypeError("Cannot convert undefined or null to object");for(var c=Object(a),d=1,e=arguments.length;d<e;++d){var f=arguments[d];if(void 0!==f&&null!==f)for(var g in f)f.hasOwnProperty(g)&&(c[g]=f[g])}return c};function ua(a){for(var b in a)delete a[b]}function va(a){var b=[],c;for(c in a)b.push(a[c]);return b}function wa(a){for(var b in a)return!1;return!b};function xa(a){function b(b){var d=a.listener,e=a.xg||a.target;a.zg&&ya(a);return d.call(e,b)}return a.yg=b}function za(a,b,c,d){for(var e,f=0,g=a.length;f<g;++f)if(e=a[f],e.listener===b&&e.xg===c)return d&&(e.deleteIndex=f),e}function Aa(a,b){var c=a.eb;return c?c[b]:void 0}function Ba(a){var b=a.eb;b||(b=a.eb={});return b}
+function Ca(a,b){var c=Aa(a,b);if(c){for(var d=0,e=c.length;d<e;++d)a.removeEventListener(b,c[d].yg),ua(c[d]);c.length=0;if(c=a.eb)delete c[b],0===Object.keys(c).length&&delete a.eb}}function B(a,b,c,d,e){var f=Ba(a),g=f[b];g||(g=f[b]=[]);(f=za(g,c,d,!1))?e||(f.zg=!1):(f={xg:d,zg:!!e,listener:c,target:a,type:b},a.addEventListener(b,xa(f)),g.push(f));return f}function Da(a,b,c,d){return B(a,b,c,d,!0)}function Ea(a,b,c,d){(a=Aa(a,b))&&(c=za(a,c,d,!0))&&ya(c)}
+function ya(a){if(a&&a.target){a.target.removeEventListener(a.type,a.yg);var b=Aa(a.target,a.type);if(b){var c="deleteIndex"in a?a.deleteIndex:b.indexOf(a);-1!==c&&b.splice(c,1);0===b.length&&Ca(a.target,a.type)}ua(a)}}function Fa(a){var b=Ba(a),c;for(c in b)Ca(a,c)};function Ga(){}Ga.prototype.zb=!1;function Ha(a){a.zb||(a.zb=!0,a.oa())}Ga.prototype.oa=ea;function Ia(a){this.type=a;this.target=null}Ia.prototype.preventDefault=Ia.prototype.stopPropagation=function(){this.xo=!0};function Ka(a){a.stopPropagation()};function Na(){this.Qa={};this.va={};this.ra={}}v(Na,Ga);Na.prototype.addEventListener=function(a,b){var c=this.ra[a];c||(c=this.ra[a]=[]);-1===c.indexOf(b)&&c.push(b)};
+Na.prototype.b=function(a){var b="string"===typeof a?new Ia(a):a;a=b.type;b.target=this;var c=this.ra[a],d;if(c){a in this.va||(this.va[a]=0,this.Qa[a]=0);++this.va[a];for(var e=0,f=c.length;e<f;++e)if(!1===c[e].call(this,b)||b.xo){d=!1;break}--this.va[a];if(0===this.va[a]){b=this.Qa[a];for(delete this.Qa[a];b--;)this.removeEventListener(a,ea);delete this.va[a]}return d}};Na.prototype.oa=function(){Fa(this)};function Oa(a,b){return b?b in a.ra:0<Object.keys(a.ra).length}
+Na.prototype.removeEventListener=function(a,b){var c=this.ra[a];if(c){var d=c.indexOf(b);a in this.Qa?(c[d]=ea,++this.Qa[a]):(c.splice(d,1),0===c.length&&delete this.ra[a])}};function Pa(){Na.call(this);this.g=0}v(Pa,Na);function Qa(a){if(Array.isArray(a))for(var b=0,c=a.length;b<c;++b)ya(a[b]);else ya(a)}k=Pa.prototype;k.s=function(){++this.g;this.b("change")};k.M=function(){return this.g};k.J=function(a,b,c){if(Array.isArray(a)){for(var d=a.length,e=Array(d),f=0;f<d;++f)e[f]=B(this,a[f],b,c);return e}return B(this,a,b,c)};k.N=function(a,b,c){if(Array.isArray(a)){for(var d=a.length,e=Array(d),f=0;f<d;++f)e[f]=Da(this,a[f],b,c);return e}return Da(this,a,b,c)};
+k.K=function(a,b,c){if(Array.isArray(a))for(var d=0,e=a.length;d<e;++d)Ea(this,a[d],b,c);else Ea(this,a,b,c)};k.O=Qa;function Sa(a){Pa.call(this);x(this);this.H={};void 0!==a&&this.I(a)}v(Sa,Pa);var Ta={};function Ua(a){return Ta.hasOwnProperty(a)?Ta[a]:Ta[a]="change:"+a}k=Sa.prototype;k.get=function(a){var b;this.H.hasOwnProperty(a)&&(b=this.H[a]);return b};k.S=function(){return Object.keys(this.H)};k.R=function(){return ta({},this.H)};function Va(a,b,c){var d;d=Ua(b);a.b(new Wa(d,b,c));d=Xa;a.b(new Wa(d,b,c))}k.set=function(a,b,c){c?this.H[a]=b:(c=this.H[a],this.H[a]=b,c!==b&&Va(this,a,c))};
+k.I=function(a,b){for(var c in a)this.set(c,a[c],b)};k.T=function(a,b){if(a in this.H){var c=this.H[a];delete this.H[a];b||Va(this,a,c)}};var Xa="propertychange";function Wa(a,b,c){Ia.call(this,a);this.key=b;this.oldValue=c}v(Wa,Ia);function Ya(a,b){return a>b?1:a<b?-1:0}function Za(a,b){return 0<=a.indexOf(b)}function $a(a,b,c){var d=a.length;if(a[0]<=b)return 0;if(!(b<=a[d-1]))if(0<c)for(c=1;c<d;++c){if(a[c]<b)return c-1}else if(0>c)for(c=1;c<d;++c){if(a[c]<=b)return c}else for(c=1;c<d;++c){if(a[c]==b)return c;if(a[c]<b)return a[c-1]-b<b-a[c]?c-1:c}return d-1}function ab(a){return a.reduce(function(a,c){return Array.isArray(c)?a.concat(ab(c)):a.concat(c)},[])}
+function bb(a,b){var c,d=Array.isArray(b)?b:[b],e=d.length;for(c=0;c<e;c++)a[a.length]=d[c]}function cb(a,b){for(var c=a.length>>>0,d,e=0;e<c;e++)if(d=a[e],b(d,e,a))return d;return null}function db(a,b){var c=a.length;if(c!==b.length)return!1;for(var d=0;d<c;d++)if(a[d]!==b[d])return!1;return!0}function eb(a){var b=fb,c=a.length,d=Array(a.length),e;for(e=0;e<c;e++)d[e]={index:e,value:a[e]};d.sort(function(a,c){return b(a.value,c.value)||a.index-c.index});for(e=0;e<a.length;e++)a[e]=d[e].value}
+function gb(a,b){var c;return a.every(function(d,e){c=e;return!b(d,e,a)})?-1:c}function hb(a,b){var c=b||Ya;return a.every(function(b,e){if(0===e)return!0;var f=c(a[e-1],b);return!(0<f||0===f)})};function ib(a){return function(b,c,d){if(void 0!==b)return b=$a(a,b,d),b=ia(b+c,0,a.length-1),c=Math.floor(b),b!=c&&c<a.length-1?a[c]/Math.pow(a[c]/a[c+1],b-c):a[c]}}function jb(a,b,c){return function(d,e,f){if(void 0!==d)return d=Math.max(Math.floor(Math.log(b/d)/Math.log(a)+(-f/2+.5))+e,0),void 0!==c&&(d=Math.min(d,c)),b/Math.pow(a,d)}};function lb(a){if(void 0!==a)return 0}function mb(a,b){if(void 0!==a)return a+b}function nb(a){var b=2*Math.PI/a;return function(a,d){if(void 0!==a)return a=Math.floor((a+d)/b+.5)*b}}function ob(){var a=na(5);return function(b,c){if(void 0!==b)return Math.abs(b+c)<=a?0:b+c}};function pb(a,b){var c=void 0!==b?a.toFixed(b):""+a,d=c.indexOf("."),d=-1===d?c.length:d;return 2<d?c:Array(3-d).join("0")+c}function qb(a){a=(""+a).split(".");for(var b=["1","3"],c=0;c<Math.max(a.length,b.length);c++){var d=parseInt(a[c]||"0",10),e=parseInt(b[c]||"0",10);if(d>e)return 1;if(e>d)return-1}return 0};function rb(a,b){a[0]+=b[0];a[1]+=b[1];return a}function sb(a,b){var c=a[0],d=a[1],e=b[0],f=b[1],g=e[0],e=e[1],h=f[0],f=f[1],l=h-g,m=f-e,c=0===l&&0===m?0:(l*(c-g)+m*(d-e))/(l*l+m*m||0);0>=c||(1<=c?(g=h,e=f):(g+=c*l,e+=c*m));return[g,e]}function tb(a,b,c){a=oa(a+180,360)-180;var d=Math.abs(3600*a);return Math.floor(d/3600)+"\u00b0 "+pb(Math.floor(d/60%60))+"\u2032 "+pb(d%60,c||0)+"\u2033 "+b.charAt(0>a?1:0)}
+function ub(a,b,c){return a?b.replace("{x}",a[0].toFixed(c)).replace("{y}",a[1].toFixed(c)):""}function vb(a,b){for(var c=!0,d=a.length-1;0<=d;--d)if(a[d]!=b[d]){c=!1;break}return c}function wb(a,b){var c=Math.cos(b),d=Math.sin(b),e=a[1]*c+a[0]*d;a[0]=a[0]*c-a[1]*d;a[1]=e;return a}function xb(a,b){a[0]*=b;a[1]*=b}function yb(a,b){var c=a[0]-b[0],d=a[1]-b[1];return c*c+d*d}function zb(a,b){return yb(a,sb(a,b))}function Ab(a,b){return ub(a,"{x}, {y}",b)};function Bb(a){return Math.pow(a,3)}function Cb(a){return 1-Bb(1-a)}function Db(a){return 3*a*a-2*a*a*a}function Eb(a){return a}function Fb(a){return.5>a?Db(2*a):1-Db(2*(a-.5))};function Gb(a){for(var b=Hb(),c=0,d=a.length;c<d;++c)Ib(b,a[c]);return b}function Jb(a,b,c){return c?(c[0]=a[0]-b,c[1]=a[1]-b,c[2]=a[2]+b,c[3]=a[3]+b,c):[a[0]-b,a[1]-b,a[2]+b,a[3]+b]}function Kb(a,b){return b?(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b):a.slice()}function Lb(a,b,c){b=b<a[0]?a[0]-b:a[2]<b?b-a[2]:0;a=c<a[1]?a[1]-c:a[3]<c?c-a[3]:0;return b*b+a*a}function Mb(a,b){return Nb(a,b[0],b[1])}function Ob(a,b){return a[0]<=b[0]&&b[2]<=a[2]&&a[1]<=b[1]&&b[3]<=a[3]}
+function Nb(a,b,c){return a[0]<=b&&b<=a[2]&&a[1]<=c&&c<=a[3]}function Qb(a,b){var c=a[1],d=a[2],e=a[3],f=b[0],g=b[1],h=0;f<a[0]?h|=16:f>d&&(h|=4);g<c?h|=8:g>e&&(h|=2);0===h&&(h=1);return h}function Hb(){return[Infinity,Infinity,-Infinity,-Infinity]}function Rb(a,b,c,d,e){return e?(e[0]=a,e[1]=b,e[2]=c,e[3]=d,e):[a,b,c,d]}function Sb(a,b){var c=a[0],d=a[1];return Rb(c,d,c,d,b)}function Tb(a,b,c,d,e){e=Rb(Infinity,Infinity,-Infinity,-Infinity,e);return Ub(e,a,b,c,d)}
+function Vb(a,b){return a[0]==b[0]&&a[2]==b[2]&&a[1]==b[1]&&a[3]==b[3]}function Wb(a,b){b[0]<a[0]&&(a[0]=b[0]);b[2]>a[2]&&(a[2]=b[2]);b[1]<a[1]&&(a[1]=b[1]);b[3]>a[3]&&(a[3]=b[3]);return a}function Ib(a,b){b[0]<a[0]&&(a[0]=b[0]);b[0]>a[2]&&(a[2]=b[0]);b[1]<a[1]&&(a[1]=b[1]);b[1]>a[3]&&(a[3]=b[1])}function Ub(a,b,c,d,e){for(;c<d;c+=e){var f=a,g=b[c],h=b[c+1];f[0]=Math.min(f[0],g);f[1]=Math.min(f[1],h);f[2]=Math.max(f[2],g);f[3]=Math.max(f[3],h)}return a}
+function Xb(a,b,c){var d;return(d=b.call(c,Yb(a)))||(d=b.call(c,Zb(a)))||(d=b.call(c,$b(a)))?d:(d=b.call(c,ac(a)))?d:!1}function bc(a){var b=0;cc(a)||(b=dc(a)*ec(a));return b}function Yb(a){return[a[0],a[1]]}function Zb(a){return[a[2],a[1]]}function gc(a){return[(a[0]+a[2])/2,(a[1]+a[3])/2]}
+function hc(a,b,c,d,e){var f=b*d[0]/2;d=b*d[1]/2;b=Math.cos(c);var g=Math.sin(c);c=f*b;f*=g;b*=d;var h=d*g,l=a[0],m=a[1];a=l-c+h;d=l-c-h;g=l+c-h;c=l+c+h;var h=m-f-b,l=m-f+b,p=m+f+b,f=m+f-b;return Rb(Math.min(a,d,g,c),Math.min(h,l,p,f),Math.max(a,d,g,c),Math.max(h,l,p,f),e)}function ec(a){return a[3]-a[1]}function ic(a,b,c){c=c?c:Hb();jc(a,b)&&(c[0]=a[0]>b[0]?a[0]:b[0],c[1]=a[1]>b[1]?a[1]:b[1],c[2]=a[2]<b[2]?a[2]:b[2],c[3]=a[3]<b[3]?a[3]:b[3]);return c}function ac(a){return[a[0],a[3]]}
+function $b(a){return[a[2],a[3]]}function dc(a){return a[2]-a[0]}function jc(a,b){return a[0]<=b[2]&&a[2]>=b[0]&&a[1]<=b[3]&&a[3]>=b[1]}function cc(a){return a[2]<a[0]||a[3]<a[1]}function kc(a,b){var c=(a[2]-a[0])/2*(b-1),d=(a[3]-a[1])/2*(b-1);a[0]-=c;a[2]+=c;a[1]-=d;a[3]+=d}
+function lc(a,b,c){a=[a[0],a[1],a[0],a[3],a[2],a[1],a[2],a[3]];b(a,a,2);var d=[a[0],a[2],a[4],a[6]],e=[a[1],a[3],a[5],a[7]];b=Math.min.apply(null,d);a=Math.min.apply(null,e);d=Math.max.apply(null,d);e=Math.max.apply(null,e);return Rb(b,a,d,e,c)};function mc(){return!0}function nc(){return!1};/*
+
+ Latitude/longitude spherical geodesy formulae taken from
+ http://www.movable-type.co.uk/scripts/latlong.html
+ Licensed under CC-BY-3.0.
+*/
+function oc(a){this.radius=a}oc.prototype.a=function(a){for(var b=0,c=a.length,d=a[c-1][0],e=a[c-1][1],f=0;f<c;f++)var g=a[f][0],h=a[f][1],b=b+na(g-d)*(2+Math.sin(na(e))+Math.sin(na(h))),d=g,e=h;return b*this.radius*this.radius/2};oc.prototype.b=function(a,b){var c=na(a[1]),d=na(b[1]),e=(d-c)/2,f=na(b[0]-a[0])/2,c=Math.sin(e)*Math.sin(e)+Math.sin(f)*Math.sin(f)*Math.cos(c)*Math.cos(d);return 2*this.radius*Math.atan2(Math.sqrt(c),Math.sqrt(1-c))};
+oc.prototype.offset=function(a,b,c){var d=na(a[1]);b/=this.radius;var e=Math.asin(Math.sin(d)*Math.cos(b)+Math.cos(d)*Math.sin(b)*Math.cos(c));return[180*(na(a[0])+Math.atan2(Math.sin(c)*Math.sin(b)*Math.cos(d),Math.cos(b)-Math.sin(d)*Math.sin(e)))/Math.PI,180*e/Math.PI]};var pc=new oc(6370997);var qc={};qc.degrees=2*Math.PI*pc.radius/360;qc.ft=.3048;qc.m=1;qc["us-ft"]=1200/3937;var rc=null;function sc(a){this.hb=a.code;this.c=a.units;this.f=void 0!==a.extent?a.extent:null;this.i=void 0!==a.worldExtent?a.worldExtent:null;this.b=void 0!==a.axisOrientation?a.axisOrientation:"enu";this.g=void 0!==a.global?a.global:!1;this.a=!(!this.g||!this.f);this.l=a.getPointResolution;this.j=null;this.o=a.metersPerUnit;var b=a.code,c=rc||window.proj4;"function"==typeof c&&(b=c.defs(b),void 0!==b&&(void 0!==b.axis&&void 0===a.axisOrientation&&(this.b=b.axis),void 0===a.metersPerUnit&&(this.o=b.to_meter),
+void 0===a.units&&(this.c=b.units)))}k=sc.prototype;k.Zj=function(){return this.hb};k.G=function(){return this.f};k.Eb=function(){return this.c};k.ic=function(){return this.o||qc[this.c]};k.Jk=function(){return this.i};k.rl=function(){return this.g};k.hp=function(a){this.g=a;this.a=!(!a||!this.f)};k.Sm=function(a){this.f=a;this.a=!(!this.g||!a)};k.op=function(a){this.i=a};k.gp=function(a){this.l=a};var tc={};var uc={};function vc(a,b,c){a=a.hb;b=b.hb;a in uc||(uc[a]={});uc[a][b]=c}function wc(a,b){var c;a in uc&&b in uc[a]&&(c=uc[a][b]);return c};function xc(a,b,c){var d=a.l;d?b=d(b,c):"degrees"!=a.Eb()&&(d=yc(a,zc("EPSG:4326")),b=[c[0]-b/2,c[1],c[0]+b/2,c[1],c[0],c[1]-b/2,c[0],c[1]+b/2],b=d(b,b,2),b=(pc.b(b.slice(0,2),b.slice(2,4))+pc.b(b.slice(4,6),b.slice(6,8)))/2,a=a.ic(),void 0!==a&&(b/=a));return b}function Ac(a){Bc(a);a.forEach(function(b){a.forEach(function(a){b!==a&&vc(b,a,Cc)})})}function Dc(){var a=Ec,b=Fc,c=Gc;Hc.forEach(function(d){a.forEach(function(a){vc(d,a,b);vc(a,d,c)})})}function Ic(a){tc[a.hb]=a;vc(a,a,Cc)}
+function Bc(a){var b=[];a.forEach(function(a){b.push(Ic(a))})}function Jc(a){return a?"string"===typeof a?zc(a):a:zc("EPSG:3857")}function Kc(a,b,c,d){a=zc(a);b=zc(b);vc(a,b,Lc(c));vc(b,a,Lc(d))}function Lc(a){return function(b,c,d){var e=b.length;d=void 0!==d?d:2;c=void 0!==c?c:Array(e);var f,g;for(g=0;g<e;g+=d)for(f=a([b[g],b[g+1]]),c[g]=f[0],c[g+1]=f[1],f=d-1;2<=f;--f)c[g+f]=b[g+f];return c}}
+function zc(a){var b=null;if(a instanceof sc)b=a;else if("string"===typeof a){var b=tc[a]||null,c=rc||window.proj4;b||"function"!=typeof c||void 0===c.defs(a)||(b=new sc({code:a}),Ic(b))}return b}function Mc(a,b){if(a===b)return!0;var c=a.Eb()===b.Eb();return a.hb===b.hb?c:yc(a,b)===Cc&&c}function Nc(a,b){var c=zc(a),d=zc(b);return yc(c,d)}
+function yc(a,b){var c=a.hb,d=b.hb,e=wc(c,d);if(!e){var f=rc||window.proj4;if("function"==typeof f){var g=f.defs(c),h=f.defs(d);void 0!==g&&void 0!==h&&(g===h?Ac([b,a]):(e=f(d,c),Kc(b,a,e.forward,e.inverse)),e=wc(c,d))}}e||(e=Oc);return e}function Oc(a,b){if(void 0!==b&&a!==b){for(var c=0,d=a.length;c<d;++c)b[c]=a[c];a=b}return a}function Cc(a,b){var c;if(void 0!==b){c=0;for(var d=a.length;c<d;++c)b[c]=a[c];c=b}else c=a.slice();return c}function Pc(a,b,c){return Nc(b,c)(a,void 0,a.length)}
+function Qc(a,b,c){b=Nc(b,c);return lc(a,b)};function Rc(){Sa.call(this);this.v=Hb();this.u=-1;this.i={};this.o=this.j=0}v(Rc,Sa);k=Rc.prototype;k.Cb=function(a,b){var c=b?b:[NaN,NaN];this.Ab(a[0],a[1],c,Infinity);return c};k.mb=function(a){return this.Hc(a[0],a[1])};k.Hc=nc;k.G=function(a){this.u!=this.g&&(this.v=this.Yd(this.v),this.u=this.g);var b=this.v;a?(a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3]):a=b;return a};k.Jb=function(a){return this.Bd(a*a)};k.ob=function(a,b){this.sc(Nc(a,b));return this};function Sc(a,b,c,d,e,f){for(var g=f?f:[],h=0;b<c;b+=d){var l=a[b],m=a[b+1];g[h++]=e[0]*l+e[2]*m+e[4];g[h++]=e[1]*l+e[3]*m+e[5]}f&&g.length!=h&&(g.length=h);return g}function Tc(a,b,c,d,e,f){var g=f?f:[],h=0,l,m;for(l=0;l<b;l+=c)for(g[h++]=a[l]+d,g[h++]=a[l+1]+e,m=l+2;m<l+c;++m)g[h++]=a[m];f&&g.length!=h&&(g.length=h);return g};function Uc(){Rc.call(this);this.ka="XY";this.a=2;this.B=null}v(Uc,Rc);function Vc(a){var b;"XY"==a?b=2:"XYZ"==a||"XYM"==a?b=3:"XYZM"==a&&(b=4);return b}k=Uc.prototype;k.Hc=nc;k.Yd=function(a){return Tb(this.B,0,this.B.length,this.a,a)};k.Rb=function(){return this.B.slice(0,this.a)};k.ia=function(){return this.B};k.Sb=function(){return this.B.slice(this.B.length-this.a)};k.Tb=function(){return this.ka};
+k.Bd=function(a){this.o!=this.g&&(ua(this.i),this.j=0,this.o=this.g);if(0>a||0!==this.j&&a<=this.j)return this;var b=a.toString();if(this.i.hasOwnProperty(b))return this.i[b];var c=this.$c(a);if(c.ia().length<this.B.length)return this.i[b]=c;this.j=a;return this};k.$c=function(){return this};k.pa=function(){return this.a};function Xc(a,b,c){a.a=Vc(b);a.ka=b;a.B=c}
+function Yc(a,b,c,d){if(b)c=Vc(b);else{for(b=0;b<d;++b){if(0===c.length){a.ka="XY";a.a=2;return}c=c[0]}c=c.length;var e;2==c?e="XY":3==c?e="XYZ":4==c&&(e="XYZM");b=e}a.ka=b;a.a=c}k.sc=function(a){this.B&&(a(this.B,this.B,this.a),this.s())};
+k.rotate=function(a,b){var c=this.ia();if(c){for(var d=c.length,e=this.pa(),f=c?c:[],g=Math.cos(a),h=Math.sin(a),l=b[0],m=b[1],p=0,n=0;n<d;n+=e){var q=c[n]-l,r=c[n+1]-m;f[p++]=l+q*g-r*h;f[p++]=m+q*h+r*g;for(q=n+2;q<n+e;++q)f[p++]=c[q]}c&&f.length!=p&&(f.length=p);this.s()}};
+k.scale=function(a,b,c){var d=b;void 0===d&&(d=a);var e=c;e||(e=gc(this.G()));if(c=this.ia()){b=c.length;for(var f=this.pa(),g=c?c:[],h=e[0],e=e[1],l=0,m=0;m<b;m+=f){var p=c[m]-h,n=c[m+1]-e;g[l++]=h+a*p;g[l++]=e+d*n;for(p=m+2;p<m+f;++p)g[l++]=c[p]}c&&g.length!=l&&(g.length=l);this.s()}};k.translate=function(a,b){var c=this.ia();c&&(Tc(c,c.length,this.pa(),a,b,c),this.s())};function Zc(a,b,c,d){for(var e=0,f=a[c-d],g=a[c-d+1];b<c;b+=d)var h=a[b],l=a[b+1],e=e+(g*h-f*l),f=h,g=l;return e/2}function $c(a,b,c,d){var e=0,f,g;f=0;for(g=c.length;f<g;++f){var h=c[f],e=e+Zc(a,b,h,d);b=h}return e};function ad(a,b,c,d,e,f,g){var h=a[b],l=a[b+1],m=a[c]-h,p=a[c+1]-l;if(0!==m||0!==p)if(f=((e-h)*m+(f-l)*p)/(m*m+p*p),1<f)b=c;else if(0<f){for(e=0;e<d;++e)g[e]=pa(a[b+e],a[c+e],f);g.length=d;return}for(e=0;e<d;++e)g[e]=a[b+e];g.length=d}function bd(a,b,c,d,e){var f=a[b],g=a[b+1];for(b+=d;b<c;b+=d){var h=a[b],l=a[b+1],f=ma(f,g,h,l);f>e&&(e=f);f=h;g=l}return e}function cd(a,b,c,d,e){var f,g;f=0;for(g=c.length;f<g;++f){var h=c[f];e=bd(a,b,h,d,e);b=h}return e}
+function dd(a,b,c,d,e,f,g,h,l,m,p){if(b==c)return m;var n;if(0===e){n=ma(g,h,a[b],a[b+1]);if(n<m){for(p=0;p<d;++p)l[p]=a[b+p];l.length=d;return n}return m}for(var q=p?p:[NaN,NaN],r=b+d;r<c;)if(ad(a,r-d,r,d,g,h,q),n=ma(g,h,q[0],q[1]),n<m){m=n;for(p=0;p<d;++p)l[p]=q[p];l.length=d;r+=d}else r+=d*Math.max((Math.sqrt(n)-Math.sqrt(m))/e|0,1);if(f&&(ad(a,c-d,b,d,g,h,q),n=ma(g,h,q[0],q[1]),n<m)){m=n;for(p=0;p<d;++p)l[p]=q[p];l.length=d}return m}
+function ed(a,b,c,d,e,f,g,h,l,m,p){p=p?p:[NaN,NaN];var n,q;n=0;for(q=c.length;n<q;++n){var r=c[n];m=dd(a,b,r,d,e,f,g,h,l,m,p);b=r}return m};function fd(a,b){var c=0,d,e;d=0;for(e=b.length;d<e;++d)a[c++]=b[d];return c}function gd(a,b,c,d){var e,f;e=0;for(f=c.length;e<f;++e){var g=c[e],h;for(h=0;h<d;++h)a[b++]=g[h]}return b}function hd(a,b,c,d,e){e=e?e:[];var f=0,g,h;g=0;for(h=c.length;g<h;++g)b=gd(a,b,c[g],d),e[f++]=b;e.length=f;return e};function id(a,b,c,d,e){e=void 0!==e?e:[];for(var f=0;b<c;b+=d)e[f++]=a.slice(b,b+d);e.length=f;return e}function jd(a,b,c,d,e){e=void 0!==e?e:[];var f=0,g,h;g=0;for(h=c.length;g<h;++g){var l=c[g];e[f++]=id(a,b,l,d,e[f]);b=l}e.length=f;return e};function kd(a,b,c,d,e,f,g){var h=(c-b)/d;if(3>h){for(;b<c;b+=d)f[g++]=a[b],f[g++]=a[b+1];return g}var l=Array(h);l[0]=1;l[h-1]=1;c=[b,c-d];for(var m=0,p;0<c.length;){var n=c.pop(),q=c.pop(),r=0,u=a[q],w=a[q+1],y=a[n],z=a[n+1];for(p=q+d;p<n;p+=d){var A=la(a[p],a[p+1],u,w,y,z);A>r&&(m=p,r=A)}r>e&&(l[(m-b)/d]=1,q+d<m&&c.push(q,m),m+d<n&&c.push(m,n))}for(p=0;p<h;++p)l[p]&&(f[g++]=a[b+p*d],f[g++]=a[b+p*d+1]);return g}
+function ld(a,b,c,d,e,f,g,h){var l,m;l=0;for(m=c.length;l<m;++l){var p=c[l];a:{var n=a,q=p,r=d,u=e,w=f;if(b!=q){var y=u*Math.round(n[b]/u),z=u*Math.round(n[b+1]/u);b+=r;w[g++]=y;w[g++]=z;var A,O;do if(A=u*Math.round(n[b]/u),O=u*Math.round(n[b+1]/u),b+=r,b==q){w[g++]=A;w[g++]=O;break a}while(A==y&&O==z);for(;b<q;){var Ja,ca;Ja=u*Math.round(n[b]/u);ca=u*Math.round(n[b+1]/u);b+=r;if(Ja!=A||ca!=O){var Ma=A-y,D=O-z,La=Ja-y,kb=ca-z;Ma*kb==D*La&&(0>Ma&&La<Ma||Ma==La||0<Ma&&La>Ma)&&(0>D&&kb<D||D==kb||0<D&&
+kb>D)||(w[g++]=A,w[g++]=O,y=A,z=O);A=Ja;O=ca}}w[g++]=A;w[g++]=O}}h.push(g);b=p}return g};function md(a,b){Uc.call(this);this.c=this.l=-1;this.qa(a,b)}v(md,Uc);k=md.prototype;k.clone=function(){var a=new md(null);nd(a,this.ka,this.B.slice());return a};k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;this.c!=this.g&&(this.l=Math.sqrt(bd(this.B,0,this.B.length,this.a,0)),this.c=this.g);return dd(this.B,0,this.B.length,this.a,this.l,!0,a,b,c,d)};k.rm=function(){return Zc(this.B,0,this.B.length,this.a)};k.$=function(){return id(this.B,0,this.B.length,this.a)};
+k.$c=function(a){var b=[];b.length=kd(this.B,0,this.B.length,this.a,a,b,0);a=new md(null);nd(a,"XY",b);return a};k.Y=function(){return"LinearRing"};k.qa=function(a,b){a?(Yc(this,b,a,1),this.B||(this.B=[]),this.B.length=gd(this.B,0,a,this.a),this.s()):nd(this,"XY",null)};function nd(a,b,c){Xc(a,b,c);a.s()};function C(a,b){Uc.call(this);this.qa(a,b)}v(C,Uc);k=C.prototype;k.clone=function(){var a=new C(null);a.da(this.ka,this.B.slice());return a};k.Ab=function(a,b,c,d){var e=this.B;a=ma(a,b,e[0],e[1]);if(a<d){d=this.a;for(b=0;b<d;++b)c[b]=e[b];c.length=d;return a}return d};k.$=function(){return this.B?this.B.slice():[]};k.Yd=function(a){return Sb(this.B,a)};k.Y=function(){return"Point"};k.Ta=function(a){return Nb(a,this.B[0],this.B[1])};
+k.qa=function(a,b){a?(Yc(this,b,a,0),this.B||(this.B=[]),this.B.length=fd(this.B,a),this.s()):this.da("XY",null)};k.da=function(a,b){Xc(this,a,b);this.s()};function pd(a,b,c,d,e){return!Xb(e,function(e){return!qd(a,b,c,d,e[0],e[1])})}function qd(a,b,c,d,e,f){for(var g=0,h=a[c-d],l=a[c-d+1];b<c;b+=d){var m=a[b],p=a[b+1];l<=f?p>f&&0<(m-h)*(f-l)-(e-h)*(p-l)&&g++:p<=f&&0>(m-h)*(f-l)-(e-h)*(p-l)&&g--;h=m;l=p}return 0!==g}function rd(a,b,c,d,e,f){if(0===c.length||!qd(a,b,c[0],d,e,f))return!1;var g;b=1;for(g=c.length;b<g;++b)if(qd(a,c[b-1],c[b],d,e,f))return!1;return!0};function sd(a,b,c,d,e,f,g){var h,l,m,p,n,q=e[f+1],r=[],u=c[0];m=a[u-d];n=a[u-d+1];for(h=b;h<u;h+=d){p=a[h];l=a[h+1];if(q<=n&&l<=q||n<=q&&q<=l)m=(q-n)/(l-n)*(p-m)+m,r.push(m);m=p;n=l}u=NaN;n=-Infinity;r.sort(Ya);m=r[0];h=1;for(l=r.length;h<l;++h){p=r[h];var w=Math.abs(p-m);w>n&&(m=(m+p)/2,rd(a,b,c,d,m,q)&&(u=m,n=w));m=p}isNaN(u)&&(u=e[f]);return g?(g.push(u,q),g):[u,q]};function td(a,b,c,d,e,f){for(var g=[a[b],a[b+1]],h=[],l;b+d<c;b+=d){h[0]=a[b+d];h[1]=a[b+d+1];if(l=e.call(f,g,h))return l;g[0]=h[0];g[1]=h[1]}return!1};function ud(a,b,c,d,e){var f=Ub(Hb(),a,b,c,d);return jc(e,f)?Ob(e,f)||f[0]>=e[0]&&f[2]<=e[2]||f[1]>=e[1]&&f[3]<=e[3]?!0:td(a,b,c,d,function(a,b){var c=!1,d=Qb(e,a),f=Qb(e,b);if(1===d||1===f)c=!0;else{var n=e[0],q=e[1],r=e[2],u=e[3],w=b[0],y=b[1],z=(y-a[1])/(w-a[0]);f&2&&!(d&2)&&(c=w-(y-u)/z,c=c>=n&&c<=r);c||!(f&4)||d&4||(c=y-(w-r)*z,c=c>=q&&c<=u);c||!(f&8)||d&8||(c=w-(y-q)/z,c=c>=n&&c<=r);c||!(f&16)||d&16||(c=y-(w-n)*z,c=c>=q&&c<=u)}return c}):!1}
+function vd(a,b,c,d,e){var f=c[0];if(!(ud(a,b,f,d,e)||qd(a,b,f,d,e[0],e[1])||qd(a,b,f,d,e[0],e[3])||qd(a,b,f,d,e[2],e[1])||qd(a,b,f,d,e[2],e[3])))return!1;if(1===c.length)return!0;b=1;for(f=c.length;b<f;++b)if(pd(a,c[b-1],c[b],d,e))return!1;return!0};function wd(a,b,c,d){for(var e=0,f=a[c-d],g=a[c-d+1];b<c;b+=d)var h=a[b],l=a[b+1],e=e+(h-f)*(l+g),f=h,g=l;return 0<e}function xd(a,b,c,d){var e=0;d=void 0!==d?d:!1;var f,g;f=0;for(g=b.length;f<g;++f){var h=b[f],e=wd(a,e,h,c);if(0===f){if(d&&e||!d&&!e)return!1}else if(d&&!e||!d&&e)return!1;e=h}return!0}
+function yd(a,b,c,d,e){e=void 0!==e?e:!1;var f,g;f=0;for(g=c.length;f<g;++f){var h=c[f],l=wd(a,b,h,d);if(0===f?e&&l||!e&&!l:e&&!l||!e&&l)for(var l=a,m=h,p=d;b<m-p;){var n;for(n=0;n<p;++n){var q=l[b+n];l[b+n]=l[m-p+n];l[m-p+n]=q}b+=p;m-=p}b=h}return b}function zd(a,b,c,d){var e=0,f,g;f=0;for(g=b.length;f<g;++f)e=yd(a,e,b[f],c,d);return e};function E(a,b){Uc.call(this);this.c=[];this.A=-1;this.C=null;this.P=this.D=this.L=-1;this.l=null;this.qa(a,b)}v(E,Uc);k=E.prototype;k.Fj=function(a){this.B?bb(this.B,a.ia()):this.B=a.ia().slice();this.c.push(this.B.length);this.s()};k.clone=function(){var a=new E(null);a.da(this.ka,this.B.slice(),this.c.slice());return a};
+k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;this.D!=this.g&&(this.L=Math.sqrt(cd(this.B,0,this.c,this.a,0)),this.D=this.g);return ed(this.B,0,this.c,this.a,this.L,!0,a,b,c,d)};k.Hc=function(a,b){return rd(this.Vb(),0,this.c,this.a,a,b)};k.um=function(){return $c(this.Vb(),0,this.c,this.a)};k.$=function(a){var b;void 0!==a?(b=this.Vb().slice(),yd(b,0,this.c,this.a,a)):b=this.B;return jd(b,0,this.c,this.a)};k.Kb=function(){return this.c};
+function Ad(a){if(a.A!=a.g){var b=gc(a.G());a.C=sd(a.Vb(),0,a.c,a.a,b,0);a.A=a.g}return a.C}k.ik=function(){return new C(Ad(this))};k.nk=function(){return this.c.length};k.Qg=function(a){if(0>a||this.c.length<=a)return null;var b=new md(null);nd(b,this.ka,this.B.slice(0===a?0:this.c[a-1],this.c[a]));return b};k.Zc=function(){var a=this.ka,b=this.B,c=this.c,d=[],e=0,f,g;f=0;for(g=c.length;f<g;++f){var h=c[f],l=new md(null);nd(l,a,b.slice(e,h));d.push(l);e=h}return d};
+k.Vb=function(){if(this.P!=this.g){var a=this.B;xd(a,this.c,this.a)?this.l=a:(this.l=a.slice(),this.l.length=yd(this.l,0,this.c,this.a));this.P=this.g}return this.l};k.$c=function(a){var b=[],c=[];b.length=ld(this.B,0,this.c,this.a,Math.sqrt(a),b,0,c);a=new E(null);a.da("XY",b,c);return a};k.Y=function(){return"Polygon"};k.Ta=function(a){return vd(this.Vb(),0,this.c,this.a,a)};
+k.qa=function(a,b){if(a){Yc(this,b,a,2);this.B||(this.B=[]);var c=hd(this.B,0,a,this.a,this.c);this.B.length=0===c.length?0:c[c.length-1];this.s()}else this.da("XY",null,this.c)};k.da=function(a,b,c){Xc(this,a,b);this.c=c;this.s()};function Bd(a,b,c,d){var e=d?d:32;d=[];var f;for(f=0;f<e;++f)bb(d,a.offset(b,c,2*Math.PI*f/e));d.push(d[0],d[1]);a=new E(null);a.da("XY",d,[d.length]);return a}
+function Cd(a){var b=a[0],c=a[1],d=a[2];a=a[3];b=[b,c,b,a,d,a,d,c,b,c];c=new E(null);c.da("XY",b,[b.length]);return c}function Dd(a,b,c){var d=b?b:32,e=a.pa();b=a.ka;for(var f=new E(null,b),d=e*(d+1),e=Array(d),g=0;g<d;g++)e[g]=0;f.da(b,e,[e.length]);Ed(f,a.Fd(),a.qe(),c);return f}function Ed(a,b,c,d){var e=a.ia(),f=a.ka,g=a.pa(),h=a.Kb(),l=e.length/g-1;d=d?d:0;for(var m,p,n=0;n<=l;++n)p=n*g,m=d+2*oa(n,l)*Math.PI/l,e[p]=b[0]+c*Math.cos(m),e[p+1]=b[1]+c*Math.sin(m);a.da(f,e,h)};function Fd(a){Sa.call(this);a=a||{};this.i=[0,0];this.f=[];this.Pe=this.Pe.bind(this);var b={};b[Gd]=void 0!==a.center?a.center:null;this.o=Jc(a.projection);var c,d,e,f=void 0!==a.minZoom?a.minZoom:0;c=void 0!==a.maxZoom?a.maxZoom:28;var g=void 0!==a.zoomFactor?a.zoomFactor:2;if(void 0!==a.resolutions)c=a.resolutions,d=c[0],e=c[c.length-1],c=ib(c);else{d=Jc(a.projection);e=d.G();var h=(e?Math.max(dc(e),ec(e)):360*qc.degrees/d.ic())/256/Math.pow(2,0),l=h/Math.pow(2,28);d=a.maxResolution;void 0!==
+d?f=0:d=h/Math.pow(g,f);e=a.minResolution;void 0===e&&(e=void 0!==a.maxZoom?void 0!==a.maxResolution?d/Math.pow(g,c):h/Math.pow(g,c):l);c=f+Math.floor(Math.log(d/e)/Math.log(g));e=d/Math.pow(g,c-f);c=jb(g,d,c-f)}this.a=d;this.j=e;this.A=g;this.c=a.resolutions;this.l=f;f=void 0!==a.extent?qa(a.extent):ra;(void 0!==a.enableRotation?a.enableRotation:1)?(g=a.constrainRotation,g=void 0===g||!0===g?ob():!1===g?mb:"number"===typeof g?nb(g):mb):g=lb;this.u=new sa(f,c,g);void 0!==a.resolution?b[Hd]=a.resolution:
+void 0!==a.zoom&&(b[Hd]=this.constrainResolution(this.a,a.zoom-this.l));b[Id]=void 0!==a.rotation?a.rotation:0;this.I(b)}v(Fd,Sa);k=Fd.prototype;
+k.animate=function(a){var b=Date.now(),c=this.fb().slice(),d=this.Oa(),e=this.Ra(),f=arguments.length,g;1<f&&"function"===typeof arguments[f-1]&&(g=arguments[f-1],--f);for(var h=[],l=0;l<f;++l){var m=arguments[l],p={start:b,complete:!1,anchor:m.anchor,duration:void 0!==m.duration?m.duration:1E3,easing:m.easing||Db};m.center&&(p.fg=c,p.hg=m.center,c=p.hg);void 0!==m.zoom?(p.Me=d,p.Ne=this.constrainResolution(this.a,m.zoom-this.l,0),d=p.Ne):m.resolution&&(p.Me=d,p.Ne=m.resolution,d=p.Ne);void 0!==m.rotation&&
+(p.gg=e,p.Hi=m.rotation,e=p.Hi);p.Vc=g;b+=p.duration;h.push(p)}this.f.push(h);Jd(this,Kd,1);this.Pe()};function Ld(a){Jd(a,Kd,-Md(a)[Kd]);for(var b=0,c=a.f.length;b<c;++b){var d=a.f[b];d[0].Vc&&d[0].Vc(!1)}a.f.length=0}
+k.Pe=function(){void 0!==this.v&&(cancelAnimationFrame(this.v),this.v=void 0);if(0<Md(this)[Kd]){for(var a=Date.now(),b=!1,c=this.f.length-1;0<=c;--c){for(var d=this.f[c],e=!0,f=0,g=d.length;f<g;++f){var h=d[f];if(!h.complete){b=a-h.start;b=0<h.duration?b/h.duration:1;1<=b?(h.complete=!0,b=1):e=!1;b=h.easing(b);if(h.fg){var l=h.fg[0],m=h.fg[1];this.set(Gd,[l+b*(h.hg[0]-l),m+b*(h.hg[1]-m)])}h.Me&&(l=h.Me+b*(h.Ne-h.Me),h.anchor&&this.set(Gd,Nd(this,l,h.anchor)),this.set(Hd,l));void 0!==h.gg&&(b=h.gg+
+b*(h.Hi-h.gg),h.anchor&&this.set(Gd,Od(this,b,h.anchor)),this.set(Id,b));b=!0;if(!h.complete)break}}e&&(this.f[c]=null,Jd(this,Kd,-1),(d=d[0].Vc)&&d(!0))}this.f=this.f.filter(Boolean);b&&void 0===this.v&&(this.v=requestAnimationFrame(this.Pe))}};function Od(a,b,c){var d,e=a.fb();void 0!==e&&(d=[e[0]-c[0],e[1]-c[1]],wb(d,b-a.Ra()),rb(d,c));return d}function Nd(a,b,c){var d,e=a.fb();a=a.Oa();void 0!==e&&void 0!==a&&(d=[c[0]-b*(c[0]-e[0])/a,c[1]-b*(c[1]-e[1])/a]);return d}k.Zd=function(a){return this.u.center(a)};
+k.constrainResolution=function(a,b,c){return this.u.resolution(a,b||0,c||0)};k.constrainRotation=function(a,b){return this.u.rotation(a,b||0)};k.fb=function(){return this.get(Gd)};function Md(a,b){return void 0!==b?(b[0]=a.i[0],b[1]=a.i[1],b):a.i.slice()}k.Uc=function(a){var b=this.fb();ha(b,1);var c=this.Oa();ha(void 0!==c,2);var d=this.Ra();ha(void 0!==d,3);return hc(b,c,d,a)};k.Zl=function(){return this.a};k.$l=function(){return this.j};k.am=function(){return this.o};k.Oa=function(){return this.get(Hd)};
+k.bm=function(){return this.c};function Pd(a,b){return Math.max(dc(a)/b[0],ec(a)/b[1])}function Qd(a){var b=a.a,c=Math.log(b/a.j)/Math.log(2);return function(a){return b/Math.pow(2,a*c)}}k.Ra=function(){return this.get(Id)};function Rd(a){var b=a.a,c=Math.log(b/a.j)/Math.log(2);return function(a){return Math.log(b/a)/Math.log(2)/c}}k.W=function(){var a=this.fb(),b=this.o,c=this.Oa(),d=this.Ra();return{center:a.slice(),projection:void 0!==b?b:null,resolution:c,rotation:d}};
+k.Kk=function(){var a,b=this.Oa();if(void 0!==b&&b>=this.j&&b<=this.a){a=this.l||0;var c,d;if(this.c){d=$a(this.c,b,1);a+=d;if(d==this.c.length-1)return a;c=this.c[d];d=c/this.c[d+1]}else c=this.a,d=this.A;a+=Math.log(c/b)/Math.log(d)}return a};
+k.lf=function(a,b,c){a instanceof Uc||(ha(Array.isArray(a),24),ha(!cc(a),25),a=Cd(a));c=c||{};var d=void 0!==c.padding?c.padding:[0,0,0,0],e=void 0!==c.constrainResolution?c.constrainResolution:!0,f=void 0!==c.nearest?c.nearest:!1,g;void 0!==c.minResolution?g=c.minResolution:void 0!==c.maxZoom?g=this.constrainResolution(this.a,c.maxZoom-this.l,0):g=0;var h=a.ia(),l=this.Ra(),m=Math.cos(-l),l=Math.sin(-l),p=Infinity,n=Infinity,q=-Infinity,r=-Infinity;a=a.pa();for(var u=0,w=h.length;u<w;u+=a)var y=
+h[u]*m-h[u+1]*l,z=h[u]*l+h[u+1]*m,p=Math.min(p,y),n=Math.min(n,z),q=Math.max(q,y),r=Math.max(r,z);b=Pd([p,n,q,r],[b[0]-d[1]-d[3],b[1]-d[0]-d[2]]);b=isNaN(b)?g:Math.max(b,g);e&&(g=this.constrainResolution(b,0,0),!f&&g<b&&(g=this.constrainResolution(g,-1,0)),b=g);l=-l;f=(p+q)/2+(d[1]-d[3])/2*b;d=(n+r)/2+(d[0]-d[2])/2*b;m=[f*m-d*l,d*m+f*l];void 0!==c.duration?this.animate({resolution:b,center:m,duration:c.duration,easing:c.easing}):(this.Oc(b),this.Mb(m))};
+k.Kj=function(a,b,c){var d=this.Ra(),e=Math.cos(-d),d=Math.sin(-d),f=a[0]*e-a[1]*d;a=a[1]*e+a[0]*d;var g=this.Oa(),f=f+(b[0]/2-c[0])*g;a+=(c[1]-b[1]/2)*g;d=-d;this.Mb([f*e-a*d,a*e+f*d])};function Sd(a){return!!a.fb()&&void 0!==a.Oa()}k.rotate=function(a,b){if(void 0!==b){var c=Od(this,a,b);this.Mb(c)}this.pe(a)};k.Mb=function(a){this.set(Gd,a);0<Md(this)[Kd]&&Ld(this)};function Jd(a,b,c){a.i[b]+=c;a.s()}k.Oc=function(a){this.set(Hd,a);0<Md(this)[Kd]&&Ld(this)};
+k.pe=function(a){this.set(Id,a);0<Md(this)[Kd]&&Ld(this)};k.pp=function(a){a=this.constrainResolution(this.a,a-this.l,0);this.Oc(a)};var Gd="center",Hd="resolution",Id="rotation",Kd=0;function Td(a,b,c,d){this.ea=a;this.ca=b;this.ga=c;this.ja=d}function Ud(a,b,c){return a.ea<=b&&b<=a.ca&&a.ga<=c&&c<=a.ja}function Vd(a,b){return a.ea==b.ea&&a.ga==b.ga&&a.ca==b.ca&&a.ja==b.ja}function Wd(a,b){return a.ea<=b.ca&&a.ca>=b.ea&&a.ga<=b.ja&&a.ja>=b.ga};function Xd(a,b,c){void 0===c&&(c=[0,0]);c[0]=a[0]+2*b;c[1]=a[1]+2*b;return c}function Yd(a,b,c){void 0===c&&(c=[0,0]);c[0]=a[0]*b+.5|0;c[1]=a[1]*b+.5|0;return c}function Zd(a,b){if(Array.isArray(a))return a;void 0===b?b=[a,a]:b[0]=b[1]=a;return b};function $d(a,b,c,d){return void 0!==d?(d[0]=a,d[1]=b,d[2]=c,d):[a,b,c]}function ae(a){var b=a[0],c=Array(b),d=1<<b-1,e,f;for(e=0;e<b;++e)f=48,a[1]&d&&(f+=1),a[2]&d&&(f+=2),c[e]=String.fromCharCode(f),d>>=1;return c.join("")};function be(a){this.minZoom=void 0!==a.minZoom?a.minZoom:0;this.b=a.resolutions;ha(hb(this.b,function(a,b){return b-a}),17);this.maxZoom=this.b.length-1;this.g=void 0!==a.origin?a.origin:null;this.f=null;void 0!==a.origins&&(this.f=a.origins,ha(this.f.length==this.b.length,20));var b=a.extent;void 0===b||this.g||this.f||(this.g=ac(b));ha(!this.g&&this.f||this.g&&!this.f,18);this.c=null;void 0!==a.tileSizes&&(this.c=a.tileSizes,ha(this.c.length==this.b.length,19));this.i=void 0!==a.tileSize?a.tileSize:
+this.c?null:256;ha(!this.i&&this.c||this.i&&!this.c,22);this.v=void 0!==b?b:null;this.a=null;this.j=[0,0];void 0!==a.sizes?this.a=a.sizes.map(function(a){return new Td(Math.min(0,a[0]),Math.max(a[0]-1,-1),Math.min(0,a[1]),Math.max(a[1]-1,-1))},this):b&&ce(this,b)}var ee=[0,0,0];k=be.prototype;k.Hg=function(a,b,c){a=fe(this,a,b);for(var d=a.ea,e=a.ca;d<=e;++d)for(var f=a.ga,g=a.ja;f<=g;++f)c([b,d,f])};
+function ge(a,b,c,d,e){e=a.Na(b,e);for(b=b[0]-1;b>=a.minZoom;){if(c.call(null,b,fe(a,e,b,d)))return!0;--b}return!1}k.G=function(){return this.v};k.Rg=function(){return this.maxZoom};k.Sg=function(){return this.minZoom};k.Kc=function(a){return this.g?this.g:this.f[a]};k.Ha=function(a){return this.b[a]};k.Wh=function(){return this.b};function he(a,b,c,d){return b[0]<a.maxZoom?(d=a.Na(b,d),fe(a,d,b[0]+1,c)):null}
+function ie(a,b,c,d){je(a,b[0],b[1],c,!1,ee);var e=ee[1],f=ee[2];je(a,b[2],b[3],c,!0,ee);a=ee[1];b=ee[2];void 0!==d?(d.ea=e,d.ca=a,d.ga=f,d.ja=b):d=new Td(e,a,f,b);return d}function fe(a,b,c,d){c=a.Ha(c);return ie(a,b,c,d)}function ke(a,b){var c=a.Kc(b[0]),d=a.Ha(b[0]),e=Zd(a.Za(b[0]),a.j);return[c[0]+(b[1]+.5)*e[0]*d,c[1]+(b[2]+.5)*e[1]*d]}k.Na=function(a,b){var c=this.Kc(a[0]),d=this.Ha(a[0]),e=Zd(this.Za(a[0]),this.j),f=c[0]+a[1]*e[0]*d,c=c[1]+a[2]*e[1]*d;return Rb(f,c,f+e[0]*d,c+e[1]*d,b)};
+k.fe=function(a,b,c){return je(this,a[0],a[1],b,!1,c)};function je(a,b,c,d,e,f){var g=a.Ec(d),h=d/a.Ha(g),l=a.Kc(g);a=Zd(a.Za(g),a.j);b=h*Math.floor((b-l[0])/d+(e?.5:0))/a[0];c=h*Math.floor((c-l[1])/d+(e?0:.5))/a[1];e?(b=Math.ceil(b)-1,c=Math.ceil(c)-1):(b=Math.floor(b),c=Math.floor(c));return $d(g,b,c,f)}k.wf=function(a,b,c){b=this.Ha(b);return je(this,a[0],a[1],b,!1,c)};k.Za=function(a){return this.i?this.i:this.c[a]};k.Ec=function(a,b){return ia($a(this.b,a,b||0),this.minZoom,this.maxZoom)};
+function ce(a,b){for(var c=a.b.length,d=Array(c),e=a.minZoom;e<c;++e)d[e]=fe(a,b,e);a.a=d};function le(a){var b=a.j;if(!b){var b=me(a),c=ne(b,void 0,void 0),b=new be({extent:b,origin:ac(b),resolutions:c,tileSize:void 0});a.j=b}return b}function oe(a){var b={};ta(b,void 0!==a?a:{});void 0===b.extent&&(b.extent=zc("EPSG:3857").G());b.resolutions=ne(b.extent,b.maxZoom,b.tileSize);delete b.maxZoom;return new be(b)}function ne(a,b,c){b=void 0!==b?b:42;var d=ec(a);a=dc(a);c=Zd(void 0!==c?c:256);c=Math.max(a/c[0],d/c[1]);b+=1;d=Array(b);for(a=0;a<b;++a)d[a]=c/Math.pow(2,a);return d}
+function me(a){a=zc(a);var b=a.G();b||(a=180*qc.degrees/a.ic(),b=Rb(-a,-a,a,a));return b};function pe(a){this.b=a.html;this.a=a.tileRanges?a.tileRanges:null}pe.prototype.g=function(){return this.b};function qe(a){Sa.call(this);this.a=a?a:[];re(this)}v(qe,Sa);k=qe.prototype;k.clear=function(){for(;0<this.Ub();)this.pop()};k.Bf=function(a){var b,c;b=0;for(c=a.length;b<c;++b)this.push(a[b]);return this};k.forEach=function(a,b){this.a.forEach(a,b)};k.Il=function(){return this.a};k.item=function(a){return this.a[a]};k.Ub=function(){return this.get(se)};k.ke=function(a,b){this.a.splice(a,0,b);re(this);this.b(new te(ue,b))};k.pop=function(){return this.Zf(this.Ub()-1)};
+k.push=function(a){var b=this.Ub();this.ke(b,a);return this.Ub()};k.remove=function(a){var b=this.a,c,d;c=0;for(d=b.length;c<d;++c)if(b[c]===a)return this.Zf(c)};k.Zf=function(a){var b=this.a[a];this.a.splice(a,1);re(this);this.b(new te(ve,b));return b};k.ep=function(a,b){var c=this.Ub();if(a<c)c=this.a[a],this.a[a]=b,this.b(new te(ve,c)),this.b(new te(ue,b));else{for(;c<a;++c)this.ke(c,void 0);this.ke(a,b)}};function re(a){a.set(se,a.a.length)}var se="length",ue="add",ve="remove";
+function te(a,b){Ia.call(this,a);this.element=b}v(te,Ia);var we=/^#(?:[0-9a-f]{3}){1,2}$/i,xe=/^([a-z]*)$/i;function ye(a){return Array.isArray(a)?a:ze(a)}function Ae(a){if("string"!==typeof a){var b=a[0];b!=(b|0)&&(b=b+.5|0);var c=a[1];c!=(c|0)&&(c=c+.5|0);var d=a[2];d!=(d|0)&&(d=d+.5|0);a="rgba("+b+","+c+","+d+","+(void 0===a[3]?1:a[3])+")"}return a}
+var ze=function(){var a={},b=0;return function(c){var d;if(a.hasOwnProperty(c))d=a[c];else{if(1024<=b){d=0;for(var e in a)0===(d++&3)&&(delete a[e],--b)}d=c;var f;xe.exec(d)&&(e=document.createElement("div"),e.style.color=d,document.body.appendChild(e),d=getComputedStyle(e).color,document.body.removeChild(e));if(we.exec(d)){f=d.length-1;ha(3==f||6==f,54);var g=3==f?1:2;f=parseInt(d.substr(1+0*g,g),16);e=parseInt(d.substr(1+1*g,g),16);d=parseInt(d.substr(1+2*g,g),16);1==g&&(f=(f<<4)+f,e=(e<<4)+e,d=
+(d<<4)+d);f=[f,e,d,1]}else 0==d.indexOf("rgba(")?(d=d.slice(5,-1).split(",").map(Number),f=Be(d)):0==d.indexOf("rgb(")?(d=d.slice(4,-1).split(",").map(Number),d.push(1),f=Be(d)):ha(!1,14);d=f;a[c]=d;++b}return d}}();function Be(a){var b=[];b[0]=ia(a[0]+.5|0,0,255);b[1]=ia(a[1]+.5|0,0,255);b[2]=ia(a[2]+.5|0,0,255);b[3]=ia(a[3],0,1);return b};function Ce(a){return"string"===typeof a||a instanceof CanvasPattern||a instanceof CanvasGradient?a:Ae(a)};function De(a,b){var c=document.createElement("CANVAS");a&&(c.width=a);b&&(c.height=b);return c.getContext("2d")}function Ee(a,b){var c=b.parentNode;c&&c.replaceChild(a,b)}function Fe(a){a&&a.parentNode&&a.parentNode.removeChild(a)};function Ge(a,b,c){Ia.call(this,a);this.map=b;this.frameState=void 0!==c?c:null}v(Ge,Ia);function Ie(a){Sa.call(this);this.element=a.element?a.element:null;this.a=this.P=null;this.v=[];this.render=a.render?a.render:ea;a.target&&this.c(a.target)}v(Ie,Sa);Ie.prototype.oa=function(){Fe(this.element);Sa.prototype.oa.call(this)};Ie.prototype.i=function(){return this.a};
+Ie.prototype.setMap=function(a){this.a&&Fe(this.element);for(var b=0,c=this.v.length;b<c;++b)ya(this.v[b]);this.v.length=0;if(this.a=a)(this.P?this.P:a.u).appendChild(this.element),this.render!==ea&&this.v.push(B(a,"postrender",this.render,this)),a.render()};Ie.prototype.c=function(a){this.P="string"===typeof a?document.getElementById(a):a};function Je(a){a=a?a:{};this.L=document.createElement("UL");this.u=document.createElement("LI");this.L.appendChild(this.u);this.u.style.display="none";this.f=void 0!==a.collapsed?a.collapsed:!0;this.l=void 0!==a.collapsible?a.collapsible:!0;this.l||(this.f=!1);var b=void 0!==a.className?a.className:"ol-attribution",c=void 0!==a.tipLabel?a.tipLabel:"Attributions",d=void 0!==a.collapseLabel?a.collapseLabel:"\u00bb";"string"===typeof d?(this.A=document.createElement("span"),this.A.textContent=d):this.A=
+d;d=void 0!==a.label?a.label:"i";"string"===typeof d?(this.C=document.createElement("span"),this.C.textContent=d):this.C=d;var e=this.l&&!this.f?this.A:this.C,d=document.createElement("button");d.setAttribute("type","button");d.title=c;d.appendChild(e);B(d,"click",this.em,this);c=document.createElement("div");c.className=b+" ol-unselectable ol-control"+(this.f&&this.l?" ol-collapsed":"")+(this.l?"":" ol-uncollapsible");c.appendChild(this.L);c.appendChild(d);Ie.call(this,{element:c,render:a.render?
+a.render:Ke,target:a.target});this.D=!0;this.o={};this.j={};this.U={}}v(Je,Ie);
+function Ke(a){if(a=a.frameState){var b,c,d,e,f,g,h,l,m,p,n,q=a.layerStatesArray,r=ta({},a.attributions),u={},w={},y=a.viewState.projection;c=0;for(b=q.length;c<b;c++)if(g=q[c].layer.la())if(p=x(g).toString(),m=g.j)for(d=0,e=m.length;d<e;d++)if(h=m[d],l=x(h).toString(),!(l in r)){if(f=a.usedTiles[p]){var z=g.Db(y);a:{n=h;var A=y;if(n.a){var O,Ja,ca,Ma=void 0;for(Ma in f)if(Ma in n.a){ca=f[Ma];var D;O=0;for(Ja=n.a[Ma].length;O<Ja;++O){D=n.a[Ma][O];if(Wd(D,ca)){n=!0;break a}var La=fe(z,me(A),parseInt(Ma,
+10)),kb=La.ca-La.ea+1;if(ca.ea<La.ea||ca.ca>La.ca)if(Wd(D,new Td(oa(ca.ea,kb),oa(ca.ca,kb),ca.ga,ca.ja))||ca.ca-ca.ea+1>kb&&Wd(D,La)){n=!0;break a}}}n=!1}else n=!0}}else n=!1;n?(l in u&&delete u[l],n=h.b,n in w||(w[n]=!0,r[l]=h)):u[l]=h}b=[r,u];c=b[0];b=b[1];for(var W in this.o)W in c?(this.j[W]||(this.o[W].style.display="",this.j[W]=!0),delete c[W]):W in b?(this.j[W]&&(this.o[W].style.display="none",delete this.j[W]),delete b[W]):(Fe(this.o[W]),delete this.o[W],delete this.j[W]);for(W in c)d=document.createElement("LI"),
+d.innerHTML=c[W].b,this.L.appendChild(d),this.o[W]=d,this.j[W]=!0;for(W in b)d=document.createElement("LI"),d.innerHTML=b[W].b,d.style.display="none",this.L.appendChild(d),this.o[W]=d;W=!wa(this.j)||!wa(a.logos);this.D!=W&&(this.element.style.display=W?"":"none",this.D=W);W&&wa(this.j)?this.element.classList.add("ol-logo-only"):this.element.classList.remove("ol-logo-only");var Ra;a=a.logos;W=this.U;for(Ra in W)Ra in a||(Fe(W[Ra]),delete W[Ra]);for(var Pb in a)b=a[Pb],b instanceof HTMLElement&&(this.u.appendChild(b),
+W[Pb]=b),Pb in W||(Ra=new Image,Ra.src=Pb,""===b?c=Ra:(c=document.createElement("a"),c.href=b,c.appendChild(Ra)),this.u.appendChild(c),W[Pb]=c);this.u.style.display=wa(a)?"none":""}else this.D&&(this.element.style.display="none",this.D=!1)}k=Je.prototype;k.em=function(a){a.preventDefault();Le(this)};function Le(a){a.element.classList.toggle("ol-collapsed");a.f?Ee(a.A,a.C):Ee(a.C,a.A);a.f=!a.f}k.dm=function(){return this.l};
+k.gm=function(a){this.l!==a&&(this.l=a,this.element.classList.toggle("ol-uncollapsible"),!a&&this.f&&Le(this))};k.fm=function(a){this.l&&this.f!==a&&Le(this)};k.cm=function(){return this.f};function Me(a){a=a?a:{};this.f=void 0!==a.className?a.className:"ol-full-screen";var b=void 0!==a.label?a.label:"\u2922";this.l="string"===typeof b?document.createTextNode(b):b;b=void 0!==a.labelActive?a.labelActive:"\u00d7";this.o="string"===typeof b?document.createTextNode(b):b;var c=a.tipLabel?a.tipLabel:"Toggle full-screen",b=document.createElement("button");b.className=this.f+"-"+Ne();b.setAttribute("type","button");b.title=c;b.appendChild(this.l);B(b,"click",this.C,this);c=document.createElement("div");
+c.className=this.f+" ol-unselectable ol-control "+(Oe()?"":"ol-unsupported");c.appendChild(b);Ie.call(this,{element:c,target:a.target});this.A=void 0!==a.keys?a.keys:!1;this.j=a.source}v(Me,Ie);
+Me.prototype.C=function(a){a.preventDefault();Oe()&&(a=this.a)&&(Ne()?document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen():(a=this.j?"string"===typeof this.j?document.getElementById(this.j):this.j:a.Cc(),this.A?a.mozRequestFullScreenWithKeys?a.mozRequestFullScreenWithKeys():a.webkitRequestFullscreen?a.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT):
+Pe(a):Pe(a)))};Me.prototype.u=function(){var a=this.element.firstElementChild,b=this.a;Ne()?(a.className=this.f+"-true",Ee(this.o,this.l)):(a.className=this.f+"-false",Ee(this.l,this.o));b&&b.ld()};Me.prototype.setMap=function(a){Ie.prototype.setMap.call(this,a);a&&this.v.push(B(document,Qe(),this.u,this))};
+function Oe(){var a=document.body;return!!(a.webkitRequestFullscreen||a.mozRequestFullScreen&&document.mozFullScreenEnabled||a.msRequestFullscreen&&document.msFullscreenEnabled||a.requestFullscreen&&document.fullscreenEnabled)}function Ne(){return!!(document.webkitIsFullScreen||document.mozFullScreen||document.msFullscreenElement||document.fullscreenElement)}
+function Pe(a){a.requestFullscreen?a.requestFullscreen():a.msRequestFullscreen?a.msRequestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen&&a.webkitRequestFullscreen()}var Qe=function(){var a;return function(){if(!a){var b=document.body;b.webkitRequestFullscreen?a="webkitfullscreenchange":b.mozRequestFullScreen?a="mozfullscreenchange":b.msRequestFullscreen?a="MSFullscreenChange":b.requestFullscreen&&(a="fullscreenchange")}return a}}();function Re(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-rotate",c=void 0!==a.label?a.label:"\u21e7";this.f=null;"string"===typeof c?(this.f=document.createElement("span"),this.f.className="ol-compass",this.f.textContent=c):(this.f=c,this.f.classList.add("ol-compass"));var d=a.tipLabel?a.tipLabel:"Reset rotation",c=document.createElement("button");c.className=b+"-reset";c.setAttribute("type","button");c.title=d;c.appendChild(this.f);B(c,"click",Re.prototype.A,this);d=document.createElement("div");
+d.className=b+" ol-unselectable ol-control";d.appendChild(c);b=a.render?a.render:Se;this.l=a.resetNorth?a.resetNorth:void 0;Ie.call(this,{element:d,render:b,target:a.target});this.o=void 0!==a.duration?a.duration:250;this.j=void 0!==a.autoHide?a.autoHide:!0;this.u=void 0;this.j&&this.element.classList.add("ol-hidden")}v(Re,Ie);Re.prototype.A=function(a){a.preventDefault();void 0!==this.l?this.l():(a=this.a.aa())&&void 0!==a.Ra()&&(0<this.o?a.animate({rotation:0,duration:this.o,easing:Cb}):a.pe(0))};
+function Se(a){if(a=a.frameState){a=a.viewState.rotation;if(a!=this.u){var b="rotate("+a+"rad)";if(this.j){var c=this.element.classList.contains("ol-hidden");c||0!==a?c&&0!==a&&this.element.classList.remove("ol-hidden"):this.element.classList.add("ol-hidden")}this.f.style.msTransform=b;this.f.style.webkitTransform=b;this.f.style.transform=b}this.u=a}};function Te(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-zoom",c=void 0!==a.delta?a.delta:1,d=void 0!==a.zoomInLabel?a.zoomInLabel:"+",e=void 0!==a.zoomOutLabel?a.zoomOutLabel:"\u2212",f=void 0!==a.zoomInTipLabel?a.zoomInTipLabel:"Zoom in",g=void 0!==a.zoomOutTipLabel?a.zoomOutTipLabel:"Zoom out",h=document.createElement("button");h.className=b+"-in";h.setAttribute("type","button");h.title=f;h.appendChild("string"===typeof d?document.createTextNode(d):d);B(h,"click",Te.prototype.j.bind(this,
+c));d=document.createElement("button");d.className=b+"-out";d.setAttribute("type","button");d.title=g;d.appendChild("string"===typeof e?document.createTextNode(e):e);B(d,"click",Te.prototype.j.bind(this,-c));c=document.createElement("div");c.className=b+" ol-unselectable ol-control";c.appendChild(h);c.appendChild(d);Ie.call(this,{element:c,target:a.target});this.f=void 0!==a.duration?a.duration:250}v(Te,Ie);
+Te.prototype.j=function(a,b){b.preventDefault();var c=this.a.aa();if(c){var d=c.Oa();d&&(d=c.constrainResolution(d,a),0<this.f?(0<Md(c)[Kd]&&Ld(c),c.animate({resolution:d,duration:this.f,easing:Cb})):c.Oc(d))}};function Ue(a){a=a?a:{};var b=new qe;(void 0!==a.zoom?a.zoom:1)&&b.push(new Te(a.zoomOptions));(void 0!==a.rotate?a.rotate:1)&&b.push(new Re(a.rotateOptions));(void 0!==a.attribution?a.attribution:1)&&b.push(new Je(a.attributionOptions));return b};function Ve(a){a=a?a:{};var b=document.createElement("DIV");b.className=void 0!==a.className?a.className:"ol-mouse-position";Ie.call(this,{element:b,render:a.render?a.render:We,target:a.target});B(this,Ua(Xe),this.hm,this);a.coordinateFormat&&this.oi(a.coordinateFormat);a.projection&&this.sh(zc(a.projection));this.u=void 0!==a.undefinedHTML?a.undefinedHTML:"";this.o=b.innerHTML;this.l=this.j=this.f=null}v(Ve,Ie);
+function We(a){a=a.frameState;a?this.f!=a.viewState.projection&&(this.f=a.viewState.projection,this.j=null):this.f=null;Ye(this,this.l)}k=Ve.prototype;k.hm=function(){this.j=null};k.Lg=function(){return this.get(Ze)};k.rh=function(){return this.get(Xe)};k.$k=function(a){this.l=this.a.ce(a);Ye(this,this.l)};k.al=function(){Ye(this,null);this.l=null};k.setMap=function(a){Ie.prototype.setMap.call(this,a);a&&(a=a.f,this.v.push(B(a,"mousemove",this.$k,this),B(a,"mouseout",this.al,this)))};
+k.oi=function(a){this.set(Ze,a)};k.sh=function(a){this.set(Xe,a)};function Ye(a,b){var c=a.u;if(b&&a.f){if(!a.j){var d=a.rh();a.j=d?yc(a.f,d):Oc}if(d=a.a.Sa(b))a.j(d,d),c=(c=a.Lg())?c(d):d.toString()}a.o&&c==a.o||(a.element.innerHTML=c,a.o=c)}var Xe="projection",Ze="coordinateFormat";function $e(a,b,c,d,e){Ge.call(this,a,b,e);this.originalEvent=c;this.pixel=b.ce(c);this.coordinate=b.Sa(this.pixel);this.dragging=void 0!==d?d:!1}v($e,Ge);$e.prototype.preventDefault=function(){Ge.prototype.preventDefault.call(this);this.originalEvent.preventDefault()};$e.prototype.stopPropagation=function(){Ge.prototype.stopPropagation.call(this);this.originalEvent.stopPropagation()};
+var af={Np:"singleclick",Cp:"click",Dp:"dblclick",Gp:"pointerdrag",Jp:"pointermove",Fp:"pointerdown",Mp:"pointerup",Lp:"pointerover",Kp:"pointerout",Hp:"pointerenter",Ip:"pointerleave",Ep:"pointercancel"};function cf(a,b,c,d,e){$e.call(this,a,b,c.b,d,e);this.b=c}v(cf,$e);var df=["experimental-webgl","webgl","webkit-3d","moz-webgl"];function ef(a,b){var c,d,e=df.length;for(d=0;d<e;++d)try{if(c=a.getContext(df[d],b))return c}catch(f){}return null};var ff,gf="undefined"!==typeof navigator?navigator.userAgent.toLowerCase():"",hf=-1!==gf.indexOf("firefox"),jf=-1!==gf.indexOf("safari")&&-1==gf.indexOf("chrom"),kf=-1!==gf.indexOf("webkit")&&-1==gf.indexOf("edge"),lf=-1!==gf.indexOf("macintosh"),mf=window.devicePixelRatio||1,nf=!1,of=function(){if(!("HTMLCanvasElement"in window))return!1;try{var a=document.createElement("CANVAS").getContext("2d");return a?(void 0!==a.setLineDash&&(nf=!0),!0):!1}catch(b){return!1}}(),pf="DeviceOrientationEvent"in
+window,qf="geolocation"in navigator,rf="ontouchstart"in window,sf="PointerEvent"in window,tf=!!navigator.msPointerEnabled,uf=!1,vf,wf=[];if("WebGLRenderingContext"in window)try{var xf=ef(document.createElement("CANVAS"),{failIfMajorPerformanceCaveat:!0});xf&&(uf=!0,vf=xf.getParameter(xf.MAX_TEXTURE_SIZE),wf=xf.getSupportedExtensions())}catch(a){}ff=uf;da=wf;ba=vf;function yf(a,b){this.b=a;this.c=b};function zf(a){yf.call(this,a,{mousedown:this.tl,mousemove:this.ul,mouseup:this.xl,mouseover:this.wl,mouseout:this.vl});this.a=a.g;this.g=[]}v(zf,yf);function Af(a,b){for(var c=a.g,d=b.clientX,e=b.clientY,f=0,g=c.length,h;f<g&&(h=c[f]);f++){var l=Math.abs(e-h[1]);if(25>=Math.abs(d-h[0])&&25>=l)return!0}return!1}function Bf(a){var b=Cf(a,a),c=b.preventDefault;b.preventDefault=function(){a.preventDefault();c()};b.pointerId=1;b.isPrimary=!0;b.pointerType="mouse";return b}k=zf.prototype;
+k.tl=function(a){if(!Af(this,a)){if((1).toString()in this.a){var b=Bf(a);Df(this.b,"pointercancel",b,a);delete this.a[(1).toString()]}b=Bf(a);this.a[(1).toString()]=a;Df(this.b,"pointerdown",b,a)}};k.ul=function(a){if(!Af(this,a)){var b=Bf(a);Df(this.b,"pointermove",b,a)}};k.xl=function(a){if(!Af(this,a)){var b=this.a[(1).toString()];b&&b.button===a.button&&(b=Bf(a),Df(this.b,"pointerup",b,a),delete this.a[(1).toString()])}};k.wl=function(a){if(!Af(this,a)){var b=Bf(a);Ef(this.b,b,a)}};
+k.vl=function(a){if(!Af(this,a)){var b=Bf(a);Ff(this.b,b,a)}};function Gf(a){yf.call(this,a,{MSPointerDown:this.Cl,MSPointerMove:this.Dl,MSPointerUp:this.Gl,MSPointerOut:this.El,MSPointerOver:this.Fl,MSPointerCancel:this.Bl,MSGotPointerCapture:this.zl,MSLostPointerCapture:this.Al});this.a=a.g;this.g=["","unavailable","touch","pen","mouse"]}v(Gf,yf);function Hf(a,b){var c=b;"number"===typeof b.pointerType&&(c=Cf(b,b),c.pointerType=a.g[b.pointerType]);return c}k=Gf.prototype;
+k.Cl=function(a){this.a[a.pointerId.toString()]=a;var b=Hf(this,a);Df(this.b,"pointerdown",b,a)};k.Dl=function(a){var b=Hf(this,a);Df(this.b,"pointermove",b,a)};k.Gl=function(a){var b=Hf(this,a);Df(this.b,"pointerup",b,a);delete this.a[a.pointerId.toString()]};k.El=function(a){var b=Hf(this,a);Ff(this.b,b,a)};k.Fl=function(a){var b=Hf(this,a);Ef(this.b,b,a)};k.Bl=function(a){var b=Hf(this,a);Df(this.b,"pointercancel",b,a);delete this.a[a.pointerId.toString()]};
+k.Al=function(a){this.b.b(new If("lostpointercapture",a,a))};k.zl=function(a){this.b.b(new If("gotpointercapture",a,a))};function Jf(a){yf.call(this,a,{pointerdown:this.po,pointermove:this.qo,pointerup:this.to,pointerout:this.ro,pointerover:this.so,pointercancel:this.oo,gotpointercapture:this.Lk,lostpointercapture:this.sl})}v(Jf,yf);k=Jf.prototype;k.po=function(a){Kf(this.b,a)};k.qo=function(a){Kf(this.b,a)};k.to=function(a){Kf(this.b,a)};k.ro=function(a){Kf(this.b,a)};k.so=function(a){Kf(this.b,a)};k.oo=function(a){Kf(this.b,a)};k.sl=function(a){Kf(this.b,a)};k.Lk=function(a){Kf(this.b,a)};function If(a,b,c){Ia.call(this,a);this.b=b;a=c?c:{};this.buttons=Lf(a);this.pressure=Mf(a,this.buttons);this.bubbles="bubbles"in a?a.bubbles:!1;this.cancelable="cancelable"in a?a.cancelable:!1;this.view="view"in a?a.view:null;this.detail="detail"in a?a.detail:null;this.screenX="screenX"in a?a.screenX:0;this.screenY="screenY"in a?a.screenY:0;this.clientX="clientX"in a?a.clientX:0;this.clientY="clientY"in a?a.clientY:0;this.button="button"in a?a.button:0;this.relatedTarget="relatedTarget"in a?a.relatedTarget:
+null;this.pointerId="pointerId"in a?a.pointerId:0;this.width="width"in a?a.width:0;this.height="height"in a?a.height:0;this.pointerType="pointerType"in a?a.pointerType:"";this.isPrimary="isPrimary"in a?a.isPrimary:!1;b.preventDefault&&(this.preventDefault=function(){b.preventDefault()})}v(If,Ia);function Lf(a){if(a.buttons||Nf)a=a.buttons;else switch(a.which){case 1:a=1;break;case 2:a=4;break;case 3:a=2;break;default:a=0}return a}
+function Mf(a,b){var c=0;a.pressure?c=a.pressure:c=b?.5:0;return c}var Nf=!1;try{Nf=1===(new MouseEvent("click",{buttons:1})).buttons}catch(a){};function Of(a,b){yf.call(this,a,{touchstart:this.up,touchmove:this.tp,touchend:this.sp,touchcancel:this.rp});this.a=a.g;this.j=b;this.g=void 0;this.i=0;this.f=void 0}v(Of,yf);k=Of.prototype;k.mi=function(){this.i=0;this.f=void 0};
+function Pf(a,b,c){b=Cf(b,c);b.pointerId=c.identifier+2;b.bubbles=!0;b.cancelable=!0;b.detail=a.i;b.button=0;b.buttons=1;b.width=c.webkitRadiusX||c.radiusX||0;b.height=c.webkitRadiusY||c.radiusY||0;b.pressure=c.webkitForce||c.force||.5;b.isPrimary=a.g===c.identifier;b.pointerType="touch";b.clientX=c.clientX;b.clientY=c.clientY;b.screenX=c.screenX;b.screenY=c.screenY;return b}
+function Qf(a,b,c){function d(){b.preventDefault()}var e=Array.prototype.slice.call(b.changedTouches),f=e.length,g,h;for(g=0;g<f;++g)h=Pf(a,b,e[g]),h.preventDefault=d,c.call(a,b,h)}
+k.up=function(a){var b=a.touches,c=Object.keys(this.a),d=c.length;if(d>=b.length){var e=[],f,g,h;for(f=0;f<d;++f){g=c[f];h=this.a[g];var l;if(!(l=1==g))a:{l=b.length;for(var m,p=0;p<l;p++)if(m=b[p],m.identifier===g-2){l=!0;break a}l=!1}l||e.push(h.out)}for(f=0;f<e.length;++f)this.ef(a,e[f])}b=a.changedTouches[0];c=Object.keys(this.a).length;if(0===c||1===c&&(1).toString()in this.a)this.g=b.identifier,void 0!==this.f&&clearTimeout(this.f);Rf(this,a);this.i++;Qf(this,a,this.ko)};
+k.ko=function(a,b){this.a[b.pointerId]={target:b.target,out:b,Xh:b.target};var c=this.b;b.bubbles=!0;Df(c,"pointerover",b,a);c=this.b;b.bubbles=!1;Df(c,"pointerenter",b,a);Df(this.b,"pointerdown",b,a)};k.tp=function(a){a.preventDefault();Qf(this,a,this.yl)};
+k.yl=function(a,b){var c=this.a[b.pointerId];if(c){var d=c.out,e=c.Xh;Df(this.b,"pointermove",b,a);d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,d.target=e,b.target?(Ff(this.b,d,a),Ef(this.b,b,a)):(b.target=e,b.relatedTarget=null,this.ef(a,b)));c.out=b;c.Xh=b.target}};k.sp=function(a){Rf(this,a);Qf(this,a,this.vp)};
+k.vp=function(a,b){Df(this.b,"pointerup",b,a);this.b.out(b,a);Sf(this.b,b,a);delete this.a[b.pointerId];b.isPrimary&&(this.g=void 0,this.f=setTimeout(this.mi.bind(this),200))};k.rp=function(a){Qf(this,a,this.ef)};k.ef=function(a,b){Df(this.b,"pointercancel",b,a);this.b.out(b,a);Sf(this.b,b,a);delete this.a[b.pointerId];b.isPrimary&&(this.g=void 0,this.f=setTimeout(this.mi.bind(this),200))};
+function Rf(a,b){var c=a.j.g,d=b.changedTouches[0];if(a.g===d.identifier){var e=[d.clientX,d.clientY];c.push(e);setTimeout(function(){var a=c.indexOf(e);-1<a&&c.splice(a,1)},2500)}};function Tf(a){Na.call(this);this.i=a;this.g={};this.c={};this.a=[];sf?Uf(this,new Jf(this)):tf?Uf(this,new Gf(this)):(a=new zf(this),Uf(this,a),rf&&Uf(this,new Of(this,a)));a=this.a.length;for(var b,c=0;c<a;c++)b=this.a[c],Vf(this,Object.keys(b.c))}v(Tf,Na);function Uf(a,b){var c=Object.keys(b.c);c&&(c.forEach(function(a){var c=b.c[a];c&&(this.c[a]=c.bind(b))},a),a.a.push(b))}Tf.prototype.f=function(a){var b=this.c[a.type];b&&b(a)};
+function Vf(a,b){b.forEach(function(a){B(this.i,a,this.f,this)},a)}function Wf(a,b){b.forEach(function(a){Ea(this.i,a,this.f,this)},a)}function Cf(a,b){for(var c={},d,e=0,f=Xf.length;e<f;e++)d=Xf[e][0],c[d]=a[d]||b[d]||Xf[e][1];return c}function Sf(a,b,c){b.bubbles=!1;Df(a,"pointerleave",b,c)}Tf.prototype.out=function(a,b){a.bubbles=!0;Df(this,"pointerout",a,b)};function Ff(a,b,c){a.out(b,c);var d=b.target,e=b.relatedTarget;d&&e&&d.contains(e)||Sf(a,b,c)}
+function Ef(a,b,c){b.bubbles=!0;Df(a,"pointerover",b,c);var d=b.target,e=b.relatedTarget;d&&e&&d.contains(e)||(b.bubbles=!1,Df(a,"pointerenter",b,c))}function Df(a,b,c,d){a.b(new If(b,d,c))}function Kf(a,b){a.b(new If(b.type,b,b))}Tf.prototype.oa=function(){for(var a=this.a.length,b,c=0;c<a;c++)b=this.a[c],Wf(this,Object.keys(b.c));Na.prototype.oa.call(this)};
+var Xf=[["bubbles",!1],["cancelable",!1],["view",null],["detail",null],["screenX",0],["screenY",0],["clientX",0],["clientY",0],["ctrlKey",!1],["altKey",!1],["shiftKey",!1],["metaKey",!1],["button",0],["relatedTarget",null],["buttons",0],["pointerId",0],["width",0],["height",0],["pressure",0],["tiltX",0],["tiltY",0],["pointerType",""],["hwTimestamp",0],["isPrimary",!1],["type",""],["target",null],["currentTarget",null],["which",0]];function Yf(a){Na.call(this);this.f=a;this.j=0;this.l=!1;this.c=[];this.g=null;a=this.f.f;this.u=0;this.H={};this.i=new Tf(a);this.a=null;this.o=B(this.i,"pointerdown",this.cl,this);this.v=B(this.i,"pointermove",this.So,this)}v(Yf,Na);function Zf(a,b){var c=new cf("click",a.f,b);a.b(c);0!==a.j?(clearTimeout(a.j),a.j=0,c=new cf("dblclick",a.f,b),a.b(c)):a.j=setTimeout(function(){this.j=0;var a=new cf("singleclick",this.f,b);this.b(a)}.bind(a),250)}
+function $f(a,b){"pointerup"==b.type||"pointercancel"==b.type?delete a.H[b.pointerId]:"pointerdown"==b.type&&(a.H[b.pointerId]=!0);a.u=Object.keys(a.H).length}k=Yf.prototype;k.$g=function(a){$f(this,a);var b=new cf("pointerup",this.f,a);this.b(b);!this.l&&0===a.button&&Zf(this,this.g);0===this.u&&(this.c.forEach(ya),this.c.length=0,this.l=!1,this.g=null,Ha(this.a),this.a=null)};
+k.cl=function(a){$f(this,a);var b=new cf("pointerdown",this.f,a);this.b(b);this.g=a;0===this.c.length&&(this.a=new Tf(document),this.c.push(B(this.a,"pointermove",this.Vl,this),B(this.a,"pointerup",this.$g,this),B(this.i,"pointercancel",this.$g,this)))};k.Vl=function(a){if(a.clientX!=this.g.clientX||a.clientY!=this.g.clientY){this.l=!0;var b=new cf("pointerdrag",this.f,a,this.l);this.b(b)}a.preventDefault()};
+k.So=function(a){this.b(new cf(a.type,this.f,a,!(!this.g||a.clientX==this.g.clientX&&a.clientY==this.g.clientY)))};k.oa=function(){this.v&&(ya(this.v),this.v=null);this.o&&(ya(this.o),this.o=null);this.c.forEach(ya);this.c.length=0;this.a&&(Ha(this.a),this.a=null);this.i&&(Ha(this.i),this.i=null);Na.prototype.oa.call(this)};function ag(a,b){Na.call(this);this.Ca=a;this.state=b;this.a=null;this.key=""}v(ag,Na);ag.prototype.s=function(){this.b("change")};ag.prototype.bb=function(){return this.key+"/"+this.Ca};function bg(a){if(!a.a)return a;var b=a.a;do{if(b.W()==cg)return b;b=b.a}while(b);return a}ag.prototype.c=function(){return this.Ca};ag.prototype.W=function(){return this.state};var cg=2;function dg(a,b){this.o=a;this.f=b;this.b=[];this.g=[];this.a={}}dg.prototype.clear=function(){this.b.length=0;this.g.length=0;ua(this.a)};function eg(a){var b=a.b,c=a.g,d=b[0];1==b.length?(b.length=0,c.length=0):(b[0]=b.pop(),c[0]=c.pop(),fg(a,0));b=a.f(d);delete a.a[b];return d}dg.prototype.c=function(a){ha(!(this.f(a)in this.a),31);var b=this.o(a);return Infinity!=b?(this.b.push(a),this.g.push(b),this.a[this.f(a)]=!0,gg(this,0,this.b.length-1),!0):!1};
+function fg(a,b){for(var c=a.b,d=a.g,e=c.length,f=c[b],g=d[b],h=b;b<e>>1;){var l=2*b+1,m=2*b+2,l=m<e&&d[m]<d[l]?m:l;c[b]=c[l];d[b]=d[l];b=l}c[b]=f;d[b]=g;gg(a,h,b)}function gg(a,b,c){var d=a.b;a=a.g;for(var e=d[c],f=a[c];c>b;){var g=c-1>>1;if(a[g]>f)d[c]=d[g],a[c]=a[g],c=g;else break}d[c]=e;a[c]=f}function hg(a){var b=a.o,c=a.b,d=a.g,e=0,f=c.length,g,h,l;for(h=0;h<f;++h)g=c[h],l=b(g),Infinity==l?delete a.a[a.f(g)]:(d[e]=l,c[e++]=g);c.length=e;d.length=e;for(b=(a.b.length>>1)-1;0<=b;b--)fg(a,b)};function ig(a,b){dg.call(this,function(b){return a.apply(null,b)},function(a){return a[0].bb()});this.v=b;this.j=0;this.i={}}v(ig,dg);ig.prototype.c=function(a){var b=dg.prototype.c.call(this,a);b&&B(a[0],"change",this.l,this);return b};ig.prototype.l=function(a){a=a.target;var b=a.W();if(b===cg||3===b||4===b||5===b)Ea(a,"change",this.l,this),a=a.bb(),a in this.i&&(delete this.i[a],--this.j),this.v()};
+function jg(a,b,c){for(var d=0,e,f;a.j<b&&d<c&&0<a.b.length;)e=eg(a)[0],f=e.bb(),0!==e.W()||f in a.i||(a.i[f]=!0,++a.j,++d,e.load())};function kg(a,b,c){this.c=a;this.f=b;this.i=c;this.b=[];this.a=this.g=0};function lg(a){Sa.call(this);this.v=null;this.Ea(!0);this.handleEvent=a.handleEvent}v(lg,Sa);lg.prototype.f=function(){return this.get(mg)};lg.prototype.c=function(){return this.v};lg.prototype.Ea=function(a){this.set(mg,a)};lg.prototype.setMap=function(a){this.v=a};function ng(a,b,c,d){if(void 0!==b){var e=a.Ra(),f=a.fb();void 0!==e&&f&&0<d?a.animate({rotation:b,anchor:c,duration:d,easing:Cb}):a.rotate(b,c)}}function og(a,b,c,d){var e=a.Oa();b=a.constrainResolution(e,b,0);pg(a,b,c,d)}
+function pg(a,b,c,d){if(b){var e=a.Oa(),f=a.fb();void 0!==e&&f&&b!==e&&d?a.animate({resolution:b,anchor:c,duration:d,easing:Cb}):(c&&(c=Nd(a,b,c),a.Mb(c)),a.Oc(b))}}var mg="active";function qg(a){a=a?a:{};this.a=a.delta?a.delta:1;lg.call(this,{handleEvent:rg});this.i=void 0!==a.duration?a.duration:250}v(qg,lg);function rg(a){var b=!1,c=a.originalEvent;if("dblclick"==a.type){var b=a.coordinate,c=c.shiftKey?-this.a:this.a,d=a.map.aa();og(d,c,b,this.i);a.preventDefault();b=!0}return!b};function sg(a){a=a.originalEvent;return a.altKey&&!(a.metaKey||a.ctrlKey)&&a.shiftKey}function tg(a){a=a.originalEvent;return 0==a.button&&!(kf&&lf&&a.ctrlKey)}function ug(a){return"pointermove"==a.type}function vg(a){return"singleclick"==a.type}function wg(a){a=a.originalEvent;return!a.altKey&&!(a.metaKey||a.ctrlKey)&&!a.shiftKey}function xg(a){a=a.originalEvent;return!a.altKey&&!(a.metaKey||a.ctrlKey)&&a.shiftKey}
+function yg(a){a=a.originalEvent.target.tagName;return"INPUT"!==a&&"SELECT"!==a&&"TEXTAREA"!==a}function zg(a){ha(a.b,56);return"mouse"==a.b.pointerType}function Ag(a){a=a.b;return a.isPrimary&&0===a.button};function Bg(a){a=a?a:{};lg.call(this,{handleEvent:a.handleEvent?a.handleEvent:Cg});this.Ve=a.handleDownEvent?a.handleDownEvent:nc;this.bf=a.handleDragEvent?a.handleDragEvent:ea;this.cf=a.handleMoveEvent?a.handleMoveEvent:ea;this.df=a.handleUpEvent?a.handleUpEvent:nc;this.A=!1;this.Z={};this.l=[]}v(Bg,lg);function Dg(a){for(var b=a.length,c=0,d=0,e=0;e<b;e++)c+=a[e].clientX,d+=a[e].clientY;return[c/b,d/b]}
+function Cg(a){if(!(a instanceof cf))return!0;var b=!1,c=a.type;if("pointerdown"===c||"pointerdrag"===c||"pointerup"===c)c=a.b,"pointerup"==a.type?delete this.Z[c.pointerId]:"pointerdown"==a.type?this.Z[c.pointerId]=c:c.pointerId in this.Z&&(this.Z[c.pointerId]=c),this.l=va(this.Z);this.A&&("pointerdrag"==a.type?this.bf(a):"pointerup"==a.type&&(this.A=this.df(a)));"pointerdown"==a.type?(this.A=a=this.Ve(a),b=this.Qc(a)):"pointermove"==a.type&&this.cf(a);return!b}Bg.prototype.Qc=function(a){return a};function Eg(a){Bg.call(this,{handleDownEvent:Fg,handleDragEvent:Gg,handleUpEvent:Hg});a=a?a:{};this.a=a.kinetic;this.i=null;this.o=a.condition?a.condition:wg;this.j=!1}v(Eg,Bg);function Gg(a){var b=Dg(this.l);this.a&&this.a.b.push(b[0],b[1],Date.now());if(this.i){var c=this.i[0]-b[0],d=b[1]-this.i[1];a=a.map.aa();var e=a.W(),c=[c,d];xb(c,e.resolution);wb(c,e.rotation);rb(c,e.center);c=a.Zd(c);a.Mb(c)}this.i=b}
+function Hg(a){var b=a.map;a=b.aa();if(0===this.l.length){var c;if(c=!this.j&&this.a)if(c=this.a,6>c.b.length)c=!1;else{var d=Date.now()-c.i,e=c.b.length-3;if(c.b[e+2]<d)c=!1;else{for(var f=e-3;0<f&&c.b[f+2]>d;)f-=3;var d=c.b[e+2]-c.b[f+2],g=c.b[e]-c.b[f],e=c.b[e+1]-c.b[f+1];c.g=Math.atan2(e,g);c.a=Math.sqrt(g*g+e*e)/d;c=c.a>c.f}}c&&(c=this.a,c=(c.f-c.a)/c.c,e=this.a.g,f=a.fb(),f=b.Ga(f),b=b.Sa([f[0]-c*Math.cos(e),f[1]-c*Math.sin(e)]),a.animate({center:a.Zd(b),duration:500,easing:Cb}));Jd(a,1,-1);
+return!1}this.i=null;return!0}function Fg(a){if(0<this.l.length&&this.o(a)){var b=a.map.aa();this.i=null;this.A||Jd(b,1,1);b.Mb(a.frameState.viewState.center);this.a&&(a=this.a,a.b.length=0,a.g=0,a.a=0);this.j=1<this.l.length;return!0}return!1}Eg.prototype.Qc=nc;function Ig(a){a=a?a:{};Bg.call(this,{handleDownEvent:Jg,handleDragEvent:Kg,handleUpEvent:Lg});this.i=a.condition?a.condition:sg;this.a=void 0;this.j=void 0!==a.duration?a.duration:250}v(Ig,Bg);function Kg(a){if(zg(a)){var b=a.map,c=b.nb();a=a.pixel;c=Math.atan2(c[1]/2-a[1],a[0]-c[0]/2);if(void 0!==this.a){a=c-this.a;var b=b.aa(),d=b.Ra();ng(b,d-a)}this.a=c}}function Lg(a){if(!zg(a))return!0;a=a.map.aa();Jd(a,1,-1);var b=a.Ra(),c=this.j,b=a.constrainRotation(b,0);ng(a,b,void 0,c);return!1}
+function Jg(a){return zg(a)&&tg(a)&&this.i(a)?(Jd(a.map.aa(),1,1),this.a=void 0,!0):!1}Ig.prototype.Qc=nc;function Mg(a){this.f=null;this.a=document.createElement("div");this.a.style.position="absolute";this.a.className="ol-box "+a;this.g=this.c=this.b=null}v(Mg,Ga);Mg.prototype.oa=function(){this.setMap(null)};function Ng(a){var b=a.c,c=a.g;a=a.a.style;a.left=Math.min(b[0],c[0])+"px";a.top=Math.min(b[1],c[1])+"px";a.width=Math.abs(c[0]-b[0])+"px";a.height=Math.abs(c[1]-b[1])+"px"}
+Mg.prototype.setMap=function(a){if(this.b){this.b.A.removeChild(this.a);var b=this.a.style;b.left=b.top=b.width=b.height="inherit"}(this.b=a)&&this.b.A.appendChild(this.a)};function Og(a){var b=a.c,c=a.g,b=[b,[b[0],c[1]],c,[c[0],b[1]]].map(a.b.Sa,a.b);b[4]=b[0].slice();a.f?a.f.qa([b]):a.f=new E([b])}Mg.prototype.V=function(){return this.f};function Pg(a){Bg.call(this,{handleDownEvent:Qg,handleDragEvent:Rg,handleUpEvent:Sg});a=a?a:{};this.a=new Mg(a.className||"ol-dragbox");this.i=null;this.u=a.condition?a.condition:mc;this.o=a.boxEndCondition?a.boxEndCondition:Tg}v(Pg,Bg);function Tg(a,b,c){a=c[0]-b[0];b=c[1]-b[1];return 64<=a*a+b*b}function Rg(a){if(zg(a)){var b=this.a,c=a.pixel;b.c=this.i;b.g=c;Og(b);Ng(b);this.b(new Ug(Vg,a.coordinate,a))}}Pg.prototype.V=function(){return this.a.V()};Pg.prototype.j=ea;
+function Sg(a){if(!zg(a))return!0;this.a.setMap(null);this.o(a,this.i,a.pixel)&&(this.j(a),this.b(new Ug(Wg,a.coordinate,a)));return!1}function Qg(a){if(zg(a)&&tg(a)&&this.u(a)){this.i=a.pixel;this.a.setMap(a.map);var b=this.a,c=this.i;b.c=this.i;b.g=c;Og(b);Ng(b);this.b(new Ug(Xg,a.coordinate,a));return!0}return!1}var Xg="boxstart",Vg="boxdrag",Wg="boxend";function Ug(a,b,c){Ia.call(this,a);this.coordinate=b;this.mapBrowserEvent=c}v(Ug,Ia);function Yg(a){a=a?a:{};var b=a.condition?a.condition:xg;this.C=void 0!==a.duration?a.duration:200;this.D=void 0!==a.out?a.out:!1;Pg.call(this,{condition:b,className:a.className||"ol-dragzoom"})}v(Yg,Pg);
+Yg.prototype.j=function(){var a=this.v,b=a.aa(),c=a.nb(),d=this.V().G();if(this.D){var e=b.Uc(c),d=[a.Ga(Yb(d)),a.Ga($b(d))],a=Rb(Infinity,Infinity,-Infinity,-Infinity,void 0),f,g;f=0;for(g=d.length;f<g;++f)Ib(a,d[f]);kc(e,1/Pd(a,c));d=e}c=b.constrainResolution(Pd(d,c));b.animate({resolution:c,center:gc(d),duration:this.C,easing:Cb})};function Zg(a){lg.call(this,{handleEvent:$g});a=a||{};this.a=function(a){return wg(a)&&yg(a)};this.i=void 0!==a.condition?a.condition:this.a;this.j=void 0!==a.duration?a.duration:100;this.l=void 0!==a.pixelDelta?a.pixelDelta:128}v(Zg,lg);
+function $g(a){var b=!1;if("keydown"==a.type){var c=a.originalEvent.keyCode;if(this.i(a)&&(40==c||37==c||39==c||38==c)){var b=a.map.aa(),d=b.Oa()*this.l,e=0,f=0;40==c?f=-d:37==c?e=-d:39==c?e=d:f=d;d=[e,f];wb(d,b.Ra());c=this.j;if(e=b.fb())d=b.Zd([e[0]+d[0],e[1]+d[1]]),c?b.animate({duration:c,easing:Eb,center:d}):b.Mb(d);a.preventDefault();b=!0}}return!b};function ah(a){lg.call(this,{handleEvent:bh});a=a?a:{};this.i=a.condition?a.condition:yg;this.a=a.delta?a.delta:1;this.j=void 0!==a.duration?a.duration:100}v(ah,lg);function bh(a){var b=!1;if("keydown"==a.type||"keypress"==a.type){var c=a.originalEvent.charCode;!this.i(a)||43!=c&&45!=c||(b=43==c?this.a:-this.a,c=a.map.aa(),og(c,b,void 0,this.j),a.preventDefault(),b=!0)}return!b};function ch(a){lg.call(this,{handleEvent:dh});a=a||{};this.j=0;this.L=void 0!==a.duration?a.duration:250;this.U=void 0!==a.timeout?a.timeout:80;this.A=void 0!==a.useAnchor?a.useAnchor:!0;this.a=null;this.o=this.l=this.u=this.i=void 0}v(ch,lg);
+function dh(a){var b=a.type;if("wheel"!==b&&"mousewheel"!==b)return!0;a.preventDefault();var b=a.map,c=a.originalEvent;this.A&&(this.a=a.coordinate);var d;"wheel"==a.type?(d=c.deltaY,hf&&c.deltaMode===WheelEvent.DOM_DELTA_PIXEL&&(d/=mf),c.deltaMode===WheelEvent.DOM_DELTA_LINE&&(d*=40)):"mousewheel"==a.type&&(d=-c.wheelDeltaY,jf&&(d/=3));if(0===d)return!1;a=Date.now();void 0===this.i&&(this.i=a);if(!this.l||400<a-this.i)this.l=4>Math.abs(d)?eh:fh;if(this.l===eh){b=b.aa();this.o?clearTimeout(this.o):
+Jd(b,1,1);this.o=setTimeout(this.C.bind(this),400);d=b.Oa()*Math.pow(2,d/300);var c=b.j,e=b.a,f=0;d<c?(d=Math.max(d,c/1.5),f=1):d>e&&(d=Math.min(d,1.5*e),f=-1);if(this.a){var g=Nd(b,d,this.a);b.Mb(g)}b.Oc(d);0<f?b.animate({resolution:c,easing:Cb,anchor:this.a,duration:500}):0>f&&b.animate({resolution:e,easing:Cb,anchor:this.a,duration:500});this.i=a;return!1}this.j+=d;a=Math.max(this.U-(a-this.i),0);clearTimeout(this.u);this.u=setTimeout(this.D.bind(this,b),a);return!1}
+ch.prototype.C=function(){this.o=void 0;Jd(this.v.aa(),1,-1)};ch.prototype.D=function(a){a=a.aa();0<Md(a)[Kd]&&Ld(a);og(a,-ia(this.j,-1,1),this.a,this.L);this.l=void 0;this.j=0;this.a=null;this.u=this.i=void 0};ch.prototype.P=function(a){this.A=a;a||(this.a=null)};var eh="trackpad",fh="wheel";function gh(a){Bg.call(this,{handleDownEvent:hh,handleDragEvent:ih,handleUpEvent:jh});a=a||{};this.i=null;this.j=void 0;this.a=!1;this.o=0;this.C=void 0!==a.threshold?a.threshold:.3;this.u=void 0!==a.duration?a.duration:250}v(gh,Bg);
+function ih(a){var b=0,c=this.l[0],d=this.l[1],c=Math.atan2(d.clientY-c.clientY,d.clientX-c.clientX);void 0!==this.j&&(b=c-this.j,this.o+=b,!this.a&&Math.abs(this.o)>this.C&&(this.a=!0));this.j=c;a=a.map;c=a.f.getBoundingClientRect();d=Dg(this.l);d[0]-=c.left;d[1]-=c.top;this.i=a.Sa(d);this.a&&(c=a.aa(),d=c.Ra(),a.render(),ng(c,d+b,this.i))}
+function jh(a){if(2>this.l.length){a=a.map.aa();Jd(a,1,-1);if(this.a){var b=a.Ra(),c=this.i,d=this.u,b=a.constrainRotation(b,0);ng(a,b,c,d)}return!1}return!0}function hh(a){return 2<=this.l.length?(a=a.map,this.i=null,this.j=void 0,this.a=!1,this.o=0,this.A||Jd(a.aa(),1,1),!0):!1}gh.prototype.Qc=nc;function kh(a){Bg.call(this,{handleDownEvent:lh,handleDragEvent:nh,handleUpEvent:oh});a=a?a:{};this.o=a.constrainResolution||!1;this.i=null;this.u=void 0!==a.duration?a.duration:400;this.a=void 0;this.j=1}v(kh,Bg);
+function nh(a){var b=1,c=this.l[0],d=this.l[1],e=c.clientX-d.clientX,c=c.clientY-d.clientY,e=Math.sqrt(e*e+c*c);void 0!==this.a&&(b=this.a/e);this.a=e;1!=b&&(this.j=b);a=a.map;var e=a.aa(),c=e.Oa(),d=a.f.getBoundingClientRect(),f=Dg(this.l);f[0]-=d.left;f[1]-=d.top;this.i=a.Sa(f);a.render();pg(e,c*b,this.i)}function oh(a){if(2>this.l.length){a=a.map.aa();Jd(a,1,-1);if(this.o){var b=a.Oa(),c=this.i,d=this.u,b=a.constrainResolution(b,0,this.j-1);pg(a,b,c,d)}return!1}return!0}
+function lh(a){return 2<=this.l.length?(a=a.map,this.i=null,this.a=void 0,this.j=1,this.A||Jd(a.aa(),1,1),!0):!1}kh.prototype.Qc=nc;function ph(a){a=a?a:{};var b=new qe,c=new kg(-.005,.05,100);(void 0!==a.altShiftDragRotate?a.altShiftDragRotate:1)&&b.push(new Ig);(void 0!==a.doubleClickZoom?a.doubleClickZoom:1)&&b.push(new qg({delta:a.zoomDelta,duration:a.zoomDuration}));(void 0!==a.dragPan?a.dragPan:1)&&b.push(new Eg({kinetic:c}));(void 0!==a.pinchRotate?a.pinchRotate:1)&&b.push(new gh);(void 0!==a.pinchZoom?a.pinchZoom:1)&&b.push(new kh({duration:a.zoomDuration}));if(void 0!==a.keyboard?a.keyboard:1)b.push(new Zg),b.push(new ah({delta:a.zoomDelta,
+duration:a.zoomDuration}));(void 0!==a.mouseWheelZoom?a.mouseWheelZoom:1)&&b.push(new ch({duration:a.zoomDuration}));(void 0!==a.shiftDragZoom?a.shiftDragZoom:1)&&b.push(new Yg({duration:a.zoomDuration}));return b};function qh(a){Sa.call(this);var b=ta({},a);b[rh]=void 0!==a.opacity?a.opacity:1;b[sh]=void 0!==a.visible?a.visible:!0;b[th]=void 0!==a.zIndex?a.zIndex:0;b[uh]=void 0!==a.maxResolution?a.maxResolution:Infinity;b[vh]=void 0!==a.minResolution?a.minResolution:0;this.I(b);this.a={layer:this,me:!0}}v(qh,Sa);function wh(a){a.a.opacity=ia(a.Yb(),0,1);a.a.Ei=a.uf();a.a.visible=a.Fb();a.a.extent=a.G();a.a.zIndex=a.Zb();a.a.maxResolution=a.Wb();a.a.minResolution=Math.max(a.Xb(),0);return a.a}k=qh.prototype;
+k.G=function(){return this.get(xh)};k.Wb=function(){return this.get(uh)};k.Xb=function(){return this.get(vh)};k.Yb=function(){return this.get(rh)};k.Fb=function(){return this.get(sh)};k.Zb=function(){return this.get(th)};k.kc=function(a){this.set(xh,a)};k.pc=function(a){this.set(uh,a)};k.qc=function(a){this.set(vh,a)};k.lc=function(a){this.set(rh,a)};k.mc=function(a){this.set(sh,a)};k.nc=function(a){this.set(th,a)};var rh="opacity",sh="visible",xh="extent",th="zIndex",uh="maxResolution",vh="minResolution";function yh(a){var b=a||{};a=ta({},b);delete a.layers;b=b.layers;qh.call(this,a);this.c=[];this.f={};B(this,Ua(zh),this.Wk,this);b?Array.isArray(b)?b=new qe(b.slice()):ha(b instanceof qe,43):b=new qe;this.yh(b)}v(yh,qh);k=yh.prototype;k.ie=function(){this.Fb()&&this.s()};
+k.Wk=function(){this.c.forEach(ya);this.c.length=0;var a=this.cd();this.c.push(B(a,ue,this.Vk,this),B(a,ve,this.Xk,this));for(var b in this.f)this.f[b].forEach(ya);ua(this.f);var a=a.a,c,d;b=0;for(c=a.length;b<c;b++)d=a[b],this.f[x(d).toString()]=[B(d,Xa,this.ie,this),B(d,"change",this.ie,this)];this.s()};k.Vk=function(a){a=a.element;var b=x(a).toString();this.f[b]=[B(a,Xa,this.ie,this),B(a,"change",this.ie,this)];this.s()};
+k.Xk=function(a){a=x(a.element).toString();this.f[a].forEach(ya);delete this.f[a];this.s()};k.cd=function(){return this.get(zh)};k.yh=function(a){this.set(zh,a)};
+k.sf=function(a){var b=void 0!==a?a:[],c=b.length;this.cd().forEach(function(a){a.sf(b)});a=wh(this);var d,e;for(d=b.length;c<d;c++)e=b[c],e.opacity*=a.opacity,e.visible=e.visible&&a.visible,e.maxResolution=Math.min(e.maxResolution,a.maxResolution),e.minResolution=Math.max(e.minResolution,a.minResolution),void 0!==a.extent&&(e.extent=void 0!==e.extent?ic(e.extent,a.extent):a.extent);return b};k.uf=function(){return"ready"};var zh="layers";function Ah(a){sc.call(this,{code:a,units:"m",extent:Bh,global:!0,worldExtent:Ch,getPointResolution:function(a,c){return a/ja(c[1]/6378137)}})}v(Ah,sc);var Dh=6378137*Math.PI,Bh=[-Dh,-Dh,Dh,Dh],Ch=[-180,-85,180,85],Ec="EPSG:3857 EPSG:102100 EPSG:102113 EPSG:900913 urn:ogc:def:crs:EPSG:6.18:3:3857 urn:ogc:def:crs:EPSG::3857 http://www.opengis.net/gml/srs/epsg.xml#3857".split(" ").map(function(a){return new Ah(a)});
+function Fc(a,b,c){var d=a.length;c=1<c?c:2;void 0===b&&(2<c?b=a.slice():b=Array(d));for(var e=0;e<d;e+=c){b[e]=Dh*a[e]/180;var f=6378137*Math.log(Math.tan(Math.PI*(a[e+1]+90)/360));f>Dh?f=Dh:f<-Dh&&(f=-Dh);b[e+1]=f}return b}function Gc(a,b,c){var d=a.length;c=1<c?c:2;void 0===b&&(2<c?b=a.slice():b=Array(d));for(var e=0;e<d;e+=c)b[e]=180*a[e]/Dh,b[e+1]=360*Math.atan(Math.exp(a[e+1]/6378137))/Math.PI-90;return b};var Eh=new oc(6378137);function Fh(a,b){sc.call(this,{code:a,units:"degrees",extent:Gh,axisOrientation:b,global:!0,metersPerUnit:Hh,worldExtent:Gh})}v(Fh,sc);var Gh=[-180,-90,180,90],Hh=Math.PI*Eh.radius/180,Hc=[new Fh("CRS:84"),new Fh("EPSG:4326","neu"),new Fh("urn:ogc:def:crs:EPSG::4326","neu"),new Fh("urn:ogc:def:crs:EPSG:6.6:4326","neu"),new Fh("urn:ogc:def:crs:OGC:1.3:CRS84"),new Fh("urn:ogc:def:crs:OGC:2:84"),new Fh("http://www.opengis.net/gml/srs/epsg.xml#4326","neu"),new Fh("urn:x-ogc:def:crs:EPSG:4326","neu")];function Ih(){Ac(Ec);Ac(Hc);Dc()};function Jh(a,b,c,d,e){Ia.call(this,a);this.vectorContext=b;this.frameState=c;this.context=d;this.glContext=e}v(Jh,Ia);function Kh(a){var b=ta({},a);delete b.source;qh.call(this,b);this.A=this.v=this.o=null;a.map&&this.setMap(a.map);B(this,Ua("source"),this.il,this);this.Pc(a.source?a.source:null)}v(Kh,qh);function Lh(a,b){return a.visible&&b>=a.minResolution&&b<a.maxResolution}k=Kh.prototype;k.sf=function(a){a=a?a:[];a.push(wh(this));return a};k.la=function(){return this.get("source")||null};k.uf=function(){var a=this.la();return a?a.W():"undefined"};k.Rm=function(){this.s()};
+k.il=function(){this.A&&(ya(this.A),this.A=null);var a=this.la();a&&(this.A=B(a,"change",this.Rm,this));this.s()};k.setMap=function(a){this.o&&(ya(this.o),this.o=null);a||this.s();this.v&&(ya(this.v),this.v=null);a&&(this.o=B(a,"precompose",function(a){var c=wh(this);c.me=!1;c.zIndex=Infinity;a.frameState.layerStatesArray.push(c);a.frameState.layerStates[x(this)]=c},this),this.v=B(this,"change",a.render,a),this.s())};k.Pc=function(a){this.set("source",a)};function Mh(){this.b={};this.a=0}Mh.prototype.clear=function(){this.b={};this.a=0};Mh.prototype.get=function(a,b,c){a=b+":"+a+":"+(c?Ae(c):"null");return a in this.b?this.b[a]:null};Mh.prototype.set=function(a,b,c,d){this.b[b+":"+a+":"+(c?Ae(c):"null")]=d;++this.a};var Nh=new Mh;var Oh=Array(6);function Ph(){return[1,0,0,1,0,0]}function Qh(a){return Rh(a,1,0,0,1,0,0)}function Sh(a,b){var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],l=b[0],m=b[1],p=b[2],n=b[3],q=b[4],r=b[5];a[0]=c*l+e*m;a[1]=d*l+f*m;a[2]=c*p+e*n;a[3]=d*p+f*n;a[4]=c*q+e*r+g;a[5]=d*q+f*r+h;return a}function Rh(a,b,c,d,e,f,g){a[0]=b;a[1]=c;a[2]=d;a[3]=e;a[4]=f;a[5]=g;return a}function Th(a,b){a[0]=b[0];a[1]=b[1];a[2]=b[2];a[3]=b[3];a[4]=b[4];a[5]=b[5];return a}
+function Uh(a,b){var c=b[0],d=b[1];b[0]=a[0]*c+a[2]*d+a[4];b[1]=a[1]*c+a[3]*d+a[5];return b}function Vh(a,b){var c=Math.cos(b),d=Math.sin(b);Sh(a,Rh(Oh,c,d,-d,c,0,0))}function Wh(a,b,c){return Sh(a,Rh(Oh,b,0,0,c,0,0))}function Xh(a,b,c){Sh(a,Rh(Oh,1,0,0,1,b,c))}function Yh(a,b,c,d,e,f,g,h){var l=Math.sin(f);f=Math.cos(f);a[0]=d*f;a[1]=e*l;a[2]=-d*l;a[3]=e*f;a[4]=g*d*f-h*d*l+b;a[5]=g*e*l+h*e*f+c;return a}
+function Zh(a){var b=a[0]*a[3]-a[1]*a[2];ha(0!==b,32);var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5];a[0]=f/b;a[1]=-d/b;a[2]=-e/b;a[3]=c/b;a[4]=(e*h-f*g)/b;a[5]=-(c*h-d*g)/b;return a};function $h(a,b){this.l=b;this.f={};this.v={}}v($h,Ga);function ai(a){var b=a.viewState,c=a.coordinateToPixelTransform,d=a.pixelToCoordinateTransform;Yh(c,a.size[0]/2,a.size[1]/2,1/b.resolution,-1/b.resolution,-b.rotation,-b.center[0],-b.center[1]);Zh(Th(d,c))}k=$h.prototype;k.oa=function(){for(var a in this.f)Ha(this.f[a])};function bi(){if(32<Nh.a){var a=0,b,c;for(b in Nh.b)c=Nh.b[b],0!==(a++&3)||Oa(c)||(delete Nh.b[b],--Nh.a)}}
+k.Ba=function(a,b,c,d,e,f,g){function h(a,c){var f=x(a).toString(),g=b.layerStates[x(c)].me;if(!(f in b.skippedFeatureUids)||g)return d.call(e,a,g?c:null)}var l,m=b.viewState,p=m.resolution,n=m.projection,m=a;if(n.a){var n=n.G(),q=dc(n),r=a[0];if(r<n[0]||r>n[2])m=[r+q*Math.ceil((n[0]-r)/q),a[1]]}n=b.layerStatesArray;for(q=n.length-1;0<=q;--q){var u=n[q],r=u.layer;if(Lh(u,p)&&f.call(g,r)&&(u=ci(this,r),r.la()&&(l=u.Ba(r.la().D?m:a,b,c,h,e)),l))return l}};
+k.Ch=function(a,b,c,d,e){return void 0!==this.Ba(a,b,c,mc,this,d,e)};function ci(a,b){var c=x(b).toString();if(c in a.f)return a.f[c];var d=a.Ag(b);a.f[c]=d;a.v[c]=B(d,"change",a.Uk,a);return d}k.Uk=function(){this.l.render()};k.ag=ea;k.Yo=function(a,b){for(var c in this.f)if(!(b&&c in b.layerStates)){var d=c,e=this.f[d];delete this.f[d];ya(this.v[d]);delete this.v[d];Ha(e)}};function di(a,b){for(var c in a.f)if(!(c in b.layerStates)){b.postRenderFunctions.push(a.Yo.bind(a));break}}
+function fb(a,b){return a.zIndex-b.zIndex};function ei(a){Kh.call(this,a?a:{})}v(ei,Kh);function F(a){a=a?a:{};var b=ta({},a);delete b.preload;delete b.useInterimTilesOnError;Kh.call(this,b);this.l(void 0!==a.preload?a.preload:0);this.C(void 0!==a.useInterimTilesOnError?a.useInterimTilesOnError:!0)}v(F,Kh);F.prototype.f=function(){return this.get(fi)};F.prototype.l=function(a){this.set(fi,a)};F.prototype.c=function(){return this.get(gi)};F.prototype.C=function(a){this.set(gi,a)};var fi="preload",gi="useInterimTilesOnError";function hi(a,b,c,d,e){Na.call(this);this.j=e;this.extent=a;this.f=c;this.resolution=b;this.state=d}v(hi,Na);hi.prototype.s=function(){this.b("change")};hi.prototype.G=function(){return this.extent};hi.prototype.W=function(){return this.state};function ii(a,b,c,d,e,f,g){hi.call(this,a,b,c,ji,d);this.o=e;this.g=new Image;null!==f&&(this.g.crossOrigin=f);this.i={};this.c=null;this.state=ji;this.l=g}v(ii,hi);ii.prototype.a=function(a){if(void 0!==a){var b;a=x(a);if(a in this.i)return this.i[a];wa(this.i)?b=this.g:b=this.g.cloneNode(!1);return this.i[a]=b}return this.g};ii.prototype.v=function(){this.state=ki;this.c.forEach(ya);this.c=null;this.s()};
+ii.prototype.H=function(){void 0===this.resolution&&(this.resolution=ec(this.extent)/this.g.height);this.state=li;this.c.forEach(ya);this.c=null;this.s()};ii.prototype.load=function(){if(this.state==ji||this.state==ki)this.state=mi,this.s(),this.c=[Da(this.g,"error",this.v,this),Da(this.g,"load",this.H,this)],this.l(this,this.o)};var ji=0,mi=1,li=2,ki=3;var ni=[0,0,0,1],oi=[],pi=[0,0,0,1];function qi(a,b,c,d){0!==b&&(a.translate(c,d),a.rotate(b),a.translate(-c,-d))};function ri(a){this.l=a.opacity;this.H=a.rotateWithView;this.o=a.rotation;this.i=a.scale;this.u=a.snapToPixel}k=ri.prototype;k.ye=function(){return this.l};k.ze=function(){return this.H};k.Ae=function(){return this.o};k.Be=function(){return this.i};k.ee=function(){return this.u};k.dd=function(a){this.l=a};k.Ce=function(a){this.o=a};k.ed=function(a){this.i=a};function si(a){this.C=this.A=this.j=null;this.f=void 0!==a.fill?a.fill:null;this.P=[0,0];this.b=a.points;this.a=void 0!==a.radius?a.radius:a.radius1;this.c=void 0!==a.radius2?a.radius2:this.a;this.v=void 0!==a.angle?a.angle:0;this.g=void 0!==a.stroke?a.stroke:null;this.L=this.va=this.U=this.ra=null;this.D=a.atlasManager;ti(this,this.D);ri.call(this,{opacity:1,rotateWithView:void 0!==a.rotateWithView?a.rotateWithView:!1,rotation:void 0!==a.rotation?a.rotation:0,scale:1,snapToPixel:void 0!==a.snapToPixel?
+a.snapToPixel:!0})}v(si,ri);k=si.prototype;k.clone=function(){var a=new si({fill:this.f?this.f.clone():void 0,points:this.c!==this.a?this.b/2:this.b,radius:this.a,radius2:this.c,angle:this.v,snapToPixel:this.u,stroke:this.g?this.g.clone():void 0,rotation:this.o,rotateWithView:this.H,atlasManager:this.D});a.dd(this.l);a.ed(this.i);return a};k.Ac=function(){return this.ra};k.Nh=function(){return this.v};k.Oh=function(){return this.f};k.Lf=function(){return this.C};k.Ic=function(){return this.A};
+k.de=function(){return this.va};k.xe=function(){return li};k.Jc=function(){return this.P};k.Ph=function(){return this.b};k.Qh=function(){return this.a};k.Vg=function(){return this.c};k.ac=function(){return this.U};k.Rh=function(){return this.g};k.eh=ea;k.load=ea;k.Ii=ea;
+function ti(a,b){var c,d="",e="",f=0,g=null,h,l=0;a.g&&(h=Ce(a.g.b),l=a.g.f,void 0===l&&(l=1),g=a.g.g,nf||(g=null),e=a.g.i,void 0===e&&(e="round"),d=a.g.c,void 0===d&&(d="round"),f=a.g.j,void 0===f&&(f=10));var m=2*(a.a+l)+1,d={strokeStyle:h,Fi:l,size:m,lineCap:d,lineDash:g,lineJoin:e,miterLimit:f};void 0===b?(e=De(m,m),a.A=e.canvas,c=m=a.A.width,a.Eg(d,e,0,0),a.L=[d.size,d.size],a.f?a.C=a.A:(e=De(d.size,d.size),a.C=e.canvas,a.Dg(d,e,0,0))):(m=Math.round(m),(e=!a.f)&&(c=a.Dg.bind(a,d)),a.g?(f=a.g,
+void 0===f.a&&(f.a="s",f.a=f.b?"string"===typeof f.b?f.a+f.b:f.a+x(f.b).toString():f.a+"-",f.a+=","+(void 0!==f.c?f.c.toString():"-")+","+(f.g?f.g.toString():"-")+","+(void 0!==f.i?f.i:"-")+","+(void 0!==f.j?f.j.toString():"-")+","+(void 0!==f.f?f.f.toString():"-")),f=f.a):f="-",a.f?(g=a.f,void 0===g.a&&(g.a=g.b instanceof CanvasPattern||g.b instanceof CanvasGradient?x(g.b).toString():"f"+(g.b?Ae(g.b):"-")),g=g.a):g="-",a.j&&f==a.j[1]&&g==a.j[2]&&a.a==a.j[3]&&a.c==a.j[4]&&a.v==a.j[5]&&a.b==a.j[6]||
+(a.j=["r"+f+g+(void 0!==a.a?a.a.toString():"-")+(void 0!==a.c?a.c.toString():"-")+(void 0!==a.v?a.v.toString():"-")+(void 0!==a.b?a.b.toString():"-"),f,g,a.a,a.c,a.v,a.b]),d=b.add(a.j[0],m,m,a.Eg.bind(a,d),c),a.A=d.image,a.P=[d.offsetX,d.offsetY],c=d.image.width,e?(a.C=d.yf,a.L=[d.yf.width,d.yf.height]):(a.C=a.A,a.L=[c,c]));a.ra=[m/2,m/2];a.U=[m,m];a.va=[c,c]}
+k.Eg=function(a,b,c,d){var e;b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();if(Infinity===this.b)b.arc(a.size/2,a.size/2,this.a,0,2*Math.PI,!0);else for(this.c!==this.a&&(this.b*=2),c=0;c<=this.b;c++)d=2*c*Math.PI/this.b-Math.PI/2+this.v,e=0===c%2?this.a:this.c,b.lineTo(a.size/2+e*Math.cos(d),a.size/2+e*Math.sin(d));this.f&&(b.fillStyle=Ce(this.f.b),b.fill());this.g&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.Fi,a.lineDash&&b.setLineDash(a.lineDash),b.lineCap=a.lineCap,b.lineJoin=a.lineJoin,
+b.miterLimit=a.miterLimit,b.stroke());b.closePath()};
+k.Dg=function(a,b,c,d){b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();if(Infinity===this.b)b.arc(a.size/2,a.size/2,this.a,0,2*Math.PI,!0);else{this.c!==this.a&&(this.b*=2);var e;for(c=0;c<=this.b;c++)e=2*c*Math.PI/this.b-Math.PI/2+this.v,d=0===c%2?this.a:this.c,b.lineTo(a.size/2+d*Math.cos(e),a.size/2+d*Math.sin(e))}b.fillStyle=ni;b.fill();this.g&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.Fi,a.lineDash&&b.setLineDash(a.lineDash),b.stroke());b.closePath()};function ui(a){a=a||{};si.call(this,{points:Infinity,fill:a.fill,radius:a.radius,snapToPixel:a.snapToPixel,stroke:a.stroke,atlasManager:a.atlasManager})}v(ui,si);ui.prototype.clone=function(){var a=new ui({fill:this.f?this.f.clone():void 0,stroke:this.g?this.g.clone():void 0,radius:this.a,snapToPixel:this.u,atlasManager:this.D});a.dd(this.l);a.ed(this.i);return a};ui.prototype.Qa=function(a){this.a=a;ti(this,this.D)};function vi(a){a=a||{};this.b=void 0!==a.color?a.color:null;this.a=void 0}vi.prototype.clone=function(){var a=this.b;return new vi({color:a&&a.slice?a.slice():a||void 0})};vi.prototype.g=function(){return this.b};vi.prototype.f=function(a){this.b=a;this.a=void 0};function wi(a){a=a||{};this.b=void 0!==a.color?a.color:null;this.c=a.lineCap;this.g=void 0!==a.lineDash?a.lineDash:null;this.i=a.lineJoin;this.j=a.miterLimit;this.f=a.width;this.a=void 0}k=wi.prototype;k.clone=function(){var a=this.b;return new wi({color:a&&a.slice?a.slice():a||void 0,lineCap:this.c,lineDash:this.g?this.g.slice():void 0,lineJoin:this.i,miterLimit:this.j,width:this.f})};k.In=function(){return this.b};k.kk=function(){return this.c};k.Jn=function(){return this.g};k.lk=function(){return this.i};
+k.rk=function(){return this.j};k.Kn=function(){return this.f};k.Ln=function(a){this.b=a;this.a=void 0};k.ip=function(a){this.c=a;this.a=void 0};k.setLineDash=function(a){this.g=a;this.a=void 0};k.jp=function(a){this.i=a;this.a=void 0};k.kp=function(a){this.j=a;this.a=void 0};k.np=function(a){this.f=a;this.a=void 0};function xi(a){a=a||{};this.i=null;this.c=yi;void 0!==a.geometry&&this.Pa(a.geometry);this.f=void 0!==a.fill?a.fill:null;this.a=void 0!==a.image?a.image:null;this.g=void 0!==a.stroke?a.stroke:null;this.j=void 0!==a.text?a.text:null;this.b=a.zIndex}k=xi.prototype;k.clone=function(){var a=this.V();a&&a.clone&&(a=a.clone());return new xi({geometry:a,fill:this.f?this.f.clone():void 0,image:this.a?this.a.clone():void 0,stroke:this.g?this.g.clone():void 0,text:this.Ka()?this.Ka().clone():void 0,zIndex:this.b})};
+k.V=function(){return this.i};k.fk=function(){return this.c};k.Mn=function(){return this.f};k.Qn=function(a){this.f=a};k.Nn=function(){return this.a};k.Rn=function(a){this.a=a};k.On=function(){return this.g};k.Sn=function(a){this.g=a};k.Ka=function(){return this.j};k.Tn=function(a){this.j=a};k.Pn=function(){return this.b};k.Pa=function(a){"function"===typeof a?this.c=a:"string"===typeof a?this.c=function(b){return b.get(a)}:a?a&&(this.c=function(){return a}):this.c=yi;this.i=a};
+k.Un=function(a){this.b=a};function zi(a){if("function"!==typeof a){var b;Array.isArray(a)?b=a:(ha(a instanceof xi,41),b=[a]);a=function(){return b}}return a}var Ai=null;function Bi(){if(!Ai){var a=new vi({color:"rgba(255,255,255,0.4)"}),b=new wi({color:"#3399CC",width:1.25});Ai=[new xi({image:new ui({fill:a,stroke:b,radius:5}),fill:a,stroke:b})]}return Ai}
+function Ci(){var a={},b=[255,255,255,1],c=[0,153,255,1];a.Polygon=[new xi({fill:new vi({color:[255,255,255,.5]})})];a.MultiPolygon=a.Polygon;a.LineString=[new xi({stroke:new wi({color:b,width:5})}),new xi({stroke:new wi({color:c,width:3})})];a.MultiLineString=a.LineString;a.Circle=a.Polygon.concat(a.LineString);a.Point=[new xi({image:new ui({radius:6,fill:new vi({color:c}),stroke:new wi({color:b,width:1.5})}),zIndex:Infinity})];a.MultiPoint=a.Point;a.GeometryCollection=a.Polygon.concat(a.LineString,
+a.Point);return a}function yi(a){return a.V()};function G(a){a=a?a:{};var b=ta({},a);delete b.style;delete b.renderBuffer;delete b.updateWhileAnimating;delete b.updateWhileInteracting;Kh.call(this,b);this.i=void 0!==a.renderBuffer?a.renderBuffer:100;this.C=null;this.j=void 0;this.l(a.style);this.Z=void 0!==a.updateWhileAnimating?a.updateWhileAnimating:!1;this.fa=void 0!==a.updateWhileInteracting?a.updateWhileInteracting:!1}v(G,Kh);G.prototype.D=function(){return this.C};G.prototype.L=function(){return this.j};
+G.prototype.l=function(a){this.C=void 0!==a?a:Bi;this.j=null===a?void 0:zi(this.C);this.s()};function H(a){a=a?a:{};var b=ta({},a);delete b.preload;delete b.useInterimTilesOnError;G.call(this,b);this.P(a.preload?a.preload:0);this.U(a.useInterimTilesOnError?a.useInterimTilesOnError:!0);ha(void 0==a.renderMode||a.renderMode==Di||a.renderMode==Ei||a.renderMode==Fi,28);this.u=a.renderMode||Ei}v(H,G);H.prototype.f=function(){return this.get(Gi)};H.prototype.c=function(){return this.get(Hi)};H.prototype.P=function(a){this.set(fi,a)};H.prototype.U=function(a){this.set(gi,a)};
+var Gi="preload",Hi="useInterimTilesOnError",Di="image",Ei="hybrid",Fi="vector";function Ii(){};function Ji(a,b,c,d,e){this.f=a;this.A=b;this.c=c;this.C=d;this.fc=e;this.i=this.b=this.a=this.Z=this.Qa=this.U=null;this.fa=this.eb=this.H=this.ra=this.L=this.D=0;this.sa=!1;this.j=this.zb=0;this.na=!1;this.va=0;this.g="";this.xa=this.Fa=0;this.Ja=!1;this.o=this.Ua=0;this.P=this.v=this.l=null;this.u=[];this.Ob=Ph()}v(Ji,Ii);
+function Ki(a,b,c){if(a.i){b=Sc(b,0,c,2,a.C,a.u);c=a.f;var d=a.Ob,e=c.globalAlpha;1!=a.H&&(c.globalAlpha=e*a.H);var f=a.zb;a.sa&&(f+=a.fc);var g,h;g=0;for(h=b.length;g<h;g+=2){var l=b[g]-a.D,m=b[g+1]-a.L;a.na&&(l=Math.round(l),m=Math.round(m));if(0!==f||1!=a.j){var p=l+a.D,n=m+a.L;Yh(d,p,n,a.j,a.j,f,-p,-n);c.setTransform.apply(c,d)}c.drawImage(a.i,a.eb,a.fa,a.va,a.ra,l,m,a.va,a.ra)}0===f&&1==a.j||c.setTransform(1,0,0,1,0,0);1!=a.H&&(c.globalAlpha=e)}}
+function Li(a,b,c,d){var e=0;if(a.P&&""!==a.g){a.l&&Mi(a,a.l);a.v&&Ni(a,a.v);var f=a.P,g=a.f,h=a.Z;h?(h.font!=f.font&&(h.font=g.font=f.font),h.textAlign!=f.textAlign&&(h.textAlign=g.textAlign=f.textAlign),h.textBaseline!=f.textBaseline&&(h.textBaseline=g.textBaseline=f.textBaseline)):(g.font=f.font,g.textAlign=f.textAlign,g.textBaseline=f.textBaseline,a.Z={font:f.font,textAlign:f.textAlign,textBaseline:f.textBaseline});b=Sc(b,e,c,d,a.C,a.u);f=a.f;g=a.Ua;for(a.Ja&&(g+=a.fc);e<c;e+=d){var h=b[e]+a.Fa,
+l=b[e+1]+a.xa;if(0!==g||1!=a.o){var m=Yh(a.Ob,h,l,a.o,a.o,g,-h,-l);f.setTransform.apply(f,m)}a.v&&f.strokeText(a.g,h,l);a.l&&f.fillText(a.g,h,l)}0===g&&1==a.o||f.setTransform(1,0,0,1,0,0)}}function Oi(a,b,c,d,e,f){var g=a.f;a=Sc(b,c,d,e,a.C,a.u);g.moveTo(a[0],a[1]);b=a.length;f&&(b-=2);for(c=2;c<b;c+=2)g.lineTo(a[c],a[c+1]);f&&g.closePath();return d}function Pi(a,b,c,d,e){var f,g;f=0;for(g=d.length;f<g;++f)c=Oi(a,b,c,d[f],e,!0);return c}k=Ji.prototype;
+k.hc=function(a){if(jc(this.c,a.G())){if(this.a||this.b){this.a&&Mi(this,this.a);this.b&&Ni(this,this.b);var b;b=this.C;var c=this.u,d=a.ia();b=d?Sc(d,0,d.length,a.pa(),b,c):null;c=b[2]-b[0];d=b[3]-b[1];c=Math.sqrt(c*c+d*d);d=this.f;d.beginPath();d.arc(b[0],b[1],c,0,2*Math.PI);this.a&&d.fill();this.b&&d.stroke()}""!==this.g&&Li(this,a.Fd(),2,2)}};k.Gd=function(a){this.Ma(a.f,a.g);this.dc(a.a);this.$b(a.Ka())};
+k.tc=function(a){switch(a.Y()){case "Point":this.xc(a);break;case "LineString":this.Pb(a);break;case "Polygon":this.yc(a);break;case "MultiPoint":this.vc(a);break;case "MultiLineString":this.uc(a);break;case "MultiPolygon":this.wc(a);break;case "GeometryCollection":this.kf(a);break;case "Circle":this.hc(a)}};k.jf=function(a,b){var c=(0,b.c)(a);c&&jc(this.c,c.G())&&(this.Gd(b),this.tc(c))};k.kf=function(a){a=a.f;var b,c;b=0;for(c=a.length;b<c;++b)this.tc(a[b])};
+k.xc=function(a){var b=a.ia();a=a.pa();this.i&&Ki(this,b,b.length);""!==this.g&&Li(this,b,b.length,a)};k.vc=function(a){var b=a.ia();a=a.pa();this.i&&Ki(this,b,b.length);""!==this.g&&Li(this,b,b.length,a)};k.Pb=function(a){if(jc(this.c,a.G())){if(this.b){Ni(this,this.b);var b=this.f,c=a.ia();b.beginPath();Oi(this,c,0,c.length,a.pa(),!1);b.stroke()}""!==this.g&&(a=Qi(a),Li(this,a,2,2))}};
+k.uc=function(a){var b=a.G();if(jc(this.c,b)){if(this.b){Ni(this,this.b);var b=this.f,c=a.ia(),d=0,e=a.Kb(),f=a.pa();b.beginPath();var g,h;g=0;for(h=e.length;g<h;++g)d=Oi(this,c,d,e[g],f,!1);b.stroke()}""!==this.g&&(a=Ri(a),Li(this,a,a.length,2))}};k.yc=function(a){if(jc(this.c,a.G())){if(this.b||this.a){this.a&&Mi(this,this.a);this.b&&Ni(this,this.b);var b=this.f;b.beginPath();Pi(this,a.Vb(),0,a.Kb(),a.pa());this.a&&b.fill();this.b&&b.stroke()}""!==this.g&&(a=Ad(a),Li(this,a,2,2))}};
+k.wc=function(a){if(jc(this.c,a.G())){if(this.b||this.a){this.a&&Mi(this,this.a);this.b&&Ni(this,this.b);var b=this.f,c=Si(a),d=0,e=a.c,f=a.pa(),g,h;b.beginPath();g=0;for(h=e.length;g<h;++g)d=Pi(this,c,d,e[g],f);this.a&&b.fill();this.b&&b.stroke()}""!==this.g&&(a=Ti(a),Li(this,a,a.length,2))}};function Mi(a,b){var c=a.f,d=a.U;d?d.fillStyle!=b.fillStyle&&(d.fillStyle=c.fillStyle=b.fillStyle):(c.fillStyle=b.fillStyle,a.U={fillStyle:b.fillStyle})}
+function Ni(a,b){var c=a.f,d=a.Qa;d?(d.lineCap!=b.lineCap&&(d.lineCap=c.lineCap=b.lineCap),nf&&!db(d.lineDash,b.lineDash)&&c.setLineDash(d.lineDash=b.lineDash),d.lineJoin!=b.lineJoin&&(d.lineJoin=c.lineJoin=b.lineJoin),d.lineWidth!=b.lineWidth&&(d.lineWidth=c.lineWidth=b.lineWidth),d.miterLimit!=b.miterLimit&&(d.miterLimit=c.miterLimit=b.miterLimit),d.strokeStyle!=b.strokeStyle&&(d.strokeStyle=c.strokeStyle=b.strokeStyle)):(c.lineCap=b.lineCap,nf&&c.setLineDash(b.lineDash),c.lineJoin=b.lineJoin,c.lineWidth=
+b.lineWidth,c.miterLimit=b.miterLimit,c.strokeStyle=b.strokeStyle,a.Qa={lineCap:b.lineCap,lineDash:b.lineDash,lineJoin:b.lineJoin,lineWidth:b.lineWidth,miterLimit:b.miterLimit,strokeStyle:b.strokeStyle})}
+k.Ma=function(a,b){if(a){var c=a.b;this.a={fillStyle:Ce(c?c:ni)}}else this.a=null;if(b){var c=b.b,d=b.c,e=b.g,f=b.i,g=b.f,h=b.j;this.b={lineCap:void 0!==d?d:"round",lineDash:e?e:oi,lineJoin:void 0!==f?f:"round",lineWidth:this.A*(void 0!==g?g:1),miterLimit:void 0!==h?h:10,strokeStyle:Ce(c?c:pi)}}else this.b=null};
+k.dc=function(a){if(a){var b=a.Ac(),c=a.Ic(1),d=a.Jc(),e=a.ac();this.D=b[0];this.L=b[1];this.ra=e[1];this.i=c;this.H=a.l;this.eb=d[0];this.fa=d[1];this.sa=a.H;this.zb=a.o;this.j=a.i;this.na=a.u;this.va=e[0]}else this.i=null};
+k.$b=function(a){if(a){var b=a.b;b?(b=b.b,this.l={fillStyle:Ce(b?b:ni)}):this.l=null;var c=a.f;if(c){var b=c.b,d=c.c,e=c.g,f=c.i,g=c.f,c=c.j;this.v={lineCap:void 0!==d?d:"round",lineDash:e?e:oi,lineJoin:void 0!==f?f:"round",lineWidth:void 0!==g?g:1,miterLimit:void 0!==c?c:10,strokeStyle:Ce(b?b:pi)}}else this.v=null;var b=a.g,d=a.c,e=a.i,f=a.v,g=a.j,c=a.a,h=a.Ka(),l=a.l;a=a.o;this.P={font:void 0!==b?b:"10px sans-serif",textAlign:void 0!==l?l:"center",textBaseline:void 0!==a?a:"middle"};this.g=void 0!==
+h?h:"";this.Fa=void 0!==d?this.A*d:0;this.xa=void 0!==e?this.A*e:0;this.Ja=void 0!==f?f:!1;this.Ua=void 0!==g?g:0;this.o=this.A*(void 0!==c?c:1)}else this.g=""};function Ui(a){Pa.call(this);this.a=a}v(Ui,Pa);Ui.prototype.Ba=ea;Ui.prototype.te=nc;Ui.prototype.hf=function(a,b,c){return function(d,e){return Vi(a,b,d,e,function(a){c[d]||(c[d]={});c[d][a.Ca.toString()]=a})}};Ui.prototype.Fa=function(a){a.target.W()===li&&Wi(this)};function Xi(a,b){var c=b.W();c!=li&&c!=ki&&B(b,"change",a.Fa,a);c==ji&&(b.load(),c=b.W());return c==li}function Wi(a){var b=a.a;b.Fb()&&"ready"==b.uf()&&a.s()}
+function Yi(a,b){b.Ih()&&a.postRenderFunctions.push(function(a,b,e){b=x(a).toString();a.Wc(e.viewState.projection,e.usedTiles[b])}.bind(null,b))}function Zi(a,b){if(b){var c,d,e;d=0;for(e=b.length;d<e;++d)c=b[d],a[x(c).toString()]=c}}function $i(a,b){var c=b.L;void 0!==c&&("string"===typeof c?a.logos[c]="":c&&(ha("string"==typeof c.href,44),ha("string"==typeof c.src,45),a.logos[c.src]=c.href))}
+function aj(a,b,c,d){b=x(b).toString();c=c.toString();b in a?c in a[b]?(a=a[b][c],d.ea<a.ea&&(a.ea=d.ea),d.ca>a.ca&&(a.ca=d.ca),d.ga<a.ga&&(a.ga=d.ga),d.ja>a.ja&&(a.ja=d.ja)):a[b][c]=d:(a[b]={},a[b][c]=d)}
+function bj(a,b,c,d,e,f,g,h,l,m){var p=x(b).toString();p in a.wantedTiles||(a.wantedTiles[p]={});var n=a.wantedTiles[p];a=a.tileQueue;var q=c.minZoom,r,u,w,y,z,A;for(A=g;A>=q;--A)for(u=fe(c,f,A,u),w=c.Ha(A),y=u.ea;y<=u.ca;++y)for(z=u.ga;z<=u.ja;++z)g-A<=h?(r=b.Dc(A,y,z,d,e),0==r.W()&&(n[r.bb()]=!0,r.bb()in a.a||a.c([r,p,ke(c,r.Ca),w])),void 0!==l&&l.call(m,r)):b.ig(A,y,z,e)};function cj(a){Ui.call(this,a);this.xa=Ph()}v(cj,Ui);function dj(a,b,c){var d=b.pixelRatio,e=b.size[0]*d,f=b.size[1]*d,g=b.viewState.rotation,h=ac(c),l=$b(c),m=Zb(c);c=Yb(c);Uh(b.coordinateToPixelTransform,h);Uh(b.coordinateToPixelTransform,l);Uh(b.coordinateToPixelTransform,m);Uh(b.coordinateToPixelTransform,c);a.save();qi(a,-g,e/2,f/2);a.beginPath();a.moveTo(h[0]*d,h[1]*d);a.lineTo(l[0]*d,l[1]*d);a.lineTo(m[0]*d,m[1]*d);a.lineTo(c[0]*d,c[1]*d);a.clip();qi(a,g,e/2,f/2)}
+function ej(a,b,c,d,e){var f=a.a;if(Oa(f,b)){var g=d.size[0]*d.pixelRatio,h=d.size[1]*d.pixelRatio,l=d.viewState.rotation;qi(c,-l,g/2,h/2);a=void 0!==e?e:fj(a,d,0);f.b(new Jh(b,new Ji(c,d.pixelRatio,d.extent,a,d.viewState.rotation),d,c,null));qi(c,l,g/2,h/2)}}cj.prototype.C=function(a,b,c,d){if(this.Ba(a,b,0,mc,this))return c.call(d,this.a,null)};cj.prototype.v=function(a,b,c,d){ej(this,"postcompose",a,b,d)};
+function fj(a,b,c){var d=b.viewState,e=b.pixelRatio,f=e/d.resolution;return Yh(a.xa,e*b.size[0]/2,e*b.size[1]/2,f,-f,-d.rotation,-d.center[0]+c,-d.center[1])};function gj(a){cj.call(this,a);this.u=Ph();this.i=null}v(gj,cj);gj.prototype.D=function(a,b,c){ej(this,"precompose",c,a,void 0);var d=this.l();if(d){var e=b.extent,f=void 0!==e;f&&dj(c,a,e);var e=this.P(),g=c.globalAlpha;c.globalAlpha=b.opacity;c.drawImage(d,0,0,+d.width,+d.height,Math.round(e[4]),Math.round(e[5]),Math.round(d.width*e[0]),Math.round(d.height*e[3]));c.globalAlpha=g;f&&c.restore()}this.v(c,a,b)};
+gj.prototype.Ba=function(a,b,c,d,e){var f=this.a;return f.la().Ba(a,b.viewState.resolution,b.viewState.rotation,c,b.skippedFeatureUids,function(a){return d.call(e,a,f)})};
+gj.prototype.C=function(a,b,c,d){if(this.l()){if(this.a.la().Ba!==ea)return cj.prototype.C.apply(this,arguments);var e=Uh(this.u,a.slice());xb(e,b.viewState.resolution/this.c);this.i||(this.i=De(1,1));this.i.clearRect(0,0,1,1);this.i.drawImage(this.l(),e[0],e[1],1,1,0,0,1,1);e=this.i.getImageData(0,0,1,1).data;if(0<e[3])return c.call(d,this.a,e)}};function hj(a){gj.call(this,a);this.f=null;this.j=Ph()}v(hj,gj);hj.prototype.l=function(){return this.f?this.f.a():null};hj.prototype.P=function(){return this.j};
+hj.prototype.o=function(a,b){var c=a.pixelRatio,d=a.size,e=a.viewState,f=e.center,g=e.resolution,h=this.a.la(),l=a.viewHints,m=a.extent;void 0!==b.extent&&(m=ic(m,b.extent));l[Kd]||l[1]||cc(m)||(e=h.U(m,g,c,e.projection))&&Xi(this,e)&&(this.f=e,this.c=g);if(this.f){var e=this.f,l=e.G(),m=e.resolution,p=e.f,n=c*m/(g*p),l=Yh(this.j,c*d[0]/2,c*d[1]/2,n,n,0,p*(l[0]-f[0])/m,p*(f[1]-l[3])/m);Yh(this.u,c*d[0]/2-l[4],c*d[1]/2-l[5],c/g,-c/g,0,-f[0],-f[1]);Zi(a.attributions,e.j);$i(a,h)}return!!this.f};function ij(a){gj.call(this,a);this.L=De();this.j=null;this.f=[];this.H=Hb();this.Ua=new Td(0,0,0,0);this.U=Ph();this.na=0}v(ij,gj);
+ij.prototype.o=function(a,b){var c=a.pixelRatio,d=a.size,e=a.viewState,f=e.projection,g=e.resolution,e=e.center,h=this.a,l=h.la(),m=l.g,p=l.Db(f),n=p.Ec(g,this.na),q=p.Ha(n),r=a.extent;void 0!==b.extent&&(r=ic(r,b.extent));if(cc(r))return!1;var u=ie(p,r,q),w,y=p.Kc(n);w=p.Ha(n);var z=Zd(p.Za(n),p.j);w=Rb(y[0]+u.ea*z[0]*w,y[1]+u.ga*z[1]*w,y[0]+(u.ca+1)*z[0]*w,y[1]+(u.ja+1)*z[1]*w,void 0);y=l.jb(c);z={};z[n]={};var A=this.hf(l,f,z),O=h.c(),Ja=this.H,ca=this.Ua,Ma=!1,D,La,kb;for(La=u.ea;La<=u.ca;++La)for(kb=
+u.ga;kb<=u.ja;++kb){D=l.Dc(n,La,kb,c,f);var W=D.W();W==cg||4==W||3==W&&!O?W==cg&&(z[n][D.Ca.toString()]=D,Ma||-1!=this.f.indexOf(D)||(Ma=!0)):(D=bg(D),ge(p,D.Ca,A,ca,Ja)||(D=he(p,D.Ca,ca,Ja))&&A(n+1,D))}La=a.viewHints;if(!(this.c&&16<Date.now()-a.time&&(La[Kd]||La[1])||!Ma&&this.j&&Vb(this.j,w)&&this.Ja==m)){D=l.Dd(n,c,f);La=(u.ca-u.ea+1)*D[0];D=(u.ja-u.ga+1)*D[0];Ma=this.L;kb=Ma.canvas;A=l.tf(f);kb.width!=La||kb.height!=D?(kb.width=La,kb.height=D):Ma.clearRect(0,0,La,D);this.f.length=0;O=Object.keys(z).map(Number);
+O.sort(Ya);var Ra,Pb,fc,Wc,de,He,W=0;for(Pb=O.length;W<Pb;++W){La=O[W];ca=l.Dd(La,c,f);D=p.Ha(La);Ra=D/q;fc=y*l.qf(f);Wc=z[La];for(var od in Wc)D=Wc[od],kb=p.Na(D.Ca,Ja),La=(kb[0]-w[0])/q*y,kb=(w[3]-kb[3])/q*y,de=ca[0]*Ra,He=ca[1]*Ra,A||Ma.clearRect(La,kb,de,He),this.A(D,a,b,La,kb,de,He,fc),this.f.push(D)}this.Ja=m;this.c=q;this.j=w}od=c/y*this.c/g;od=Yh(this.U,c*d[0]/2,c*d[1]/2,od,od,0,y*(this.j[0]-e[0])/this.c,y*(e[1]-this.j[3])/this.c);Yh(this.u,c*d[0]/2-od[4],c*d[1]/2-od[5],c/g,-c/g,0,-e[0],-e[1]);
+aj(a.usedTiles,l,n,u);bj(a,l,p,c,f,r,n,h.f());Yi(a,l);$i(a,l);return 0<this.f.length};ij.prototype.A=function(a,b,c,d,e,f,g,h){(a=a.ub())&&this.L.drawImage(a,h,h,a.width-2*h,a.height-2*h,d,e,f,g)};ij.prototype.l=function(){return this.L.canvas};ij.prototype.P=function(){return this.U};function jj(){};function kj(a,b,c,d){this.zb=a;this.Qa=b;this.overlaps=d;this.c=0;this.resolution=c;this.va=this.ra=null;this.a=[];this.coordinates=[];this.Z=Ph();this.b=[];this.eb=[];this.sa=Ph();this.fa=Ph()}v(kj,Ii);
+function lj(a,b,c,d,e,f,g){var h=a.coordinates.length,l=a.mf();g&&(c+=e);g=[b[c],b[c+1]];var m=[NaN,NaN],p=!0,n,q,r;for(n=c+e;n<d;n+=e)m[0]=b[n],m[1]=b[n+1],r=Qb(l,m),r!==q?(p&&(a.coordinates[h++]=g[0],a.coordinates[h++]=g[1]),a.coordinates[h++]=m[0],a.coordinates[h++]=m[1],p=!1):1===r?(a.coordinates[h++]=m[0],a.coordinates[h++]=m[1],p=!1):p=!0,g[0]=m[0],g[1]=m[1],q=r;if(f&&p||n===c+e)a.coordinates[h++]=g[0],a.coordinates[h++]=g[1];return h}
+function mj(a,b){a.ra=[0,b,0];a.a.push(a.ra);a.va=[0,b,0];a.b.push(a.va)}function nj(a,b,c){if(a.U){var d=Uh(a.Z,a.U.slice());b.translate(d[0],d[1]);b.rotate(c)}b.fill();a.U&&b.setTransform.apply(b,a.fa)}
+function oj(a,b,c,d,e,f,g,h,l){var m;db(d,a.Z)?m=a.eb:(m=Sc(a.coordinates,0,a.coordinates.length,2,d,a.eb),Th(a.Z,d));d=!wa(f);for(var p=0,n=g.length,q=0,r,u=a.sa,w=a.fa,y,z,A,O,Ja=0,ca=0,Ma=a.a!=g||a.overlaps?0:200;p<n;){var D=g[p],La,kb,W,Ra;switch(D[0]){case 0:q=D[1];d&&f[x(q).toString()]||!q.V()?p=D[2]:void 0===l||jc(l,q.V().G())?++p:p=D[2]+1;break;case 1:Ja>Ma&&(nj(a,b,e),Ja=0);ca>Ma&&(b.stroke(),ca=0);Ja||ca||b.beginPath();++p;break;case 2:q=D[1];r=m[q];D=m[q+1];A=m[q+2]-r;q=m[q+3]-D;q=Math.sqrt(A*
+A+q*q);b.moveTo(r+q,D);b.arc(r,D,q,0,2*Math.PI,!0);++p;break;case 3:b.closePath();++p;break;case 4:q=D[1];r=D[2];La=D[3];kb=D[4]*c;W=D[5]*c;var Pb=D[6],fc=D[7],Wc=D[8],de=D[9];Ra=D[10];A=D[11];O=D[12];var He=D[13],od=D[14];for(Ra&&(A+=e);q<r;q+=2){D=m[q]-kb;Ra=m[q+1]-W;He&&(D=Math.round(D),Ra=Math.round(Ra));if(1!=O||0!==A){var bf=D+kb,mh=Ra+W;Yh(u,bf,mh,O,O,A,-bf,-mh);b.setTransform.apply(b,u)}bf=b.globalAlpha;1!=fc&&(b.globalAlpha=bf*fc);var mh=od+Wc>La.width?La.width-Wc:od,xo=Pb+de>La.height?La.height-
+de:Pb;b.drawImage(La,Wc,de,mh,xo,D,Ra,mh*c,xo*c);1!=fc&&(b.globalAlpha=bf);1==O&&0===A||b.setTransform.apply(b,w)}++p;break;case 5:q=D[1];r=D[2];W=D[3];Pb=D[4]*c;fc=D[5]*c;A=D[6];O=D[7]*c;La=D[8];kb=D[9];for((Ra=D[10])&&(A+=e);q<r;q+=2){D=m[q]+Pb;Ra=m[q+1]+fc;if(1!=O||0!==A)Yh(u,D,Ra,O,O,A,-D,-Ra),b.setTransform.apply(b,u);Wc=W.split("\n");de=Wc.length;1<de?(He=Math.round(1.5*b.measureText("M").width),Ra-=(de-1)/2*He):He=0;for(od=0;od<de;od++)bf=Wc[od],kb&&b.strokeText(bf,D,Ra),La&&b.fillText(bf,
+D,Ra),Ra+=He;1==O&&0===A||b.setTransform.apply(b,w)}++p;break;case 6:if(void 0!==h&&(q=D[1],q=h(q)))return q;++p;break;case 7:Ma?Ja++:nj(a,b,e);++p;break;case 8:q=D[1];r=D[2];D=m[q];Ra=m[q+1];A=D+.5|0;O=Ra+.5|0;if(A!==y||O!==z)b.moveTo(D,Ra),y=A,z=O;for(q+=2;q<r;q+=2)if(D=m[q],Ra=m[q+1],A=D+.5|0,O=Ra+.5|0,q==r-2||A!==y||O!==z)b.lineTo(D,Ra),y=A,z=O;++p;break;case 9:a.U=D[2];Ja&&(nj(a,b,e),Ja=0);b.fillStyle=D[1];++p;break;case 10:y=void 0!==D[7]?D[7]:!0;var yo=D[8];z=D[2];ca&&(b.stroke(),ca=0);b.strokeStyle=
+D[1];b.lineWidth=y?z*c:z;b.lineCap=D[3];b.lineJoin=D[4];b.miterLimit=D[5];nf&&(z=D[6],y&&c!==yo&&(z=z.map(function(a){return a*c/yo}),D[6]=z,D[8]=c),b.setLineDash(z));z=y=NaN;++p;break;case 11:b.font=D[1];b.textAlign=D[2];b.textBaseline=D[3];++p;break;case 12:Ma?ca++:b.stroke();++p;break;default:++p}}Ja&&nj(a,b,e);ca&&b.stroke()}kj.prototype.f=function(a,b,c,d,e){oj(this,a,b,c,d,e,this.a,void 0,void 0)};
+function pj(a){var b=a.b;b.reverse();var c,d=b.length,e,f,g=-1;for(c=0;c<d;++c)if(e=b[c],f=e[0],6==f)g=c;else if(0==f){e[2]=c;e=a.b;for(f=c;g<f;){var h=e[g];e[g]=e[f];e[f]=h;++g;--f}g=-1}}function qj(a,b){a.ra[2]=a.a.length;a.ra=null;a.va[2]=a.b.length;a.va=null;var c=[6,b];a.a.push(c);a.b.push(c)}kj.prototype.se=ea;kj.prototype.mf=function(){return this.Qa};function rj(a,b,c,d){kj.call(this,a,b,c,d);this.l=this.P=null;this.L=this.D=this.C=this.A=this.u=this.H=this.v=this.o=this.j=this.i=this.g=void 0}v(rj,kj);
+rj.prototype.xc=function(a,b){if(this.l){mj(this,b);var c=a.ia(),d=this.coordinates.length,c=lj(this,c,0,c.length,a.pa(),!1,!1);this.a.push([4,d,c,this.l,this.g,this.i,this.j,this.o,this.v,this.H,this.u,this.A,this.C,this.D,this.L]);this.b.push([4,d,c,this.P,this.g,this.i,this.j,this.o,this.v,this.H,this.u,this.A,this.C,this.D,this.L]);qj(this,b)}};
+rj.prototype.vc=function(a,b){if(this.l){mj(this,b);var c=a.ia(),d=this.coordinates.length,c=lj(this,c,0,c.length,a.pa(),!1,!1);this.a.push([4,d,c,this.l,this.g,this.i,this.j,this.o,this.v,this.H,this.u,this.A,this.C,this.D,this.L]);this.b.push([4,d,c,this.P,this.g,this.i,this.j,this.o,this.v,this.H,this.u,this.A,this.C,this.D,this.L]);qj(this,b)}};rj.prototype.se=function(){pj(this);this.i=this.g=void 0;this.l=this.P=null;this.L=this.D=this.A=this.u=this.H=this.v=this.o=this.C=this.j=void 0};
+rj.prototype.dc=function(a){var b=a.Ac(),c=a.ac(),d=a.Lf(1),e=a.Ic(1),f=a.Jc();this.g=b[0];this.i=b[1];this.P=d;this.l=e;this.j=c[1];this.o=a.l;this.v=f[0];this.H=f[1];this.u=a.H;this.A=a.o;this.C=a.i;this.D=a.u;this.L=c[0]};function sj(a,b,c,d){kj.call(this,a,b,c,d);this.i=null;this.g={wd:void 0,rd:void 0,sd:null,td:void 0,ud:void 0,vd:void 0,Af:0,strokeStyle:void 0,lineCap:void 0,lineDash:null,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0}}v(sj,kj);function tj(a,b,c,d,e){var f=a.coordinates.length;b=lj(a,b,c,d,e,!1,!1);f=[8,f,b];a.a.push(f);a.b.push(f);return d}k=sj.prototype;k.mf=function(){this.i||(this.i=Kb(this.Qa),0<this.c&&Jb(this.i,this.resolution*(this.c+1)/2,this.i));return this.i};
+function uj(a){var b=a.g,c=b.strokeStyle,d=b.lineCap,e=b.lineDash,f=b.lineJoin,g=b.lineWidth,h=b.miterLimit;b.wd==c&&b.rd==d&&db(b.sd,e)&&b.td==f&&b.ud==g&&b.vd==h||(b.Af!=a.coordinates.length&&(a.a.push([12]),b.Af=a.coordinates.length),a.a.push([10,c,g,d,f,h,e,!0,1],[1]),b.wd=c,b.rd=d,b.sd=e,b.td=f,b.ud=g,b.vd=h)}
+k.Pb=function(a,b){var c=this.g,d=c.lineWidth;void 0!==c.strokeStyle&&void 0!==d&&(uj(this),mj(this,b),this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,!0,1],[1]),c=a.ia(),tj(this,c,0,c.length,a.pa()),this.b.push([12]),qj(this,b))};
+k.uc=function(a,b){var c=this.g,d=c.lineWidth;if(void 0!==c.strokeStyle&&void 0!==d){uj(this);mj(this,b);this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,!0,1],[1]);var c=a.Kb(),d=a.ia(),e=a.pa(),f=0,g,h;g=0;for(h=c.length;g<h;++g)f=tj(this,d,f,c[g],e);this.b.push([12]);qj(this,b)}};k.se=function(){this.g.Af!=this.coordinates.length&&this.a.push([12]);pj(this);this.g=null};
+k.Ma=function(a,b){var c=b.b;this.g.strokeStyle=Ce(c?c:pi);c=b.c;this.g.lineCap=void 0!==c?c:"round";c=b.g;this.g.lineDash=c?c:oi;c=b.i;this.g.lineJoin=void 0!==c?c:"round";c=b.f;this.g.lineWidth=void 0!==c?c:1;c=b.j;this.g.miterLimit=void 0!==c?c:10;this.g.lineWidth>this.c&&(this.c=this.g.lineWidth,this.i=null)};function vj(a,b,c,d){kj.call(this,a,b,c,d);this.i=null;this.g={Bg:void 0,wd:void 0,rd:void 0,sd:null,td:void 0,ud:void 0,vd:void 0,fillStyle:void 0,strokeStyle:void 0,lineCap:void 0,lineDash:null,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0}}v(vj,kj);
+function wj(a,b,c,d,e){var f=a.g,g=void 0!==f.fillStyle,f=void 0!=f.strokeStyle,h=d.length,l=[1];a.a.push(l);a.b.push(l);for(l=0;l<h;++l){var m=d[l],p=a.coordinates.length;c=lj(a,b,c,m,e,!0,!f);c=[8,p,c];a.a.push(c);a.b.push(c);f&&(c=[3],a.a.push(c),a.b.push(c));c=m}b=[7];a.b.push(b);g&&a.a.push(b);f&&(g=[12],a.a.push(g),a.b.push(g));return c}k=vj.prototype;
+k.hc=function(a,b){var c=this.g,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d){xj(this,a);mj(this,b);this.b.push([9,Ae(ni)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,!0,1]);var e=a.ia(),d=this.coordinates.length;lj(this,e,0,e.length,a.pa(),!1,!1);e=[1];d=[2,d];this.a.push(e,d);this.b.push(e,d);d=[7];this.b.push(d);void 0!==c.fillStyle&&this.a.push(d);void 0!==c.strokeStyle&&(c=[12],this.a.push(c),this.b.push(c));qj(this,b)}};
+k.yc=function(a,b){var c=this.g;xj(this,a);mj(this,b);this.b.push([9,Ae(ni)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,!0,1]);var c=a.Kb(),d=a.Vb();wj(this,d,0,c,a.pa());qj(this,b)};
+k.wc=function(a,b){var c=this.g,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d){xj(this,a);mj(this,b);this.b.push([9,Ae(ni)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,!0,1]);var c=a.c,d=Si(a),e=a.pa(),f=0,g,h;g=0;for(h=c.length;g<h;++g)f=wj(this,d,f,c[g],e);qj(this,b)}};k.se=function(){pj(this);this.g=null;var a=this.zb;if(0!==a){var b=this.coordinates,c,d;c=0;for(d=b.length;c<d;++c)b[c]=a*Math.round(b[c]/a)}};
+k.mf=function(){this.i||(this.i=Kb(this.Qa),0<this.c&&Jb(this.i,this.resolution*(this.c+1)/2,this.i));return this.i};
+k.Ma=function(a,b){var c=this.g;if(a){var d=a.b;c.fillStyle=Ce(d?d:ni)}else c.fillStyle=void 0;b?(d=b.b,c.strokeStyle=Ce(d?d:pi),d=b.c,c.lineCap=void 0!==d?d:"round",d=b.g,c.lineDash=d?d.slice():oi,d=b.i,c.lineJoin=void 0!==d?d:"round",d=b.f,c.lineWidth=void 0!==d?d:1,d=b.j,c.miterLimit=void 0!==d?d:10,c.lineWidth>this.c&&(this.c=c.lineWidth,this.i=null)):(c.strokeStyle=void 0,c.lineCap=void 0,c.lineDash=null,c.lineJoin=void 0,c.lineWidth=void 0,c.miterLimit=void 0)};
+function xj(a,b){var c=a.g,d=c.fillStyle,e=c.strokeStyle,f=c.lineCap,g=c.lineDash,h=c.lineJoin,l=c.lineWidth,m=c.miterLimit;if(void 0!==d&&("string"!==typeof d||c.Bg!=d)){var p=[9,d];"string"!==typeof d&&(d=b.G(),p.push([d[0],d[3]]));a.a.push(p);c.Bg=c.fillStyle}void 0===e||c.wd==e&&c.rd==f&&db(c.sd,g)&&c.td==h&&c.ud==l&&c.vd==m||(a.a.push([10,e,l,f,h,m,g,!0,1]),c.wd=e,c.rd=f,c.sd=g,c.td=h,c.ud=l,c.vd=m)};function yj(a,b,c,d){kj.call(this,a,b,c,d);this.L=this.D=this.C=null;this.l="";this.v=this.o=0;this.H=void 0;this.A=this.u=0;this.j=this.i=this.g=null}v(yj,kj);
+function zj(a,b,c,d,e){if(""!==a.l&&a.j&&(a.g||a.i)){if(a.g){var f=a.g,g=a.C;if(!g||g.fillStyle!=f.fillStyle){var h=[9,f.fillStyle];a.a.push(h);a.b.push(h);g?g.fillStyle=f.fillStyle:a.C={fillStyle:f.fillStyle}}}a.i&&(f=a.i,g=a.D,g&&g.lineCap==f.lineCap&&g.lineDash==f.lineDash&&g.lineJoin==f.lineJoin&&g.lineWidth==f.lineWidth&&g.miterLimit==f.miterLimit&&g.strokeStyle==f.strokeStyle||(h=[10,f.strokeStyle,f.lineWidth,f.lineCap,f.lineJoin,f.miterLimit,f.lineDash,!1,1],a.a.push(h),a.b.push(h),g?(g.lineCap=
+f.lineCap,g.lineDash=f.lineDash,g.lineJoin=f.lineJoin,g.lineWidth=f.lineWidth,g.miterLimit=f.miterLimit,g.strokeStyle=f.strokeStyle):a.D={lineCap:f.lineCap,lineDash:f.lineDash,lineJoin:f.lineJoin,lineWidth:f.lineWidth,miterLimit:f.miterLimit,strokeStyle:f.strokeStyle}));f=a.j;g=a.L;g&&g.font==f.font&&g.textAlign==f.textAlign&&g.textBaseline==f.textBaseline||(h=[11,f.font,f.textAlign,f.textBaseline],a.a.push(h),a.b.push(h),g?(g.font=f.font,g.textAlign=f.textAlign,g.textBaseline=f.textBaseline):a.L=
+{font:f.font,textAlign:f.textAlign,textBaseline:f.textBaseline});mj(a,e);f=a.coordinates.length;b=lj(a,b,0,c,d,!1,!1);b=[5,f,b,a.l,a.o,a.v,a.u,a.A,!!a.g,!!a.i,a.H];a.a.push(b);a.b.push(b);qj(a,e)}}
+yj.prototype.$b=function(a){if(a){var b=a.b;b?(b=b.b,b=Ce(b?b:ni),this.g?this.g.fillStyle=b:this.g={fillStyle:b}):this.g=null;var c=a.f;if(c){var b=c.b,d=c.c,e=c.g,f=c.i,g=c.f,c=c.j,d=void 0!==d?d:"round",e=e?e.slice():oi,f=void 0!==f?f:"round",g=void 0!==g?g:1,c=void 0!==c?c:10,b=Ce(b?b:pi);if(this.i){var h=this.i;h.lineCap=d;h.lineDash=e;h.lineJoin=f;h.lineWidth=g;h.miterLimit=c;h.strokeStyle=b}else this.i={lineCap:d,lineDash:e,lineJoin:f,lineWidth:g,miterLimit:c,strokeStyle:b}}else this.i=null;
+var l=a.g,b=a.c,d=a.i,e=a.v,g=a.j,c=a.a,f=a.Ka(),h=a.l,m=a.o;a=void 0!==l?l:"10px sans-serif";h=void 0!==h?h:"center";m=void 0!==m?m:"middle";this.j?(l=this.j,l.font=a,l.textAlign=h,l.textBaseline=m):this.j={font:a,textAlign:h,textBaseline:m};this.l=void 0!==f?f:"";this.o=void 0!==b?b:0;this.v=void 0!==d?d:0;this.H=void 0!==e?e:!1;this.u=void 0!==g?g:0;this.A=void 0!==c?c:1}else this.l=""};var Aj=["Polygon","Circle","LineString","Image","Text"];function Bj(a,b,c,d,e){this.H=a;this.c=b;this.o=d;this.v=c;this.i=e;this.a={};this.j=De(1,1);this.l=Ph()}v(Bj,jj);var Cj={0:[[!0]]};function Dj(a,b,c){var d,e=Math.floor(a.length/2);if(b>=e)for(d=e;d<b;d++)a[d][c]=!0;else if(b<e)for(d=b+1;d<e;d++)a[d][c]=!0}
+function Ej(a){if(void 0!==Cj[a])return Cj[a];for(var b=2*a+1,c=Array(b),d=0;d<b;d++)c[d]=Array(b);for(var b=a,e=d=0;b>=d;)Dj(c,a+b,a+d),Dj(c,a+d,a+b),Dj(c,a-d,a+b),Dj(c,a-b,a+d),Dj(c,a-b,a-d),Dj(c,a-d,a-b),Dj(c,a+d,a-b),Dj(c,a+b,a-d),d++,e+=1+2*d,0<2*(e-b)+1&&(--b,e+=1-2*b);return Cj[a]=c}function Fj(a){for(var b in a.a){var c=a.a[b],d;for(d in c)c[d].se()}}
+Bj.prototype.Ba=function(a,b,c,d,e,f){d=Math.round(d);var g=2*d+1,h=Yh(this.l,d+.5,d+.5,1/b,-1/b,-c,-a[0],-a[1]),l=this.j;l.canvas.width!==g||l.canvas.height!==g?(l.canvas.width=g,l.canvas.height=g):l.clearRect(0,0,g,g);var m;void 0!==this.i&&(m=Hb(),Ib(m,a),Jb(m,b*(this.i+d),m));var p=Ej(d);return Gj(this,l,h,c,e,function(a){for(var b=l.getImageData(0,0,g,g).data,c=0;c<g;c++)for(var d=0;d<g;d++)if(p[c][d]&&0<b[4*(d*g+c)+3]){if(a=f(a))return a;l.clearRect(0,0,g,g);return}},m)};
+function Hj(a,b){var c=a.c,d=c[0],e=c[1],f=c[2],c=c[3],d=[d,e,d,c,f,c,f,e];Sc(d,0,8,2,b,d);return d}Bj.prototype.b=function(a,b){var c=void 0!==a?a.toString():"0",d=this.a[c];void 0===d&&(d={},this.a[c]=d);c=d[b];void 0===c&&(c=new Ij[b](this.H,this.c,this.v,this.o),d[b]=c);return c};Bj.prototype.g=function(){return wa(this.a)};
+Bj.prototype.f=function(a,b,c,d,e,f){var g=Object.keys(this.a).map(Number);g.sort(Ya);var h=Hj(this,c);a.save();a.beginPath();a.moveTo(h[0],h[1]);a.lineTo(h[2],h[3]);a.lineTo(h[4],h[5]);a.lineTo(h[6],h[7]);a.clip();f=f?f:Aj;var l,m,p,n,q,h=0;for(l=g.length;h<l;++h)for(n=this.a[g[h].toString()],m=0,p=f.length;m<p;++m)q=n[f[m]],void 0!==q&&q.f(a,b,c,d,e);a.restore()};
+function Gj(a,b,c,d,e,f,g){var h=Object.keys(a.a).map(Number);h.sort(function(a,b){return b-a});var l,m,p,n,q;l=0;for(m=h.length;l<m;++l)for(n=a.a[h[l].toString()],p=Aj.length-1;0<=p;--p)if(q=n[Aj[p]],void 0!==q&&(q=oj(q,b,1,c,d,e,q.b,f,g)))return q}var Ij={Circle:vj,Image:rj,LineString:sj,Polygon:vj,Text:yj};function Jj(a,b){return x(a)-x(b)}function Kj(a,b){var c=.5*a/b;return c*c}function Lj(a,b,c,d,e,f){var g=!1,h,l;if(h=c.a)l=h.xe(),l==li||l==ki?h.Ii(e,f):(l==ji&&h.load(),h.eh(e,f),g=!0);if(e=(0,c.c)(b))d=e.Bd(d),(0,Mj[d.Y()])(a,d,c,b);return g}
+var Mj={Point:function(a,b,c,d){var e=c.a;if(e){if(e.xe()!=li)return;var f=a.b(c.b,"Image");f.dc(e);f.xc(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),zj(a,b.ia(),2,2,d)},LineString:function(a,b,c,d){var e=c.g;if(e){var f=a.b(c.b,"LineString");f.Ma(null,e);f.Pb(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),zj(a,Qi(b),2,2,d)},Polygon:function(a,b,c,d){var e=c.f,f=c.g;if(e||f){var g=a.b(c.b,"Polygon");g.Ma(e,f);g.yc(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),zj(a,Ad(b),2,2,d)},MultiPoint:function(a,b,c,d){var e=
+c.a;if(e){if(e.xe()!=li)return;var f=a.b(c.b,"Image");f.dc(e);f.vc(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),c=b.ia(),zj(a,c,c.length,b.pa(),d)},MultiLineString:function(a,b,c,d){var e=c.g;if(e){var f=a.b(c.b,"LineString");f.Ma(null,e);f.uc(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),b=Ri(b),zj(a,b,b.length,2,d)},MultiPolygon:function(a,b,c,d){var e=c.f,f=c.g;if(f||e){var g=a.b(c.b,"Polygon");g.Ma(e,f);g.wc(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),b=Ti(b),zj(a,b,b.length,2,d)},GeometryCollection:function(a,
+b,c,d){b=b.f;var e,f;e=0;for(f=b.length;e<f;++e)(0,Mj[b[e].Y()])(a,b[e],c,d)},Circle:function(a,b,c,d){var e=c.f,f=c.g;if(e||f){var g=a.b(c.b,"Circle");g.Ma(e,f);g.hc(b,d)}if(e=c.Ka())a=a.b(c.b,"Text"),a.$b(e),zj(a,b.Fd(),2,2,d)}};function Nj(a){cj.call(this,a);this.f=!1;this.u=-1;this.H=NaN;this.j=Hb();this.c=this.l=null;this.i=De()}v(Nj,cj);
+Nj.prototype.D=function(a,b,c){var d=a.extent,e=a.pixelRatio,f=b.me?a.skippedFeatureUids:{},g=a.viewState,h=g.projection,g=g.rotation,l=h.G(),m=this.a.la(),p=fj(this,a,0);ej(this,"precompose",c,a,p);var n=b.extent,q=void 0!==n;q&&dj(c,a,n);if((n=this.c)&&!n.g()){var r=0,u=0,w;if(Oa(this.a,"render")){w=c.canvas.width;var y=c.canvas.height;if(g){var z=Math.round(Math.sqrt(w*w+y*y)),r=(z-w)/2,u=(z-y)/2;w=y=z}this.i.canvas.width=w;this.i.canvas.height=y;w=this.i}else w=c;y=w.globalAlpha;w.globalAlpha=
+b.opacity;w!=c&&w.translate(r,u);var z=a.size[0]*e,A=a.size[1]*e;qi(w,-g,z/2,A/2);n.f(w,e,p,g,f);if(m.D&&h.a&&!Ob(l,d)){for(var h=d[0],m=dc(l),O=0;h<l[0];)--O,p=m*O,p=fj(this,a,p),n.f(w,e,p,g,f),h+=m;O=0;for(h=d[2];h>l[2];)++O,p=m*O,p=fj(this,a,p),n.f(w,e,p,g,f),h-=m;p=fj(this,a,0)}qi(w,g,z/2,A/2);w!=c&&(ej(this,"render",w,a,p),c.drawImage(w.canvas,-r,-u),w.translate(-r,-u));w.globalAlpha=y}q&&c.restore();this.v(c,a,b,p)};
+Nj.prototype.Ba=function(a,b,c,d,e){if(this.c){var f=this.a,g={};return this.c.Ba(a,b.viewState.resolution,b.viewState.rotation,c,{},function(a){var b=x(a).toString();if(!(b in g))return g[b]=!0,d.call(e,a,f)})}};Nj.prototype.A=function(){Wi(this)};
+Nj.prototype.o=function(a){function b(a){var b,d=a.Gc();d?b=d.call(a,m):(d=c.j)&&(b=d(a,m));if(b){if(b){d=!1;if(Array.isArray(b))for(var e=0,f=b.length;e<f;++e)d=Lj(q,a,b[e],Kj(m,p),this.A,this)||d;else d=Lj(q,a,b,Kj(m,p),this.A,this)||d;a=d}else a=!1;this.f=this.f||a}}var c=this.a,d=c.la();Zi(a.attributions,d.j);$i(a,d);var e=a.viewHints[Kd],f=a.viewHints[1],g=c.Z,h=c.fa;if(!this.f&&!g&&e||!h&&f)return!0;var l=a.extent,h=a.viewState,e=h.projection,m=h.resolution,p=a.pixelRatio,f=c.g,n=c.i,g=c.get("renderOrder");
+void 0===g&&(g=Jj);l=Jb(l,n*m);n=h.projection.G();d.D&&h.projection.a&&!Ob(n,a.extent)&&(a=Math.max(dc(l)/2,dc(n)),l[0]=n[0]-a,l[2]=n[2]+a);if(!this.f&&this.H==m&&this.u==f&&this.l==g&&Ob(this.j,l))return!0;this.c=null;this.f=!1;var q=new Bj(.5*m/p,l,m,d.xa,c.i);d.Ed(l,m,e);if(g){var r=[];d.Qb(l,function(a){r.push(a)},this);r.sort(g);r.forEach(b,this)}else d.Qb(l,b,this);Fj(q);this.H=m;this.u=f;this.l=g;this.j=l;this.c=q;return!0};function Oj(a){ij.call(this,a);this.Z=!1;this.sa=Ph();this.na=a.u==Fi?1:0}v(Oj,ij);var Pj={image:Aj,hybrid:["Polygon","LineString"]},Qj={hybrid:["Image","Text"],vector:Aj};
+function Rj(a,b,c){function d(a){var b,c=a.Gc();c?b=c.call(a,r):(c=e.j)&&(b=c(a,r));if(b){Array.isArray(b)||(b=[b]);var c=A,d=z;if(b){var f=!1;if(Array.isArray(b))for(var g=0,h=b.length;g<h;++g)f=Lj(d,a,b[g],c,this.fa,this)||f;else f=Lj(d,a,b,c,this.fa,this)||f;a=f}else a=!1;this.Z=this.Z||a;l.xd=l.xd||a}}var e=a.a,f=c.pixelRatio;c=c.viewState.projection;var g=e.g,h=e.get("renderOrder")||null,l=b.g;if(l.xd||l.li!=g||l.bg!=h){l.jd=null;l.xd=!1;var m=e.la(),p=m.tileGrid,n=b.Ca,q=b.j,r=p.Ha(n[0]),u,
+w,y;"tile-pixels"==q.Eb()?(u=y=m.jb(),p=Zd(p.Za(n[0])),u=[0,0,p[0]*u,p[1]*u]):(y=r,u=p.Na(n),Mc(c,q)||(w=!0,b.Ff(c)));l.xd=!1;var z=new Bj(0,u,y,m.i,e.i),A=Kj(y,f);b=b.i;h&&h!==l.bg&&b.sort(h);m=0;for(y=b.length;m<y;++m)f=b[m],w&&f.V().ob(q,c),d.call(a,f);Fj(z);l.li=g;l.bg=h;l.jd=z;l.resolution=NaN}}
+Oj.prototype.A=function(a,b,c,d,e,f,g,h){var l=a;Rj(this,l,b);if(this.a.u!=Fi){var m=l,p=b,n=this.a,l=m.g,q=n.g,r=Pj[n.u];if(r&&l.cg!==q){l.cg=q;var u=m.Ca,w=m.Ca[0],q=p.pixelRatio,y=n.la(),z=y.tileGrid,A=y.jb(),n=Qh(this.sa);"tile-pixels"==m.j.Eb()?(u=q/A,Wh(n,u,u)):(A=z.Ha(w),A=q/A,u=z.Na(u,this.H),Wh(n,A,-A),Xh(n,-u[0],-u[3]));m=m.f;p=y.Dd(w,q,p.viewState.projection);m.canvas.width=p[0];m.canvas.height=p[1];l.jd.f(m,q,n,0,{},r)}}ij.prototype.A.apply(this,arguments)};
+Oj.prototype.Ba=function(a,b,c,d,e){var f=b.viewState.resolution;b=b.viewState.rotation;c=void 0==c?0:c;var g=this.a,h={},l=this.f,m=g.la(),p=m.tileGrid,n,q,r,u,w,y;r=0;for(u=l.length;r<u;++r)y=l[r],q=y.Ca,w=m.tileGrid.Na(q,this.H),Mb(Jb(w,c*f),a)&&("tile-pixels"===y.j.Eb()?(w=ac(w),f=m.jb(),q=p.Ha(q[0])/f,q=[(a[0]-w[0])/q,(w[1]-a[1])/q]):q=a,y=y.g.jd,n=n||y.Ba(q,f,b,c,{},function(a){var b=x(a).toString();if(!(b in h))return h[b]=!0,d.call(e,a,g)}));return n};Oj.prototype.fa=function(){Wi(this)};
+Oj.prototype.v=function(a,b,c){var d=Qj[this.a.u];if(d)for(var e=b.pixelRatio,f=b.viewState.rotation,g=b.size,h=Math.round(e*g[0]/2),g=Math.round(e*g[1]/2),l=this.f,m=[],p=[],n=l.length-1;0<=n;--n){var q=l[n],r;var u=q;r=b;if("tile-pixels"==u.j.Eb()){var w=this.a.la(),y=w.tileGrid,z=u.Ca,w=y.Ha(z[0])/w.jb(),u=r.viewState,A=r.pixelRatio,O=u.resolution/A,z=y.Na(z,this.H),y=u.center,z=ac(z);r=r.size;r=Yh(this.sa,Math.round(A*r[0]/2),Math.round(A*r[1]/2),w/O,w/O,u.rotation,(z[0]-y[0])/w,(y[1]-z[1])/w)}else r=
+fj(this,r,0);w=Hj(q.g.jd,r);u=q.Ca[0];a.save();a.globalAlpha=c.opacity;qi(a,-f,h,g);A=0;for(O=m.length;A<O;++A)y=m[A],u<p[A]&&(a.beginPath(),a.moveTo(w[0],w[1]),a.lineTo(w[2],w[3]),a.lineTo(w[4],w[5]),a.lineTo(w[6],w[7]),a.moveTo(y[6],y[7]),a.lineTo(y[4],y[5]),a.lineTo(y[2],y[3]),a.lineTo(y[0],y[1]),a.clip());q.g.jd.f(a,e,r,f,{},d);a.restore();m.push(w);p.push(u)}ij.prototype.v.apply(this,arguments)};function Sj(a,b){$h.call(this,0,b);this.g=De();this.b=this.g.canvas;this.b.style.width="100%";this.b.style.height="100%";this.b.className="ol-unselectable";a.insertBefore(this.b,a.childNodes[0]||null);this.a=!0;this.c=Ph()}v(Sj,$h);Sj.prototype.Ag=function(a){return a instanceof ei?new hj(a):a instanceof F?new ij(a):a instanceof H?new Oj(a):a instanceof G?new Nj(a):null};
+function Tj(a,b,c){var d=a.l,e=a.g;if(Oa(d,b)){var f=c.extent,g=c.pixelRatio,h=c.viewState.rotation,l=c.viewState,m=c.pixelRatio/l.resolution;a=Yh(a.c,a.b.width/2,a.b.height/2,m,-m,-l.rotation,-l.center[0],-l.center[1]);d.b(new Jh(b,new Ji(e,g,f,a,h),c,e,null))}}Sj.prototype.Y=function(){return"canvas"};
+Sj.prototype.ag=function(a){if(a){var b=this.g,c=a.pixelRatio,d=Math.round(a.size[0]*c),c=Math.round(a.size[1]*c);this.b.width!=d||this.b.height!=c?(this.b.width=d,this.b.height=c):b.clearRect(0,0,d,c);var e=a.viewState.rotation;ai(a);Tj(this,"precompose",a);var f=a.layerStatesArray;eb(f);qi(b,e,d/2,c/2);var g=a.viewState.resolution,h,l,m,p;h=0;for(l=f.length;h<l;++h)p=f[h],m=p.layer,m=ci(this,m),Lh(p,g)&&"ready"==p.Ei&&m.o(a,p)&&m.D(a,p,b);qi(b,-e,d/2,c/2);Tj(this,"postcompose",a);this.a||(this.b.style.display=
+"",this.a=!0);di(this,a);a.postRenderFunctions.push(bi)}else this.a&&(this.b.style.display="none",this.a=!1)};Sj.prototype.Bh=function(a,b,c,d,e,f){var g,h=b.viewState.resolution,l=b.layerStatesArray,m=l.length;a=Uh(b.pixelToCoordinateTransform,a.slice());for(--m;0<=m;--m){g=l[m];var p=g.layer;if(Lh(g,h)&&e.call(f,p)&&(g=ci(this,p).C(a,b,c,d)))return g}};var Uj=[0,0,0,1],Vj=[],Wj=[0,0,0,1];function Xj(a,b,c,d,e,f){a=(c-a)*(f-b)-(e-a)*(d-b);return a<=Yj&&a>=-Yj?void 0:0<a}var Yj=Number.EPSILON||2.220446049250313E-16;function Zj(a){this.b=a};function ak(a){this.b=a}v(ak,Zj);ak.prototype.Y=function(){return 35632};function bk(a){this.b=a}v(bk,Zj);bk.prototype.Y=function(){return 35633};function ck(){this.b="precision mediump float;varying vec2 a;varying vec2 b;varying float c;varying float d;uniform float m;uniform vec4 n;uniform vec4 o;uniform vec2 p;void main(void){vec2 windowCenter=vec2((a.x+1.0)/2.0*p.x*d,(a.y+1.0)/2.0*p.y*d);vec2 windowOffset=vec2((b.x+1.0)/2.0*p.x*d,(b.y+1.0)/2.0*p.y*d);float radius=length(windowCenter-windowOffset);float dist=length(windowCenter-gl_FragCoord.xy);if(dist>radius+c){if(o.a==0.0){gl_FragColor=n;}else{gl_FragColor=o;}gl_FragColor.a=gl_FragColor.a-(dist-(radius+c));}else if(n.a==0.0){gl_FragColor=o;if(dist<radius-c){gl_FragColor.a=gl_FragColor.a-(radius-c-dist);}} else{gl_FragColor=n;float strokeDist=radius-c;float antialias=2.0*d;if(dist>strokeDist){gl_FragColor=o;}else if(dist>=strokeDist-antialias){float step=smoothstep(strokeDist-antialias,strokeDist,dist);gl_FragColor=mix(n,o,step);}} gl_FragColor.a=gl_FragColor.a*m;if(gl_FragColor.a<=0.0){discard;}}"}
+v(ck,ak);var dk=new ck;
+function ek(){this.b="varying vec2 a;varying vec2 b;varying float c;varying float d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;void main(void){mat4 offsetMatrix=i*j;a=vec4(h*vec4(e,0.0,1.0)).xy;d=l;float lineWidth=k*l;c=lineWidth/2.0;if(lineWidth==0.0){lineWidth=2.0*l;}vec2 offset;float radius=g+3.0*l;if(f==0.0){offset=vec2(-1.0,1.0);}else if(f==1.0){offset=vec2(-1.0,-1.0);}else if(f==2.0){offset=vec2(1.0,-1.0);}else{offset=vec2(1.0,1.0);}gl_Position=h*vec4(e+offset*radius,0.0,1.0)+offsetMatrix*vec4(offset*lineWidth,0.0,0.0);b=vec4(h*vec4(e.x+g,e.y,0.0,1.0)).xy;if(distance(a,b)>20000.0){gl_Position=vec4(a,0.0,1.0);}}"}
+v(ek,bk);var fk=new ek;function gk(a,b){this.D=a.getUniformLocation(b,"n");this.L=a.getUniformLocation(b,"k");this.f=a.getUniformLocation(b,"j");this.c=a.getUniformLocation(b,"i");this.a=a.getUniformLocation(b,"m");this.ra=a.getUniformLocation(b,"l");this.g=a.getUniformLocation(b,"h");this.va=a.getUniformLocation(b,"p");this.P=a.getUniformLocation(b,"o");this.j=a.getAttribLocation(b,"f");this.b=a.getAttribLocation(b,"e");this.u=a.getAttribLocation(b,"g")};function hk(){return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}function ik(a,b){a[0]=b[0];a[1]=b[1];a[4]=b[2];a[5]=b[3];a[12]=b[4];a[13]=b[5];return a};function jk(a,b){this.origin=gc(b);this.Ua=Ph();this.xa=Ph();this.Ja=Ph();this.na=hk();this.b=[];this.o=null;this.g=[];this.i=[];this.a=[];this.v=null;this.j=void 0}v(jk,Ii);
+jk.prototype.f=function(a,b,c,d,e,f,g,h,l,m,p){var n=a.b,q,r,u,w,y,z,A,O;this.j&&(q=n.isEnabled(n.STENCIL_TEST),r=n.getParameter(n.STENCIL_FUNC),u=n.getParameter(n.STENCIL_VALUE_MASK),w=n.getParameter(n.STENCIL_REF),y=n.getParameter(n.STENCIL_WRITEMASK),z=n.getParameter(n.STENCIL_FAIL),A=n.getParameter(n.STENCIL_PASS_DEPTH_PASS),O=n.getParameter(n.STENCIL_PASS_DEPTH_FAIL),n.enable(n.STENCIL_TEST),n.clear(n.STENCIL_BUFFER_BIT),n.stencilMask(255),n.stencilFunc(n.ALWAYS,1,255),n.stencilOp(n.KEEP,n.KEEP,
+n.REPLACE),this.j.f(a,b,c,d,e,f,g,h,l,m,p),n.stencilMask(0),n.stencilFunc(n.NOTEQUAL,1,255));kk(a,34962,this.v);kk(a,34963,this.o);f=this.Ke(n,a,e,f);var Ja=Qh(this.Ua);Wh(Ja,2/(c*e[0]),2/(c*e[1]));Vh(Ja,-d);Xh(Ja,-(b[0]-this.origin[0]),-(b[1]-this.origin[1]));b=Qh(this.Ja);Wh(b,2/e[0],2/e[1]);e=Qh(this.xa);0!==d&&Vh(e,-d);n.uniformMatrix4fv(f.g,!1,ik(this.na,Ja));n.uniformMatrix4fv(f.c,!1,ik(this.na,b));n.uniformMatrix4fv(f.f,!1,ik(this.na,e));n.uniform1f(f.a,g);var ca;void 0===l?this.yd(n,a,h,!1):
+(m?a=this.$d(n,a,h,l,p):(n.clear(n.COLOR_BUFFER_BIT|n.DEPTH_BUFFER_BIT),this.yd(n,a,h,!0),a=(a=l(null))?a:void 0),ca=a);this.Le(n,f);this.j&&(q||n.disable(n.STENCIL_TEST),n.clear(n.STENCIL_BUFFER_BIT),n.stencilFunc(r,w,u),n.stencilMask(y),n.stencilOp(z,O,A));return ca};function lk(a,b,c,d){a.drawElements(4,d-c,b.i?5125:5123,c*(b.i?4:2))};function mk(a){this.b=void 0!==a?a:[];this.a=nk}var nk=35044;function ok(a,b){jk.call(this,0,b);this.H=null;this.l=[];this.u=[];this.A=0;this.c={fillColor:null,strokeColor:null,lineDash:null,lineWidth:void 0,s:!1}}v(ok,jk);k=ok.prototype;
+k.hc=function(a,b){var c=a.qe(),d=a.pa();if(c){this.g.push(this.b.length);this.i.push(b);this.c.s&&(this.u.push(this.b.length),this.c.s=!1);this.A=c;var c=a.ia(),c=Tc(c,2,d,-this.origin[0],-this.origin[1]),e=this.a.length,f=this.b.length,g=e/4,h;for(h=0;2>h;h+=d)this.a[e++]=c[h],this.a[e++]=c[h+1],this.a[e++]=0,this.a[e++]=this.A,this.a[e++]=c[h],this.a[e++]=c[h+1],this.a[e++]=1,this.a[e++]=this.A,this.a[e++]=c[h],this.a[e++]=c[h+1],this.a[e++]=2,this.a[e++]=this.A,this.a[e++]=c[h],this.a[e++]=c[h+
+1],this.a[e++]=3,this.a[e++]=this.A,this.b[f++]=g,this.b[f++]=g+1,this.b[f++]=g+2,this.b[f++]=g+2,this.b[f++]=g+3,this.b[f++]=g,g+=4}else this.c.s&&(this.l.pop(),this.l.length&&(d=this.l[this.l.length-1],this.c.fillColor=d[0],this.c.strokeColor=d[1],this.c.lineWidth=d[2],this.c.s=!1))};k.vb=function(){this.v=new mk(this.a);this.o=new mk(this.b);this.g.push(this.b.length);0===this.u.length&&0<this.l.length&&(this.l=[]);this.b=this.a=null};
+k.wb=function(a){var b=this.v,c=this.o;return function(){pk(a,b);pk(a,c)}};k.Ke=function(a,b,c,d){var e=qk(b,dk,fk),f;this.H?f=this.H:this.H=f=new gk(a,e);b.Lc(e);a.enableVertexAttribArray(f.b);a.vertexAttribPointer(f.b,2,5126,!1,16,0);a.enableVertexAttribArray(f.j);a.vertexAttribPointer(f.j,1,5126,!1,16,8);a.enableVertexAttribArray(f.u);a.vertexAttribPointer(f.u,1,5126,!1,16,12);a.uniform2fv(f.va,c);a.uniform1f(f.ra,d);return f};
+k.Le=function(a,b){a.disableVertexAttribArray(b.b);a.disableVertexAttribArray(b.j);a.disableVertexAttribArray(b.u)};
+k.yd=function(a,b,c){if(wa(c)){var d,e,f;e=this.g[this.g.length-1];for(c=this.u.length-1;0<=c;--c)d=this.u[c],f=this.l[c],a.uniform4fv(this.H.D,f[0]),rk(this,a,f[1],f[2]),lk(a,b,d,e),e=d}else{var g,h,l,m;l=this.g.length-2;f=e=this.g[l+1];for(d=this.u.length-1;0<=d;--d){g=this.l[d];a.uniform4fv(this.H.D,g[0]);rk(this,a,g[1],g[2]);for(g=this.u[d];0<=l&&this.g[l]>=g;)m=this.g[l],h=this.i[l],h=x(h).toString(),c[h]&&(e!==f&&lk(a,b,e,f),f=m),l--,e=m;e!==f&&lk(a,b,e,f);e=f=g}}};
+k.$d=function(a,b,c,d,e){var f,g,h,l,m,p,n;n=this.g.length-2;h=this.g[n+1];for(f=this.u.length-1;0<=f;--f)for(g=this.l[f],a.uniform4fv(this.H.D,g[0]),rk(this,a,g[1],g[2]),l=this.u[f];0<=n&&this.g[n]>=l;){g=this.g[n];m=this.i[n];p=x(m).toString();if(void 0===c[p]&&m.V()&&(void 0===e||jc(e,m.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),lk(a,b,g,h),h=d(m)))return h;n--;h=g}};function rk(a,b,c,d){b.uniform4fv(a.H.P,c);b.uniform1f(a.H.L,d)}
+k.Ma=function(a,b){var c,d;b?(c=b.g,this.c.lineDash=c?c:Vj,c=b.b,c instanceof CanvasGradient||c instanceof CanvasPattern?c=Wj:c=ye(c).map(function(a,b){return 3!=b?a/255:a})||Wj,d=b.f,d=void 0!==d?d:1):(c=[0,0,0,0],d=0);var e=a?a.b:[0,0,0,0];e instanceof CanvasGradient||e instanceof CanvasPattern?e=Uj:e=ye(e).map(function(a,b){return 3!=b?a/255:a})||Uj;this.c.strokeColor&&db(this.c.strokeColor,c)&&this.c.fillColor&&db(this.c.fillColor,e)&&this.c.lineWidth===d||(this.c.s=!0,this.c.fillColor=e,this.c.strokeColor=
+c,this.c.lineWidth=d,this.l.push([e,c,d]))};function sk(){this.b="precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}v(sk,ak);var tk=new sk;
+function uk(){this.b="varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.0,0.0);gl_Position=h*vec4(c,0.0,1.0)+offsets;a=d;b=f;}"}v(uk,bk);var vk=new uk;
+function wk(a,b){this.f=a.getUniformLocation(b,"j");this.c=a.getUniformLocation(b,"i");this.a=a.getUniformLocation(b,"k");this.g=a.getUniformLocation(b,"h");this.v=a.getAttribLocation(b,"e");this.H=a.getAttribLocation(b,"f");this.b=a.getAttribLocation(b,"c");this.A=a.getAttribLocation(b,"g");this.C=a.getAttribLocation(b,"d")};function xk(a,b){this.j=a;this.b=b;this.a={};this.f={};this.g={};this.o=this.v=this.c=this.l=null;(this.i=Za(da,"OES_element_index_uint"))&&b.getExtension("OES_element_index_uint");B(this.j,"webglcontextlost",this.co,this);B(this.j,"webglcontextrestored",this.eo,this)}v(xk,Ga);
+function kk(a,b,c){var d=a.b,e=c.b,f=String(x(c));if(f in a.a)d.bindBuffer(b,a.a[f].buffer);else{var g=d.createBuffer();d.bindBuffer(b,g);var h;34962==b?h=new Float32Array(e):34963==b&&(h=a.i?new Uint32Array(e):new Uint16Array(e));d.bufferData(b,h,c.a);a.a[f]={gc:c,buffer:g}}}function pk(a,b){var c=a.b,d=String(x(b)),e=a.a[d];c.isContextLost()||c.deleteBuffer(e.buffer);delete a.a[d]}k=xk.prototype;
+k.oa=function(){Fa(this.j);var a=this.b;if(!a.isContextLost()){for(var b in this.a)a.deleteBuffer(this.a[b].buffer);for(b in this.g)a.deleteProgram(this.g[b]);for(b in this.f)a.deleteShader(this.f[b]);a.deleteFramebuffer(this.c);a.deleteRenderbuffer(this.o);a.deleteTexture(this.v)}};k.bo=function(){return this.b};
+function yk(a){if(!a.c){var b=a.b,c=b.createFramebuffer();b.bindFramebuffer(b.FRAMEBUFFER,c);var d=zk(b,1,1),e=b.createRenderbuffer();b.bindRenderbuffer(b.RENDERBUFFER,e);b.renderbufferStorage(b.RENDERBUFFER,b.DEPTH_COMPONENT16,1,1);b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,d,0);b.framebufferRenderbuffer(b.FRAMEBUFFER,b.DEPTH_ATTACHMENT,b.RENDERBUFFER,e);b.bindTexture(b.TEXTURE_2D,null);b.bindRenderbuffer(b.RENDERBUFFER,null);b.bindFramebuffer(b.FRAMEBUFFER,null);a.c=c;
+a.v=d;a.o=e}return a.c}function Ak(a,b){var c=String(x(b));if(c in a.f)return a.f[c];var d=a.b,e=d.createShader(b.Y());d.shaderSource(e,b.b);d.compileShader(e);return a.f[c]=e}function qk(a,b,c){var d=x(b)+"/"+x(c);if(d in a.g)return a.g[d];var e=a.b,f=e.createProgram();e.attachShader(f,Ak(a,b));e.attachShader(f,Ak(a,c));e.linkProgram(f);return a.g[d]=f}k.co=function(){ua(this.a);ua(this.f);ua(this.g);this.o=this.v=this.c=this.l=null};k.eo=function(){};
+k.Lc=function(a){if(a==this.l)return!1;this.b.useProgram(a);this.l=a;return!0};function Bk(a,b,c){var d=a.createTexture();a.bindTexture(a.TEXTURE_2D,d);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR);void 0!==b&&a.texParameteri(3553,10242,b);void 0!==c&&a.texParameteri(3553,10243,c);return d}function zk(a,b,c){var d=Bk(a,void 0,void 0);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,b,c,0,a.RGBA,a.UNSIGNED_BYTE,null);return d}
+function Ck(a,b){var c=Bk(a,33071,33071);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,b);return c};function Dk(a,b){jk.call(this,0,b);this.D=this.C=void 0;this.A=[];this.H=[];this.ra=void 0;this.l=[];this.c=[];this.P=this.va=void 0;this.L=null;this.sa=this.fa=this.eb=this.Z=this.Qa=this.U=void 0;this.Fa=[];this.u=[];this.zb=void 0}v(Dk,jk);k=Dk.prototype;k.wb=function(a){var b=this.v,c=this.o,d=this.Fa,e=this.u,f=a.b;return function(){if(!f.isContextLost()){var g,h;g=0;for(h=d.length;g<h;++g)f.deleteTexture(d[g]);g=0;for(h=e.length;g<h;++g)f.deleteTexture(e[g])}pk(a,b);pk(a,c)}};
+function Ek(a,b,c,d){var e=a.C,f=a.D,g=a.ra,h=a.va,l=a.P,m=a.U,p=a.Qa,n=a.Z,q=a.eb?1:0,r=-a.fa,u=a.sa,w=a.zb,y=Math.cos(r),r=Math.sin(r),z=a.b.length,A=a.a.length,O,Ja,ca,Ma,D,La;for(O=0;O<c;O+=d)D=b[O]-a.origin[0],La=b[O+1]-a.origin[1],Ja=A/8,ca=-u*e,Ma=-u*(g-f),a.a[A++]=D,a.a[A++]=La,a.a[A++]=ca*y-Ma*r,a.a[A++]=ca*r+Ma*y,a.a[A++]=p/l,a.a[A++]=(n+g)/h,a.a[A++]=m,a.a[A++]=q,ca=u*(w-e),Ma=-u*(g-f),a.a[A++]=D,a.a[A++]=La,a.a[A++]=ca*y-Ma*r,a.a[A++]=ca*r+Ma*y,a.a[A++]=(p+w)/l,a.a[A++]=(n+g)/h,a.a[A++]=
+m,a.a[A++]=q,ca=u*(w-e),Ma=u*f,a.a[A++]=D,a.a[A++]=La,a.a[A++]=ca*y-Ma*r,a.a[A++]=ca*r+Ma*y,a.a[A++]=(p+w)/l,a.a[A++]=n/h,a.a[A++]=m,a.a[A++]=q,ca=-u*e,Ma=u*f,a.a[A++]=D,a.a[A++]=La,a.a[A++]=ca*y-Ma*r,a.a[A++]=ca*r+Ma*y,a.a[A++]=p/l,a.a[A++]=n/h,a.a[A++]=m,a.a[A++]=q,a.b[z++]=Ja,a.b[z++]=Ja+1,a.b[z++]=Ja+2,a.b[z++]=Ja,a.b[z++]=Ja+2,a.b[z++]=Ja+3}k.vc=function(a,b){this.g.push(this.b.length);this.i.push(b);var c=a.ia();Ek(this,c,c.length,a.pa())};
+k.xc=function(a,b){this.g.push(this.b.length);this.i.push(b);var c=a.ia();Ek(this,c,c.length,a.pa())};k.vb=function(a){a=a.b;this.A.push(this.b.length);this.H.push(this.b.length);this.v=new mk(this.a);this.o=new mk(this.b);var b={};Fk(this.Fa,this.l,b,a);Fk(this.u,this.c,b,a);this.ra=this.D=this.C=void 0;this.c=this.l=null;this.P=this.va=void 0;this.b=null;this.sa=this.fa=this.eb=this.Z=this.Qa=this.U=void 0;this.a=null;this.zb=void 0};
+function Fk(a,b,c,d){var e,f,g,h=b.length;for(g=0;g<h;++g)e=b[g],f=x(e).toString(),f in c?e=c[f]:(e=Ck(d,e),c[f]=e),a[g]=e}
+k.Ke=function(a,b){var c=qk(b,tk,vk),d;this.L?d=this.L:this.L=d=new wk(a,c);b.Lc(c);a.enableVertexAttribArray(d.b);a.vertexAttribPointer(d.b,2,5126,!1,32,0);a.enableVertexAttribArray(d.v);a.vertexAttribPointer(d.v,2,5126,!1,32,8);a.enableVertexAttribArray(d.C);a.vertexAttribPointer(d.C,2,5126,!1,32,16);a.enableVertexAttribArray(d.H);a.vertexAttribPointer(d.H,1,5126,!1,32,24);a.enableVertexAttribArray(d.A);a.vertexAttribPointer(d.A,1,5126,!1,32,28);return d};
+k.Le=function(a,b){a.disableVertexAttribArray(b.b);a.disableVertexAttribArray(b.v);a.disableVertexAttribArray(b.C);a.disableVertexAttribArray(b.H);a.disableVertexAttribArray(b.A)};
+k.yd=function(a,b,c,d){var e=d?this.u:this.Fa;d=d?this.H:this.A;if(wa(c)){var f,g;c=0;f=e.length;for(g=0;c<f;++c){a.bindTexture(3553,e[c]);var h=d[c];lk(a,b,g,h);g=h}}else for(g=f=0,h=e.length;g<h;++g){a.bindTexture(3553,e[g]);for(var l=0<g?d[g-1]:0,m=d[g],p=l;f<this.g.length&&this.g[f]<=m;){var n=x(this.i[f]).toString();void 0!==c[n]?(p!==l&&lk(a,b,p,l),l=p=f===this.g.length-1?m:this.g[f+1]):l=f===this.g.length-1?m:this.g[f+1];f++}p!==l&&lk(a,b,p,l)}};
+k.$d=function(a,b,c,d,e){var f,g,h,l,m,p,n=this.g.length-1;for(f=this.u.length-1;0<=f;--f)for(a.bindTexture(3553,this.u[f]),g=0<f?this.H[f-1]:0,l=this.H[f];0<=n&&this.g[n]>=g;){h=this.g[n];m=this.i[n];p=x(m).toString();if(void 0===c[p]&&m.V()&&(void 0===e||jc(e,m.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),lk(a,b,h,l),l=d(m)))return l;l=h;n--}};
+k.dc=function(a){var b=a.Ac(),c=a.Ic(1),d=a.de(),e=a.Lf(1),f=a.l,g=a.Jc(),h=a.H,l=a.o,m=a.ac();a=a.i;var p;0===this.l.length?this.l.push(c):(p=this.l[this.l.length-1],x(p)!=x(c)&&(this.A.push(this.b.length),this.l.push(c)));0===this.c.length?this.c.push(e):(p=this.c[this.c.length-1],x(p)!=x(e)&&(this.H.push(this.b.length),this.c.push(e)));this.C=b[0];this.D=b[1];this.ra=m[1];this.va=d[1];this.P=d[0];this.U=f;this.Qa=g[0];this.Z=g[1];this.fa=l;this.eb=h;this.sa=a;this.zb=m[0]};function Gk(a,b,c){var d=b-c;return a[0]===a[d]&&a[1]===a[d+1]&&3<(b-0)/c?!!Zc(a,0,b,c):!1};function Hk(){this.b="precision mediump float;varying float a;varying vec2 b;varying float c;uniform float m;uniform vec4 n;uniform vec2 o;uniform float p;void main(void){if(a>0.0){vec2 windowCoords=vec2((b.x+1.0)/2.0*o.x*p,(b.y+1.0)/2.0*o.y*p);if(length(windowCoords-gl_FragCoord.xy)>c*p){discard;}} gl_FragColor=n;float alpha=n.a*m;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}v(Hk,ak);var Ik=new Hk;
+function Jk(){this.b="varying float a;varying vec2 b;varying float c;attribute vec2 d;attribute vec2 e;attribute vec2 f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;bool nearlyEquals(in float value,in float ref){float epsilon=0.000000000001;return value>=ref-epsilon&&value<=ref+epsilon;}void alongNormal(out vec2 offset,in vec2 nextP,in float turnDir,in float direction){vec2 dirVect=nextP-e;vec2 normal=normalize(vec2(-turnDir*dirVect.y,turnDir*dirVect.x));offset=k/2.0*normal*direction;}void miterUp(out vec2 offset,out float round,in bool isRound,in float direction){float halfWidth=k/2.0;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=f-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;round=0.0;if(isRound){round=1.0;}else if(miterLength>l+k){offset=halfWidth*tmpNormal*direction;}} bool miterDown(out vec2 offset,in vec4 projPos,in mat4 offsetMatrix,in float direction){bool degenerate=false;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=d-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));vec2 longOffset,shortOffset,longVertex;vec4 shortProjVertex;float halfWidth=k/2.0;if(length(f-e)>length(d-e)){longOffset=tmpNormal*direction*halfWidth;shortOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=f;shortProjVertex=h*vec4(d,0.0,1.0);}else{shortOffset=tmpNormal*direction*halfWidth;longOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=d;shortProjVertex=h*vec4(f,0.0,1.0);}vec4 p1=h*vec4(longVertex,0.0,1.0)+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p2=projPos+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p3=shortProjVertex+offsetMatrix*vec4(-shortOffset,0.0,0.0);vec4 p4=shortProjVertex+offsetMatrix*vec4(shortOffset,0.0,0.0);float denom=(p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y);float firstU=((p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x))/denom;float secondU=((p2.x-p1.x)*(p1.y-p3.y)-(p2.y-p1.y)*(p1.x-p3.x))/denom;float epsilon=0.000000000001;if(firstU>epsilon&&firstU<1.0-epsilon&&secondU>epsilon&&secondU<1.0-epsilon){shortProjVertex.x=p1.x+firstU*(p2.x-p1.x);shortProjVertex.y=p1.y+firstU*(p2.y-p1.y);offset=shortProjVertex.xy;degenerate=true;}else{float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;}return degenerate;}void squareCap(out vec2 offset,out float round,in bool isRound,in vec2 nextP,in float turnDir,in float direction){round=0.0;vec2 dirVect=e-nextP;vec2 firstNormal=normalize(dirVect);vec2 secondNormal=vec2(turnDir*firstNormal.y*direction,-turnDir*firstNormal.x*direction);vec2 hypotenuse=normalize(firstNormal-secondNormal);vec2 normal=vec2(turnDir*hypotenuse.y*direction,-turnDir*hypotenuse.x*direction);float length=sqrt(c*c*2.0);offset=normal*length;if(isRound){round=1.0;}} void main(void){bool degenerate=false;float direction=float(sign(g));mat4 offsetMatrix=i*j;vec2 offset;vec4 projPos=h*vec4(e,0.0,1.0);bool round=nearlyEquals(mod(g,2.0),0.0);a=0.0;c=k/2.0;b=projPos.xy;if(nearlyEquals(mod(g,3.0),0.0)||nearlyEquals(mod(g,17.0),0.0)){alongNormal(offset,f,1.0,direction);}else if(nearlyEquals(mod(g,5.0),0.0)||nearlyEquals(mod(g,13.0),0.0)){alongNormal(offset,d,-1.0,direction);}else if(nearlyEquals(mod(g,23.0),0.0)){miterUp(offset,a,round,direction);}else if(nearlyEquals(mod(g,19.0),0.0)){degenerate=miterDown(offset,projPos,offsetMatrix,direction);}else if(nearlyEquals(mod(g,7.0),0.0)){squareCap(offset,a,round,f,1.0,direction);}else if(nearlyEquals(mod(g,11.0),0.0)){squareCap(offset,a,round,d,-1.0,direction);}if(!degenerate){vec4 offsets=offsetMatrix*vec4(offset,0.0,0.0);gl_Position=projPos+offsets;}else{gl_Position=vec4(offset,0.0,1.0);}}"}
+v(Jk,bk);var Kk=new Jk;function Lk(a,b){this.D=a.getUniformLocation(b,"n");this.L=a.getUniformLocation(b,"k");this.P=a.getUniformLocation(b,"l");this.f=a.getUniformLocation(b,"j");this.c=a.getUniformLocation(b,"i");this.a=a.getUniformLocation(b,"m");this.ra=a.getUniformLocation(b,"p");this.g=a.getUniformLocation(b,"h");this.va=a.getUniformLocation(b,"o");this.i=a.getAttribLocation(b,"g");this.l=a.getAttribLocation(b,"d");this.o=a.getAttribLocation(b,"f");this.b=a.getAttribLocation(b,"e")};function Mk(a,b){jk.call(this,0,b);this.H=null;this.u=[];this.l=[];this.c={strokeColor:null,lineCap:void 0,lineDash:null,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0,s:!1}}v(Mk,jk);
+function Nk(a,b,c,d){var e,f=a.a.length,g=a.b.length,h="bevel"===a.c.lineJoin?0:"miter"===a.c.lineJoin?1:2,l="butt"===a.c.lineCap?0:"square"===a.c.lineCap?1:2,m=Gk(b,c,d),p,n,q,r=g,u=1,w,y,z;for(e=0;e<c;e+=d){q=f/7;w=y;y=z||[b[e],b[e+1]];if(0===e){z=[b[e+d],b[e+d+1]];if(c-0===2*d&&db(y,z))break;if(m)w=[b[c-2*d],b[c-2*d+1]],p=z;else{l&&(f=Ok(a,[0,0],y,z,7*u*l,f),f=Ok(a,[0,0],y,z,7*-u*l,f),a.b[g++]=q+2,a.b[g++]=q,a.b[g++]=q+1,a.b[g++]=q+1,a.b[g++]=q+3,a.b[g++]=q+2);f=Ok(a,[0,0],y,z,3*u*(l||1),f);f=
+Ok(a,[0,0],y,z,3*-u*(l||1),f);r=f/7-1;continue}}else if(e===c-d){m?z=p:(w=w||[0,0],f=Ok(a,w,y,[0,0],5*u*(l||1),f),f=Ok(a,w,y,[0,0],5*-u*(l||1),f),a.b[g++]=q,a.b[g++]=r-1,a.b[g++]=r,a.b[g++]=r,a.b[g++]=q+1,a.b[g++]=q,l&&(f=Ok(a,w,y,[0,0],11*u*l,f),f=Ok(a,w,y,[0,0],11*-u*l,f),a.b[g++]=q+2,a.b[g++]=q,a.b[g++]=q+1,a.b[g++]=q+1,a.b[g++]=q+3,a.b[g++]=q+2));break}else z=[b[e+d],b[e+d+1]];n=Xj(w[0],w[1],y[0],y[1],z[0],z[1])?-1:1;f=Ok(a,w,y,z,13*n*(h||1),f);f=Ok(a,w,y,z,17*n*(h||1),f);f=Ok(a,w,y,z,19*-n*(h||
+1),f);0<e&&(a.b[g++]=q,a.b[g++]=r-1,a.b[g++]=r,a.b[g++]=q+2,a.b[g++]=q,a.b[g++]=0<u*n?r:r-1);a.b[g++]=q;a.b[g++]=q+2;a.b[g++]=q+1;r=q+2;u=n;h&&(f=Ok(a,w,y,z,23*n*h,f),a.b[g++]=q+1,a.b[g++]=q+3,a.b[g++]=q)}m&&(q=q||f/7,n=wd([w[0],w[1],y[0],y[1],z[0],z[1]],0,6,2)?1:-1,f=Ok(a,w,y,z,13*n*(h||1),f),Ok(a,w,y,z,19*-n*(h||1),f),a.b[g++]=q,a.b[g++]=r-1,a.b[g++]=r,a.b[g++]=q+1,a.b[g++]=q,a.b[g++]=0<u*n?r:r-1)}
+function Ok(a,b,c,d,e,f){a.a[f++]=b[0];a.a[f++]=b[1];a.a[f++]=c[0];a.a[f++]=c[1];a.a[f++]=d[0];a.a[f++]=d[1];a.a[f++]=e;return f}function Pk(a,b,c){b-=0;return b<2*c?!1:b===2*c?!db([a[0],a[1]],[a[0+c],a[c+1]]):!0}k=Mk.prototype;k.Pb=function(a,b){var c=a.ia(),d=a.pa();Pk(c,c.length,d)&&(c=Tc(c,c.length,d,-this.origin[0],-this.origin[1]),this.c.s&&(this.l.push(this.b.length),this.c.s=!1),this.g.push(this.b.length),this.i.push(b),Nk(this,c,c.length,d))};
+k.uc=function(a,b){var c=this.b.length,d=a.Yc(),e,f;e=0;for(f=d.length;e<f;++e){var g=d[e].ia(),h=d[e].pa();Pk(g,g.length,h)&&(g=Tc(g,g.length,h,-this.origin[0],-this.origin[1]),Nk(this,g,g.length,h))}this.b.length>c&&(this.g.push(c),this.i.push(b),this.c.s&&(this.l.push(c),this.c.s=!1))};
+function Qk(a,b,c,d){Gk(b,b.length,d)||(b.push(b[0]),b.push(b[1]));Nk(a,b,b.length,d);if(c.length){var e;b=0;for(e=c.length;b<e;++b)Gk(c[b],c[b].length,d)||(c[b].push(c[b][0]),c[b].push(c[b][1])),Nk(a,c[b],c[b].length,d)}}function Rk(a,b,c){c=void 0===c?a.b.length:c;a.g.push(c);a.i.push(b);a.c.s&&(a.l.push(c),a.c.s=!1)}k.vb=function(){this.v=new mk(this.a);this.o=new mk(this.b);this.g.push(this.b.length);0===this.l.length&&0<this.u.length&&(this.u=[]);this.b=this.a=null};
+k.wb=function(a){var b=this.v,c=this.o;return function(){pk(a,b);pk(a,c)}};k.Ke=function(a,b,c,d){var e=qk(b,Ik,Kk),f;this.H?f=this.H:this.H=f=new Lk(a,e);b.Lc(e);a.enableVertexAttribArray(f.l);a.vertexAttribPointer(f.l,2,5126,!1,28,0);a.enableVertexAttribArray(f.b);a.vertexAttribPointer(f.b,2,5126,!1,28,8);a.enableVertexAttribArray(f.o);a.vertexAttribPointer(f.o,2,5126,!1,28,16);a.enableVertexAttribArray(f.i);a.vertexAttribPointer(f.i,1,5126,!1,28,24);a.uniform2fv(f.va,c);a.uniform1f(f.ra,d);return f};
+k.Le=function(a,b){a.disableVertexAttribArray(b.l);a.disableVertexAttribArray(b.b);a.disableVertexAttribArray(b.o);a.disableVertexAttribArray(b.i)};
+k.yd=function(a,b,c,d){var e=a.getParameter(a.DEPTH_FUNC),f=a.getParameter(a.DEPTH_WRITEMASK);d||(a.enable(a.DEPTH_TEST),a.depthMask(!0),a.depthFunc(a.NOTEQUAL));if(wa(c)){var g,h,l;h=this.g[this.g.length-1];for(c=this.l.length-1;0<=c;--c)g=this.l[c],l=this.u[c],Sk(this,a,l[0],l[1],l[2]),lk(a,b,g,h),a.clear(a.DEPTH_BUFFER_BIT),h=g}else{var m,p,n,q;n=this.g.length-2;l=h=this.g[n+1];for(g=this.l.length-1;0<=g;--g){m=this.u[g];Sk(this,a,m[0],m[1],m[2]);for(m=this.l[g];0<=n&&this.g[n]>=m;)q=this.g[n],
+p=this.i[n],p=x(p).toString(),c[p]&&(h!==l&&(lk(a,b,h,l),a.clear(a.DEPTH_BUFFER_BIT)),l=q),n--,h=q;h!==l&&(lk(a,b,h,l),a.clear(a.DEPTH_BUFFER_BIT));h=l=m}}d||(a.disable(a.DEPTH_TEST),a.clear(a.DEPTH_BUFFER_BIT),a.depthMask(f),a.depthFunc(e))};
+k.$d=function(a,b,c,d,e){var f,g,h,l,m,p,n;n=this.g.length-2;h=this.g[n+1];for(f=this.l.length-1;0<=f;--f)for(g=this.u[f],Sk(this,a,g[0],g[1],g[2]),l=this.l[f];0<=n&&this.g[n]>=l;){g=this.g[n];m=this.i[n];p=x(m).toString();if(void 0===c[p]&&m.V()&&(void 0===e||jc(e,m.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),lk(a,b,g,h),h=d(m)))return h;n--;h=g}};function Sk(a,b,c,d,e){b.uniform4fv(a.H.D,c);b.uniform1f(a.H.L,d);b.uniform1f(a.H.P,e)}
+k.Ma=function(a,b){var c=b.c;this.c.lineCap=void 0!==c?c:"round";c=b.g;this.c.lineDash=c?c:Vj;c=b.i;this.c.lineJoin=void 0!==c?c:"round";c=b.b;c instanceof CanvasGradient||c instanceof CanvasPattern?c=Wj:c=ye(c).map(function(a,b){return 3!=b?a/255:a})||Wj;var d=b.f,d=void 0!==d?d:1,e=b.j,e=void 0!==e?e:10;this.c.strokeColor&&db(this.c.strokeColor,c)&&this.c.lineWidth===d&&this.c.miterLimit===e||(this.c.s=!0,this.c.strokeColor=c,this.c.lineWidth=d,this.c.miterLimit=e,this.u.push([c,d,e]))};function Tk(){this.b="precision mediump float;uniform vec4 e;uniform float f;void main(void){gl_FragColor=e;float alpha=e.a*f;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}v(Tk,ak);var Uk=new Tk;function Vk(){this.b="attribute vec2 a;uniform mat4 b;uniform mat4 c;uniform mat4 d;void main(void){gl_Position=b*vec4(a,0.0,1.0);}"}v(Vk,bk);var Wk=new Vk;
+function Xk(a,b){this.D=a.getUniformLocation(b,"e");this.f=a.getUniformLocation(b,"d");this.c=a.getUniformLocation(b,"c");this.a=a.getUniformLocation(b,"f");this.g=a.getUniformLocation(b,"b");this.b=a.getAttribLocation(b,"a")};function Yk(a){this.b=this.a=this.g=void 0;this.c=void 0===a?!0:a;this.f=0}function Zk(a){var b=a.b;if(b){var c=b.next,d=b.pb;c&&(c.pb=d);d&&(d.next=c);a.b=c||d;a.g===a.a?(a.b=void 0,a.g=void 0,a.a=void 0):a.g===b?a.g=a.b:a.a===b&&(a.a=d?a.b.pb:a.b);a.f--}}function $k(a){a.b=a.g;if(a.b)return a.b.data}function al(a){if(a.b&&a.b.next)return a.b=a.b.next,a.b.data}function bl(a){if(a.b&&a.b.next)return a.b.next.data}function cl(a){if(a.b&&a.b.pb)return a.b=a.b.pb,a.b.data}
+function dl(a){if(a.b&&a.b.pb)return a.b.pb.data}function el(a){if(a.b)return a.b.data}Yk.prototype.concat=function(a){if(a.b){if(this.b){var b=this.b.next;this.b.next=a.g;a.g.pb=this.b;b.pb=a.a;a.a.next=b;this.f+=a.f}else this.b=a.b,this.g=a.g,this.a=a.a,this.f=a.f;a.b=void 0;a.g=void 0;a.a=void 0;a.f=0}};var fl,gl,hl,il;
+(function(){var a={},b={ma:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ma=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Tp=c()}})(function(){return function d(a,b,g){function h(m,n){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!n&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ma:{}};a[m][0].call(q.ma,function(b){var d=
+a[m][1][b];return h(d?d:b)},q,q.ma,d,a,b,g)}return b[m].ma}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b){function f(a,b,d,e,q){d=d||0;e=e||a.length-1;for(q=q||h;e>d;){if(600<e-d){var r=e-d+1,u=b-d+1,w=Math.log(r),y=.5*Math.exp(2*w/3),w=.5*Math.sqrt(w*y*(r-y)/r)*(0>u-r/2?-1:1);f(a,b,Math.max(d,Math.floor(b-u*y/r+w)),Math.min(e,Math.floor(b+(r-u)*y/r+w)),q)}r=a[b];u=d;y=e;g(a,d,b);for(0<q(a[e],r)&&g(a,d,e);u<y;){g(a,u,y);u++;for(y--;0>q(a[u],r);)u++;
+for(;0<q(a[y],r);)y--}0===q(a[d],r)?g(a,d,y):(y++,g(a,y,e));y<=b&&(d=y+1);b<=y&&(e=y-1)}}function g(a,b,d){var e=a[b];a[b]=a[d];a[d]=e}function h(a,b){return a<b?-1:a>b?1:0}b.ma=f},{}],2:[function(a,b){function f(a,b){if(!(this instanceof f))return new f(a,b);this.af=Math.max(4,a||9);this.rg=Math.max(2,Math.ceil(.4*this.af));b&&this.vj(b);this.clear()}function g(a,b){h(a,0,a.children.length,b,a)}function h(a,b,d,e,f){f||(f=w(null));f.ea=Infinity;f.ga=Infinity;f.ca=-Infinity;f.ja=-Infinity;for(var g;b<
+d;b++)g=a.children[b],l(f,a.ab?e(g):g);return f}function l(a,b){a.ea=Math.min(a.ea,b.ea);a.ga=Math.min(a.ga,b.ga);a.ca=Math.max(a.ca,b.ca);a.ja=Math.max(a.ja,b.ja)}function m(a,b){return a.ea-b.ea}function p(a,b){return a.ga-b.ga}function n(a){return(a.ca-a.ea)*(a.ja-a.ga)}function q(a){return a.ca-a.ea+(a.ja-a.ga)}function r(a,b){return a.ea<=b.ea&&a.ga<=b.ga&&b.ca<=a.ca&&b.ja<=a.ja}function u(a,b){return b.ea<=a.ca&&b.ga<=a.ja&&b.ca>=a.ea&&b.ja>=a.ga}function w(a){return{children:a,height:1,ab:!0,
+ea:Infinity,ga:Infinity,ca:-Infinity,ja:-Infinity}}function y(a,b,d,e,f){for(var g=[b,d],h;g.length;)d=g.pop(),b=g.pop(),d-b<=e||(h=b+Math.ceil((d-b)/e/2)*e,z(a,h,b,d,f),g.push(b,h,h,d))}b.ma=f;var z=a("quickselect");f.prototype={all:function(){return this.mg(this.data,[])},search:function(a){var b=this.data,d=[],e=this.tb;if(!u(a,b))return d;for(var f=[],g,h,l,m;b;){g=0;for(h=b.children.length;g<h;g++)l=b.children[g],m=b.ab?e(l):l,u(a,m)&&(b.ab?d.push(l):r(a,m)?this.mg(l,d):f.push(l));b=f.pop()}return d},
+load:function(a){if(!a||!a.length)return this;if(a.length<this.rg){for(var b=0,d=a.length;b<d;b++)this.Da(a[b]);return this}a=this.og(a.slice(),0,a.length-1,0);this.data.children.length?this.data.height===a.height?this.tg(this.data,a):(this.data.height<a.height&&(b=this.data,this.data=a,a=b),this.qg(a,this.data.height-a.height-1,!0)):this.data=a;return this},Da:function(a){a&&this.qg(a,this.data.height-1);return this},clear:function(){this.data=w([]);return this},remove:function(a,b){if(!a)return this;
+for(var d=this.data,e=this.tb(a),f=[],g=[],h,l,m,p;d||f.length;){d||(d=f.pop(),l=f[f.length-1],h=g.pop(),p=!0);if(d.ab){a:{m=a;var n=d.children,q=b;if(q){for(var u=0;u<n.length;u++)if(q(m,n[u])){m=u;break a}m=-1}else m=n.indexOf(m)}if(-1!==m){d.children.splice(m,1);f.push(d);this.tj(f);break}}p||d.ab||!r(d,e)?l?(h++,d=l.children[h],p=!1):d=null:(f.push(d),g.push(h),h=0,l=d,d=d.children[0])}return this},tb:function(a){return a},ff:m,gf:p,toJSON:function(){return this.data},mg:function(a,b){for(var d=
+[];a;)a.ab?b.push.apply(b,a.children):d.push.apply(d,a.children),a=d.pop();return b},og:function(a,b,d,e){var f=d-b+1,h=this.af,l;if(f<=h)return l=w(a.slice(b,d+1)),g(l,this.tb),l;e||(e=Math.ceil(Math.log(f)/Math.log(h)),h=Math.ceil(f/Math.pow(h,e-1)));l=w([]);l.ab=!1;l.height=e;var f=Math.ceil(f/h),h=f*Math.ceil(Math.sqrt(h)),m,p,n;for(y(a,b,d,h,this.ff);b<=d;b+=h)for(p=Math.min(b+h-1,d),y(a,b,p,f,this.gf),m=b;m<=p;m+=f)n=Math.min(m+f-1,p),l.children.push(this.og(a,m,n,e-1));g(l,this.tb);return l},
+sj:function(a,b,d,e){for(var f,g,h,l,m,p,q,r;;){e.push(b);if(b.ab||e.length-1===d)break;q=r=Infinity;f=0;for(g=b.children.length;f<g;f++)h=b.children[f],m=n(h),p=(Math.max(h.ca,a.ca)-Math.min(h.ea,a.ea))*(Math.max(h.ja,a.ja)-Math.min(h.ga,a.ga))-m,p<r?(r=p,q=m<q?m:q,l=h):p===r&&m<q&&(q=m,l=h);b=l||b.children[0]}return b},qg:function(a,b,d){var e=this.tb;d=d?a:e(a);var e=[],f=this.sj(d,this.data,b,e);f.children.push(a);for(l(f,d);0<=b;)if(e[b].children.length>this.af)this.Aj(e,b),b--;else break;this.pj(d,
+e,b)},Aj:function(a,b){var d=a[b],e=d.children.length,f=this.rg;this.qj(d,f,e);e=this.rj(d,f,e);e=w(d.children.splice(e,d.children.length-e));e.height=d.height;e.ab=d.ab;g(d,this.tb);g(e,this.tb);b?a[b-1].children.push(e):this.tg(d,e)},tg:function(a,b){this.data=w([a,b]);this.data.height=a.height+1;this.data.ab=!1;g(this.data,this.tb)},rj:function(a,b,d){var e,f,g,l,m,p,q;m=p=Infinity;for(e=b;e<=d-b;e++)f=h(a,0,e,this.tb),g=h(a,e,d,this.tb),l=Math.max(0,Math.min(f.ca,g.ca)-Math.max(f.ea,g.ea))*Math.max(0,
+Math.min(f.ja,g.ja)-Math.max(f.ga,g.ga)),f=n(f)+n(g),l<m?(m=l,q=e,p=f<p?f:p):l===m&&f<p&&(p=f,q=e);return q},qj:function(a,b,d){var e=a.ab?this.ff:m,f=a.ab?this.gf:p,g=this.ng(a,b,d,e);b=this.ng(a,b,d,f);g<b&&a.children.sort(e)},ng:function(a,b,d,e){a.children.sort(e);e=this.tb;var f=h(a,0,b,e),g=h(a,d-b,d,e),m=q(f)+q(g),p,n;for(p=b;p<d-b;p++)n=a.children[p],l(f,a.ab?e(n):n),m+=q(f);for(p=d-b-1;p>=b;p--)n=a.children[p],l(g,a.ab?e(n):n),m+=q(g);return m},pj:function(a,b,d){for(;0<=d;d--)l(b[d],a)},
+tj:function(a){for(var b=a.length-1,d;0<=b;b--)0===a[b].children.length?0<b?(d=a[b-1].children,d.splice(d.indexOf(a[b]),1)):this.clear():g(a[b],this.tb)},vj:function(a){var b=["return a"," - b",";"];this.ff=new Function("a","b",b.join(a[0]));this.gf=new Function("a","b",b.join(a[1]));this.tb=new Function("a","return {minX: a"+a[0]+", minY: a"+a[1]+", maxX: a"+a[2]+", maxY: a"+a[3]+"};")}}},{quickselect:1}]},{},[2])(2)});fl=b.ma})();function jl(a){this.b=fl(a);this.a={}}k=jl.prototype;k.Da=function(a,b){var c={ea:a[0],ga:a[1],ca:a[2],ja:a[3],value:b};this.b.Da(c);this.a[x(b)]=c};k.load=function(a,b){for(var c=Array(b.length),d=0,e=b.length;d<e;d++){var f=a[d],g=b[d],f={ea:f[0],ga:f[1],ca:f[2],ja:f[3],value:g};c[d]=f;this.a[x(g)]=f}this.b.load(c)};k.remove=function(a){a=x(a);var b=this.a[a];delete this.a[a];return null!==this.b.remove(b)};
+function kl(a,b,c){var d=a.a[x(c)];Vb([d.ea,d.ga,d.ca,d.ja],b)||(a.remove(c),a.Da(b,c))}function ll(a){return a.b.all().map(function(a){return a.value})}function ml(a,b){return a.b.search({ea:b[0],ga:b[1],ca:b[2],ja:b[3]}).map(function(a){return a.value})}k.forEach=function(a,b){return nl(ll(this),a,b)};function pl(a,b,c,d){return nl(ml(a,b),c,d)}function nl(a,b,c){for(var d,e=0,f=a.length;e<f&&!(d=b.call(c,a[e]));e++);return d}k.clear=function(){this.b.clear();this.a={}};
+k.G=function(){var a=this.b.data;return[a.ea,a.ga,a.ca,a.ja]};function ql(a,b){jk.call(this,0,b);this.j=new Mk(0,b);this.H=null;this.u=[];this.c=[];this.l={fillColor:null,s:!1}}v(ql,jk);function rl(a,b,c,d){var e=new Yk,f=new jl;b=sl(a,b,d,e,f,!0);if(c.length){var g,h,l=[];g=0;for(h=c.length;g<h;++g){var m={list:new Yk,ca:void 0};l.push(m);m.ca=sl(a,c[g],d,m.list,f,!1)}l.sort(function(a,b){return b.ca-a.ca});for(g=0;g<l.length;++g)tl(l[g].list,l[g].ca,e,b,f)}ul(e,f,!1);vl(a,e,f)}
+function sl(a,b,c,d,e,f){var g,h,l=a.a.length/2,m,p,n,q=[],r=[];if(f===wd(b,0,b.length,c))for(p=m=wl(a,b[0],b[1],l++),f=b[0],g=c,h=b.length;g<h;g+=c)n=wl(a,b[g],b[g+1],l++),r.push(xl(p,n,d)),q.push([Math.min(p.x,n.x),Math.min(p.y,n.y),Math.max(p.x,n.x),Math.max(p.y,n.y)]),f=b[g]>f?b[g]:f,p=n;else for(g=b.length-c,p=m=wl(a,b[g],b[g+1],l++),f=b[g],g-=c,h=0;g>=h;g-=c)n=wl(a,b[g],b[g+1],l++),r.push(xl(p,n,d)),q.push([Math.min(p.x,n.x),Math.min(p.y,n.y),Math.max(p.x,n.x),Math.max(p.y,n.y)]),f=b[g]>f?b[g]:
+f,p=n;r.push(xl(n,m,d));q.push([Math.min(p.x,n.x),Math.min(p.y,n.y),Math.max(p.x,n.x),Math.max(p.y,n.y)]);e.load(q,r);return f}function ul(a,b,c){var d=$k(a),e=d,f=al(a),g=!1;do{var h=c?Xj(f.X.x,f.X.y,e.X.x,e.X.y,e.ba.x,e.ba.y):Xj(e.ba.x,e.ba.y,e.X.x,e.X.y,f.X.x,f.X.y);void 0===h?(yl(e,f,a,b),g=!0,f===d&&(d=bl(a)),f=e,cl(a)):e.X.qb!==h&&(e.X.qb=h,g=!0);e=f;f=al(a)}while(e!==d);return g}
+function tl(a,b,c,d,e){ul(a,e,!0);for(var f=$k(a);f.X.x!==b;)f=al(a);b=f.X;d={x:d,y:b.y,$a:-1};var g=Infinity,h,l,m,p;m=zl({ba:b,X:d},e,!0);h=0;for(l=m.length;h<l;++h){var n=m[h];if(void 0===n.ba.qb){var q=Al(b,d,n.ba,n.X,!0),r=Math.abs(b.x-q[0]);r<g&&(g=r,p={x:q[0],y:q[1],$a:-1},f=n)}}if(Infinity!==g){m=f.X;if(0<g&&(f=Bl(b,p,f.X,e),f.length))for(p=Infinity,h=0,l=f.length;h<l;++h)if(g=f[h],n=Math.atan2(b.y-g.y,d.x-g.x),n<p||n===p&&g.x<m.x)p=n,m=g;for(f=$k(c);f.X!==m;)f=al(c);d={x:b.x,y:b.y,$a:b.$a,
+qb:void 0};h={x:f.X.x,y:f.X.y,$a:f.X.$a,qb:void 0};bl(a).ba=d;xl(b,f.X,a,e);xl(h,d,a,e);f.X=h;a.c&&a.b&&(a.g=a.b,a.a=a.b.pb);c.concat(a)}}
+function vl(a,b,c){for(var d=!1,e=Cl(b,c);3<b.f;)if(e){if(!Dl(a,b,c,e,d)&&!ul(b,c,d)&&!El(a,b,c,!0))break}else if(!Dl(a,b,c,e,d)&&!ul(b,c,d)&&!El(a,b,c))if(e=Cl(b,c)){var d=b,f=2*d.f,g=Array(f),h=$k(d),l=h,m=0;do g[m++]=l.ba.x,g[m++]=l.ba.y,l=al(d);while(l!==h);d=!wd(g,0,f,2);ul(b,c,d)}else{e=a;d=b;f=g=$k(d);do{h=zl(f,c);if(h.length){g=h[0];h=Al(f.ba,f.X,g.ba,g.X);h=wl(e,h[0],h[1],e.a.length/2);l=new Yk;m=new jl;xl(h,f.X,l,m);f.X=h;kl(c,[Math.min(f.ba.x,h.x),Math.min(f.ba.y,h.y),Math.max(f.ba.x,h.x),
+Math.max(f.ba.y,h.y)],f);for(f=al(d);f!==g;)xl(f.ba,f.X,l,m),c.remove(f),Zk(d),f=el(d);xl(g.ba,h,l,m);g.ba=h;kl(c,[Math.min(g.X.x,h.x),Math.min(g.X.y,h.y),Math.max(g.X.x,h.x),Math.max(g.X.y,h.y)],g);ul(d,c,!1);vl(e,d,c);ul(l,m,!1);vl(e,l,m);break}f=al(d)}while(f!==g);break}3===b.f&&(e=a.b.length,a.b[e++]=dl(b).ba.$a,a.b[e++]=el(b).ba.$a,a.b[e++]=bl(b).ba.$a)}
+function Dl(a,b,c,d,e){var f=a.b.length,g=$k(b),h=dl(b),l=g,m=al(b),p=bl(b),n,q,r,u=!1;do{n=l.ba;q=l.X;r=m.X;if(!1===q.qb){var w=e?Fl(p.X,r,q,n,h.ba):Fl(h.ba,n,q,r,p.X);!d&&0!==zl({ba:n,X:r},c).length||!w||0!==Bl(n,q,r,c,!0).length||!d&&!1!==n.qb&&!1!==r.qb&&wd([h.ba.x,h.ba.y,n.x,n.y,q.x,q.y,r.x,r.y,p.X.x,p.X.y],0,10,2)!==!e||(a.b[f++]=n.$a,a.b[f++]=q.$a,a.b[f++]=r.$a,yl(l,m,b,c),m===g&&(g=p),u=!0)}h=dl(b);l=el(b);m=al(b);p=bl(b)}while(l!==g&&3<b.f);return u}
+function El(a,b,c,d){var e=$k(b);al(b);var f=e,g=al(b),h=!1;do{var l=Al(f.ba,f.X,g.ba,g.X,d);if(l){var m,h=a.b.length,p=a.a.length/2,n=cl(b);Zk(b);c.remove(n);m=n===e;d?(l[0]===f.ba.x&&l[1]===f.ba.y?(cl(b),l=f.ba,g.ba=l,c.remove(f),m=m||f===e):(l=g.X,f.X=l,c.remove(g),m=m||g===e),Zk(b)):(l=wl(a,l[0],l[1],p),f.X=l,g.ba=l,kl(c,[Math.min(f.ba.x,f.X.x),Math.min(f.ba.y,f.X.y),Math.max(f.ba.x,f.X.x),Math.max(f.ba.y,f.X.y)],f),kl(c,[Math.min(g.ba.x,g.X.x),Math.min(g.ba.y,g.X.y),Math.max(g.ba.x,g.X.x),Math.max(g.ba.y,
+g.X.y)],g));a.b[h++]=n.ba.$a;a.b[h++]=n.X.$a;a.b[h++]=l.$a;h=!0;if(m)break}f=dl(b);g=al(b)}while(f!==e);return h}function Cl(a,b){var c=$k(a),d=c;do{if(zl(d,b).length)return!1;d=al(a)}while(d!==c);return!0}function wl(a,b,c,d){var e=a.a.length;a.a[e++]=b;a.a[e++]=c;return{x:b,y:c,$a:d,qb:void 0}}
+function xl(a,b,c,d){var e={ba:a,X:b},f={pb:void 0,next:void 0,data:e},g=c.b;if(g){var h=g.next;f.pb=g;f.next=h;g.next=f;h&&(h.pb=f);g===c.a&&(c.a=f)}else c.g=f,c.a=f,c.c&&(f.next=f,f.pb=f);c.b=f;c.f++;d&&d.Da([Math.min(a.x,b.x),Math.min(a.y,b.y),Math.max(a.x,b.x),Math.max(a.y,b.y)],e);return e}function yl(a,b,c,d){el(c)===b&&(Zk(c),a.X=b.X,d.remove(b),kl(d,[Math.min(a.ba.x,a.X.x),Math.min(a.ba.y,a.X.y),Math.max(a.ba.x,a.X.x),Math.max(a.ba.y,a.X.y)],a))}
+function Bl(a,b,c,d,e){var f,g,h,l=[],m=ml(d,[Math.min(a.x,b.x,c.x),Math.min(a.y,b.y,c.y),Math.max(a.x,b.x,c.x),Math.max(a.y,b.y,c.y)]);d=0;for(f=m.length;d<f;++d)for(g in m[d])h=m[d][g],"object"!==typeof h||e&&!h.qb||h.x===a.x&&h.y===a.y||h.x===b.x&&h.y===b.y||h.x===c.x&&h.y===c.y||-1!==l.indexOf(h)||!qd([a.x,a.y,b.x,b.y,c.x,c.y],0,6,2,h.x,h.y)||l.push(h);return l}
+function zl(a,b,c){var d=a.ba,e=a.X;b=ml(b,[Math.min(d.x,e.x),Math.min(d.y,e.y),Math.max(d.x,e.x),Math.max(d.y,e.y)]);var f=[],g,h;g=0;for(h=b.length;g<h;++g){var l=b[g];a!==l&&(c||l.ba!==e||l.X!==d)&&Al(d,e,l.ba,l.X,c)&&f.push(l)}return f}
+function Al(a,b,c,d,e){var f=(d.y-c.y)*(b.x-a.x)-(d.x-c.x)*(b.y-a.y);if(0!==f&&(d=((d.x-c.x)*(a.y-c.y)-(d.y-c.y)*(a.x-c.x))/f,c=((b.x-a.x)*(a.y-c.y)-(b.y-a.y)*(a.x-c.x))/f,!e&&d>Yj&&d<1-Yj&&c>Yj&&c<1-Yj||e&&0<=d&&1>=d&&0<=c&&1>=c))return[a.x+d*(b.x-a.x),a.y+d*(b.y-a.y)]}
+function Fl(a,b,c,d,e){if(void 0===b.qb||void 0===d.qb)return!1;var f=(c.x-d.x)*(b.y-d.y)>(c.y-d.y)*(b.x-d.x);e=(e.x-d.x)*(b.y-d.y)<(e.y-d.y)*(b.x-d.x);a=(a.x-b.x)*(d.y-b.y)>(a.y-b.y)*(d.x-b.x);c=(c.x-b.x)*(d.y-b.y)<(c.y-b.y)*(d.x-b.x);b=b.qb?c||a:c&&a;return(d.qb?e||f:e&&f)&&b}k=ql.prototype;
+k.wc=function(a,b){var c=a.Ad(),d=a.pa(),e=this.b.length,f=this.j.b.length,g,h,l,m;g=0;for(h=c.length;g<h;++g){var p=c[g].Zc();if(0<p.length){var n=p[0].ia(),n=Tc(n,n.length,d,-this.origin[0],-this.origin[1]),q=[],r;l=1;for(m=p.length;l<m;++l)r=p[l].ia(),r=Tc(r,r.length,d,-this.origin[0],-this.origin[1]),q.push(r);Qk(this.j,n,q,d);rl(this,n,q,d)}}this.b.length>e&&(this.g.push(e),this.i.push(b),this.l.s&&(this.c.push(e),this.l.s=!1));this.j.b.length>f&&Rk(this.j,b,f)};
+k.yc=function(a,b){var c=a.Zc(),d=a.pa();if(0<c.length){this.g.push(this.b.length);this.i.push(b);this.l.s&&(this.c.push(this.b.length),this.l.s=!1);Rk(this.j,b);var e=c[0].ia(),e=Tc(e,e.length,d,-this.origin[0],-this.origin[1]),f=[],g,h,l;g=1;for(h=c.length;g<h;++g)l=c[g].ia(),l=Tc(l,l.length,d,-this.origin[0],-this.origin[1]),f.push(l);Qk(this.j,e,f,d);rl(this,e,f,d)}};
+k.vb=function(a){this.v=new mk(this.a);this.o=new mk(this.b);this.g.push(this.b.length);this.j.vb(a);0===this.c.length&&0<this.u.length&&(this.u=[]);this.b=this.a=null};k.wb=function(a){var b=this.v,c=this.o,d=this.j.wb(a);return function(){pk(a,b);pk(a,c);d()}};k.Ke=function(a,b){var c=qk(b,Uk,Wk),d;this.H?d=this.H:this.H=d=new Xk(a,c);b.Lc(c);a.enableVertexAttribArray(d.b);a.vertexAttribPointer(d.b,2,5126,!1,8,0);return d};k.Le=function(a,b){a.disableVertexAttribArray(b.b)};
+k.yd=function(a,b,c,d){var e=a.getParameter(a.DEPTH_FUNC),f=a.getParameter(a.DEPTH_WRITEMASK);d||(a.enable(a.DEPTH_TEST),a.depthMask(!0),a.depthFunc(a.NOTEQUAL));if(wa(c)){var g,h,l;h=this.g[this.g.length-1];for(c=this.c.length-1;0<=c;--c)g=this.c[c],l=this.u[c],a.uniform4fv(this.H.D,l),lk(a,b,g,h),h=g}else{var m,p,n,q;n=this.g.length-2;l=h=this.g[n+1];for(g=this.c.length-1;0<=g;--g){m=this.u[g];a.uniform4fv(this.H.D,m);for(m=this.c[g];0<=n&&this.g[n]>=m;)q=this.g[n],p=this.i[n],p=x(p).toString(),
+c[p]&&(h!==l&&(lk(a,b,h,l),a.clear(a.DEPTH_BUFFER_BIT)),l=q),n--,h=q;h!==l&&(lk(a,b,h,l),a.clear(a.DEPTH_BUFFER_BIT));h=l=m}}d||(a.disable(a.DEPTH_TEST),a.clear(a.DEPTH_BUFFER_BIT),a.depthMask(f),a.depthFunc(e))};
+k.$d=function(a,b,c,d,e){var f,g,h,l,m,p,n;n=this.g.length-2;h=this.g[n+1];for(f=this.c.length-1;0<=f;--f)for(g=this.u[f],a.uniform4fv(this.H.D,g),l=this.c[f];0<=n&&this.g[n]>=l;){g=this.g[n];m=this.i[n];p=x(m).toString();if(void 0===c[p]&&m.V()&&(void 0===e||jc(e,m.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),lk(a,b,g,h),h=d(m)))return h;n--;h=g}};
+k.Ma=function(a,b){var c=a?a.b:[0,0,0,0];c instanceof CanvasGradient||c instanceof CanvasPattern?c=Uj:c=ye(c).map(function(a,b){return 3!=b?a/255:a})||Uj;this.l.fillColor&&db(c,this.l.fillColor)||(this.l.fillColor=c,this.l.s=!0,this.u.push(c));b?this.j.Ma(null,b):this.j.Ma(null,new wi({color:[0,0,0,0],lineWidth:0}))};function Gl(){}Gl.prototype.f=function(){};function Hl(a,b,c){this.i=b;this.j=a;this.c=c;this.a={}}v(Hl,jj);function Il(a,b){var c=[],d;for(d in a.a){var e=a.a[d],f;for(f in e)c.push(e[f].wb(b))}return function(){for(var a=c.length,b,d=0;d<a;d++)b=c[d].apply(this,arguments);return b}}function Jl(a,b){for(var c in a.a){var d=a.a[c],e;for(e in d)d[e].vb(b)}}Hl.prototype.b=function(a,b){var c=void 0!==a?a.toString():"0",d=this.a[c];void 0===d&&(d={},this.a[c]=d);c=d[b];void 0===c&&(c=new Kl[b](this.j,this.i),d[b]=c);return c};
+Hl.prototype.g=function(){return wa(this.a)};Hl.prototype.f=function(a,b,c,d,e,f,g,h){var l=Object.keys(this.a).map(Number);l.sort(Ya);var m,p,n,q,r,u;m=0;for(p=l.length;m<p;++m)for(r=this.a[l[m].toString()],n=0,q=Aj.length;n<q;++n)u=r[Aj[n]],void 0!==u&&u.f(a,b,c,d,e,f,g,h,void 0,!1)};
+function Ll(a,b,c,d,e,f,g,h,l,m,p){var n=Ml,q=Object.keys(a.a).map(Number);q.sort(function(a,b){return b-a});var r,u,w,y,z;r=0;for(u=q.length;r<u;++r)for(y=a.a[q[r].toString()],w=Aj.length-1;0<=w;--w)if(z=y[Aj[w]],void 0!==z&&(z=z.f(b,c,d,e,n,f,g,h,l,m,p)))return z}
+Hl.prototype.Ba=function(a,b,c,d,e,f,g,h,l,m){var p=b.b;p.bindFramebuffer(p.FRAMEBUFFER,yk(b));var n;void 0!==this.c&&(n=Jb(Sb(a),d*this.c));return Ll(this,b,a,d,e,g,h,l,function(a){var b=new Uint8Array(4);p.readPixels(0,0,1,1,p.RGBA,p.UNSIGNED_BYTE,b);if(0<b[3]&&(a=m(a)))return a},!0,n)};
+function Nl(a,b,c,d,e,f,g,h){var l=c.b;l.bindFramebuffer(l.FRAMEBUFFER,yk(c));return void 0!==Ll(a,c,b,d,e,f,g,h,function(){var a=new Uint8Array(4);l.readPixels(0,0,1,1,l.RGBA,l.UNSIGNED_BYTE,a);return 0<a[3]},!1)}var Ml=[1,1],Kl={Circle:ok,Image:Dk,LineString:Mk,Polygon:ql,Text:Gl};function Ol(a,b,c,d,e,f,g){this.b=a;this.g=b;this.a=f;this.f=g;this.j=e;this.i=d;this.c=c;this.l=this.o=this.v=null}v(Ol,Ii);k=Ol.prototype;k.Gd=function(a){this.Ma(a.f,a.g);this.dc(a.a)};
+k.tc=function(a){switch(a.Y()){case "Point":this.xc(a,null);break;case "LineString":this.Pb(a,null);break;case "Polygon":this.yc(a,null);break;case "MultiPoint":this.vc(a,null);break;case "MultiLineString":this.uc(a,null);break;case "MultiPolygon":this.wc(a,null);break;case "GeometryCollection":this.kf(a,null);break;case "Circle":this.hc(a,null)}};k.jf=function(a,b){var c=(0,b.c)(a);c&&jc(this.a,c.G())&&(this.Gd(b),this.tc(c))};k.kf=function(a){a=a.f;var b,c;b=0;for(c=a.length;b<c;++b)this.tc(a[b])};
+k.xc=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"Image");d.dc(this.v);d.xc(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};k.vc=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"Image");d.dc(this.v);d.vc(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};
+k.Pb=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"LineString");d.Ma(null,this.l);d.Pb(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};k.uc=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"LineString");d.Ma(null,this.l);d.uc(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};
+k.yc=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"Polygon");d.Ma(this.o,this.l);d.yc(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};k.wc=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"Polygon");d.Ma(this.o,this.l);d.wc(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};
+k.hc=function(a,b){var c=this.b,d=(new Hl(1,this.a)).b(0,"Circle");d.Ma(this.o,this.l);d.hc(a,b);d.vb(c);d.f(this.b,this.g,this.c,this.i,this.j,this.f,1,{},void 0,!1);d.wb(c)()};k.dc=function(a){this.v=a};k.Ma=function(a,b){this.o=a;this.l=b};function Pl(){this.b="precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}"}v(Pl,ak);var Ql=new Pl;function Rl(){this.b="varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}"}v(Rl,bk);var Sl=new Rl;
+function Tl(a,b){this.g=a.getUniformLocation(b,"f");this.f=a.getUniformLocation(b,"e");this.i=a.getUniformLocation(b,"d");this.c=a.getUniformLocation(b,"g");this.b=a.getAttribLocation(b,"b");this.a=a.getAttribLocation(b,"c")};function Ul(a,b){Ui.call(this,b);this.f=a;this.U=new mk([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]);this.i=this.yb=null;this.j=void 0;this.v=Ph();this.u=Ph();this.C=hk();this.H=null}v(Ul,Ui);
+function Vl(a,b,c){var d=a.f.g;if(void 0===a.j||a.j!=c){b.postRenderFunctions.push(function(a,b,c){a.isContextLost()||(a.deleteFramebuffer(b),a.deleteTexture(c))}.bind(null,d,a.i,a.yb));b=zk(d,c,c);var e=d.createFramebuffer();d.bindFramebuffer(36160,e);d.framebufferTexture2D(36160,36064,3553,b,0);a.yb=b;a.i=e;a.j=c}else d.bindFramebuffer(36160,a.i)}
+Ul.prototype.Dh=function(a,b,c){Wl(this,"precompose",c,a);kk(c,34962,this.U);var d=c.b,e=qk(c,Ql,Sl),f;this.H?f=this.H:this.H=f=new Tl(d,e);c.Lc(e)&&(d.enableVertexAttribArray(f.b),d.vertexAttribPointer(f.b,2,5126,!1,16,0),d.enableVertexAttribArray(f.a),d.vertexAttribPointer(f.a,2,5126,!1,16,8),d.uniform1i(f.c,0));d.uniformMatrix4fv(f.i,!1,ik(this.C,this.v));d.uniformMatrix4fv(f.f,!1,ik(this.C,this.u));d.uniform1f(f.g,b.opacity);d.bindTexture(3553,this.yb);d.drawArrays(5,0,4);Wl(this,"postcompose",
+c,a)};function Wl(a,b,c,d){a=a.a;if(Oa(a,b)){var e=d.viewState;a.b(new Jh(b,new Ol(c,e.center,e.resolution,e.rotation,d.size,d.extent,d.pixelRatio),d,null,c))}}Ul.prototype.If=function(){this.i=this.yb=null;this.j=void 0};function Xl(a,b,c,d,e,f){this.c=void 0!==f?f:null;hi.call(this,a,b,c,void 0!==f?ji:li,d);this.g=e}v(Xl,hi);Xl.prototype.i=function(a){this.state=a?ki:li;this.s()};Xl.prototype.load=function(){this.state==ji&&(this.state=mi,this.s(),this.c(this.i.bind(this)))};Xl.prototype.a=function(){return this.g};var Yl,Zl=-1<navigator.userAgent.indexOf("OPR"),$l=-1<navigator.userAgent.indexOf("Edge");Yl=!(!navigator.userAgent.match("CriOS")&&"chrome"in window&&"Google Inc."===navigator.vendor&&0==Zl&&0==$l);function am(a,b,c,d){var e=Pc(c,b,a);c=xc(b,d,c);b=b.ic();void 0!==b&&(c*=b);b=a.ic();void 0!==b&&(c/=b);a=xc(a,c,e)/c;isFinite(a)&&0<a&&(c/=a);return c}function bm(a,b,c,d){a=c-a;b=d-b;var e=Math.sqrt(a*a+b*b);return[Math.round(c+a/e),Math.round(d+b/e)]}
+function cm(a,b,c,d,e,f,g,h,l,m,p){var n=De(Math.round(c*a),Math.round(c*b));if(0===l.length)return n.canvas;n.scale(c,c);var q=Hb();l.forEach(function(a){Wb(q,a.extent)});var r=De(Math.round(c*dc(q)/d),Math.round(c*ec(q)/d)),u=c/d;l.forEach(function(a){r.drawImage(a.image,m,m,a.image.width-2*m,a.image.height-2*m,(a.extent[0]-q[0])*u,-(a.extent[3]-q[3])*u,dc(a.extent)*u,ec(a.extent)*u)});var w=ac(g);h.f.forEach(function(a){var b=a.source,e=a.target,g=b[1][0],h=b[1][1],l=b[2][0],m=b[2][1];a=(e[0][0]-
+w[0])/f;var p=-(e[0][1]-w[1])/f,u=(e[1][0]-w[0])/f,kb=-(e[1][1]-w[1])/f,W=(e[2][0]-w[0])/f,Ra=-(e[2][1]-w[1])/f,e=b[0][0],b=b[0][1],g=g-e,h=h-b,l=l-e,m=m-b;a:{g=[[g,h,0,0,u-a],[l,m,0,0,W-a],[0,0,g,h,kb-p],[0,0,l,m,Ra-p]];h=g.length;for(l=0;l<h;l++){for(var m=l,Pb=Math.abs(g[l][l]),fc=l+1;fc<h;fc++){var Wc=Math.abs(g[fc][l]);Wc>Pb&&(Pb=Wc,m=fc)}if(0===Pb){g=null;break a}Pb=g[m];g[m]=g[l];g[l]=Pb;for(m=l+1;m<h;m++)for(Pb=-g[m][l]/g[l][l],fc=l;fc<h+1;fc++)g[m][fc]=l==fc?0:g[m][fc]+Pb*g[l][fc]}l=Array(h);
+for(m=h-1;0<=m;m--)for(l[m]=g[m][h]/g[m][m],Pb=m-1;0<=Pb;Pb--)g[Pb][h]-=g[Pb][m]*l[m];g=l}g&&(n.save(),n.beginPath(),Yl?(l=(a+u+W)/3,m=(p+kb+Ra)/3,h=bm(l,m,a,p),u=bm(l,m,u,kb),W=bm(l,m,W,Ra),n.moveTo(u[0],u[1]),n.lineTo(h[0],h[1]),n.lineTo(W[0],W[1])):(n.moveTo(u,kb),n.lineTo(a,p),n.lineTo(W,Ra)),n.clip(),n.transform(g[0],g[2],g[1],g[3],a,p),n.translate(q[0]-e,q[3]-b),n.scale(d/c,-d/c),n.drawImage(r.canvas,0,0),n.restore())});p&&(n.save(),n.strokeStyle="black",n.lineWidth=1,h.f.forEach(function(a){var b=
+a.target;a=(b[0][0]-w[0])/f;var c=-(b[0][1]-w[1])/f,d=(b[1][0]-w[0])/f,e=-(b[1][1]-w[1])/f,g=(b[2][0]-w[0])/f,b=-(b[2][1]-w[1])/f;n.beginPath();n.moveTo(d,e);n.lineTo(a,c);n.lineTo(g,b);n.closePath();n.stroke()}),n.restore());return n.canvas};function dm(a,b,c,d,e){this.g=a;this.c=b;var f={},g=Nc(this.c,this.g);this.a=function(a){var b=a[0]+"/"+a[1];f[b]||(f[b]=g(a));return f[b]};this.i=d;this.v=e*e;this.f=[];this.l=!1;this.o=this.g.a&&!!d&&!!this.g.G()&&dc(d)==dc(this.g.G());this.b=this.g.G()?dc(this.g.G()):null;this.j=this.c.G()?dc(this.c.G()):null;a=ac(c);b=$b(c);d=Zb(c);c=Yb(c);e=this.a(a);var h=this.a(b),l=this.a(d),m=this.a(c);em(this,a,b,d,c,e,h,l,m,10);if(this.l){var p=Infinity;this.f.forEach(function(a){p=Math.min(p,a.source[0][0],
+a.source[1][0],a.source[2][0])});this.f.forEach(function(a){if(Math.max(a.source[0][0],a.source[1][0],a.source[2][0])-p>this.b/2){var b=[[a.source[0][0],a.source[0][1]],[a.source[1][0],a.source[1][1]],[a.source[2][0],a.source[2][1]]];b[0][0]-p>this.b/2&&(b[0][0]-=this.b);b[1][0]-p>this.b/2&&(b[1][0]-=this.b);b[2][0]-p>this.b/2&&(b[2][0]-=this.b);Math.max(b[0][0],b[1][0],b[2][0])-Math.min(b[0][0],b[1][0],b[2][0])<this.b/2&&(a.source=b)}},this)}f={}}
+function em(a,b,c,d,e,f,g,h,l,m){var p=Gb([f,g,h,l]),n=a.b?dc(p)/a.b:null,q=a.b,r=a.g.a&&.5<n&&1>n,u=!1;if(0<m){if(a.c.g&&a.j)var w=Gb([b,c,d,e]),u=u|.25<dc(w)/a.j;!r&&a.g.g&&n&&(u|=.25<n)}if(u||!a.i||jc(p,a.i)){if(!(u||isFinite(f[0])&&isFinite(f[1])&&isFinite(g[0])&&isFinite(g[1])&&isFinite(h[0])&&isFinite(h[1])&&isFinite(l[0])&&isFinite(l[1])))if(0<m)u=!0;else return;if(0<m&&(u||(p=a.a([(b[0]+d[0])/2,(b[1]+d[1])/2]),q=r?(oa(f[0],q)+oa(h[0],q))/2-oa(p[0],q):(f[0]+h[0])/2-p[0],p=(f[1]+h[1])/2-p[1],
+u=q*q+p*p>a.v),u)){Math.abs(b[0]-d[0])<=Math.abs(b[1]-d[1])?(r=[(c[0]+d[0])/2,(c[1]+d[1])/2],q=a.a(r),p=[(e[0]+b[0])/2,(e[1]+b[1])/2],n=a.a(p),em(a,b,c,r,p,f,g,q,n,m-1),em(a,p,r,d,e,n,q,h,l,m-1)):(r=[(b[0]+c[0])/2,(b[1]+c[1])/2],q=a.a(r),p=[(d[0]+e[0])/2,(d[1]+e[1])/2],n=a.a(p),em(a,b,r,p,e,f,q,n,l,m-1),em(a,r,c,d,p,q,g,h,n,m-1));return}if(r){if(!a.o)return;a.l=!0}a.f.push({source:[f,h,l],target:[b,d,e]});a.f.push({source:[f,g,h],target:[b,c,d]})}}
+function fm(a){var b=Hb();a.f.forEach(function(a){a=a.source;Ib(b,a[0]);Ib(b,a[1]);Ib(b,a[2])});return b};function gm(a,b,c,d,e,f){this.H=b;this.v=a.G();var g=b.G(),h=g?ic(c,g):c,g=am(a,b,gc(h),d);this.l=new dm(a,b,h,this.v,.5*g);this.c=d;this.g=c;a=fm(this.l);this.o=(this.xb=f(a,g,e))?this.xb.f:1;this.Md=this.i=null;e=li;f=[];this.xb&&(e=ji,f=this.xb.j);hi.call(this,c,d,this.o,e,f)}v(gm,hi);gm.prototype.oa=function(){this.state==mi&&(ya(this.Md),this.Md=null);hi.prototype.oa.call(this)};gm.prototype.a=function(){return this.i};
+gm.prototype.Ld=function(){var a=this.xb.W();a==li&&(this.i=cm(dc(this.g)/this.c,ec(this.g)/this.c,this.o,this.xb.resolution,0,this.c,this.g,this.l,[{extent:this.xb.G(),image:this.xb.a()}],0));this.state=a;this.s()};gm.prototype.load=function(){if(this.state==ji){this.state=mi;this.s();var a=this.xb.W();a==li||a==ki?this.Ld():(this.Md=B(this.xb,"change",function(){var a=this.xb.W();if(a==li||a==ki)ya(this.Md),this.Md=null,this.Ld()},this),this.xb.load())}};function hm(a){Sa.call(this);this.f=zc(a.projection);this.j=im(a.attributions);this.L=a.logo;this.Fa=void 0!==a.state?a.state:"ready";this.D=void 0!==a.wrapX?a.wrapX:!1}v(hm,Sa);function im(a){if("string"===typeof a)return[new pe({html:a})];if(a instanceof pe)return[a];if(Array.isArray(a)){for(var b=a.length,c=Array(b),d=0;d<b;d++){var e=a[d];c[d]="string"===typeof e?new pe({html:e}):e}return c}return null}k=hm.prototype;k.Ba=ea;k.za=function(){return this.j};k.ya=function(){return this.L};k.Aa=function(){return this.f};
+k.W=function(){return this.Fa};k.wa=function(){this.s()};k.ua=function(a){this.j=im(a);this.s()};function jm(a,b){a.Fa=b;a.s()};function km(a){hm.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,state:a.state});this.A=void 0!==a.resolutions?a.resolutions:null;this.a=null;this.sa=0}v(km,hm);function lm(a,b){a.A&&(b=a.A[$a(a.A,b,0)]);return b}
+km.prototype.U=function(a,b,c,d){var e=this.f;if(e&&d&&!Mc(e,d)){if(this.a){if(this.sa==this.g&&Mc(this.a.H,d)&&this.a.resolution==b&&this.a.f==c&&Vb(this.a.G(),a))return this.a;Ha(this.a);this.a=null}this.a=new gm(e,d,a,b,c,function(a,b,c){return this.Xc(a,b,c,e)}.bind(this));this.sa=this.g;return this.a}e&&(d=e);return this.Xc(a,b,c,d)};km.prototype.o=function(a){a=a.target;switch(a.W()){case mi:this.b(new mm(nm,a));break;case li:this.b(new mm(om,a));break;case ki:this.b(new mm(pm,a))}};
+function qm(a,b){a.a().src=b}function mm(a,b){Ia.call(this,a);this.image=b}v(mm,Ia);var nm="imageloadstart",om="imageloadend",pm="imageloaderror";function rm(a){km.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions,state:a.state});this.fa=a.canvasFunction;this.P=null;this.Z=0;this.na=void 0!==a.ratio?a.ratio:1.5}v(rm,km);rm.prototype.Xc=function(a,b,c,d){b=lm(this,b);var e=this.P;if(e&&this.Z==this.g&&e.resolution==b&&e.f==c&&Ob(e.G(),a))return e;a=a.slice();kc(a,this.na);(d=this.fa(a,b,c,[dc(a)/b*c,ec(a)/b*c],d))&&(e=new Xl(a,b,c,this.j,d));this.P=e;this.Z=this.g;return e};function sm(a){this.c=a.source;this.Ja=Ph();this.i=De();this.l=[0,0];this.xa=void 0==a.renderBuffer?100:a.renderBuffer;this.u=null;rm.call(this,{attributions:a.attributions,canvasFunction:this.Jj.bind(this),logo:a.logo,projection:a.projection,ratio:a.ratio,resolutions:a.resolutions,state:this.c.W()});this.C=null;this.v=void 0;this.Fh(a.style);B(this.c,"change",this.kn,this)}v(sm,rm);k=sm.prototype;
+k.Jj=function(a,b,c,d,e){var f=new Bj(.5*b/c,a,b,this.c.xa,this.xa);this.c.Ed(a,b,e);var g=!1;this.c.Qb(a,function(a){var d;if(!(d=g)){var e;(d=a.Gc())?e=d.call(a,b):this.v&&(e=this.v(a,b));if(e){var p,n=!1;Array.isArray(e)||(e=[e]);d=0;for(p=e.length;d<p;++d)n=Lj(f,a,e[d],Kj(b,c),this.jn,this)||n;d=n}else d=!1}g=d},this);Fj(f);if(g)return null;this.l[0]!=d[0]||this.l[1]!=d[1]?(this.i.canvas.width=d[0],this.i.canvas.height=d[1],this.l[0]=d[0],this.l[1]=d[1]):this.i.clearRect(0,0,d[0],d[1]);a=tm(this,
+gc(a),b,c,d);f.f(this.i,c,a,0,{});this.u=f;return this.i.canvas};k.Ba=function(a,b,c,d,e,f){if(this.u){var g={};return this.u.Ba(a,b,0,d,e,function(a){var b=x(a).toString();if(!(b in g))return g[b]=!0,f(a)})}};k.fn=function(){return this.c};k.gn=function(){return this.C};k.hn=function(){return this.v};function tm(a,b,c,d,e){c=d/c;return Yh(a.Ja,e[0]/2,e[1]/2,c,-c,0,-b[0],-b[1])}k.jn=function(){this.s()};k.kn=function(){jm(this,this.c.W())};
+k.Fh=function(a){this.C=void 0!==a?a:Bi;this.v=a?zi(this.C):void 0;this.s()};function um(a,b){Ul.call(this,a,b);this.o=this.l=this.c=null}v(um,Ul);function vm(a,b){var c=b.a();return Ck(a.f.g,c)}um.prototype.Ba=function(a,b,c,d,e){var f=this.a;return f.la().Ba(a,b.viewState.resolution,b.viewState.rotation,c,b.skippedFeatureUids,function(a){return d.call(e,a,f)})};
+um.prototype.Jf=function(a,b){var c=this.f.g,d=a.pixelRatio,e=a.viewState,f=e.center,g=e.resolution,h=e.rotation,l=this.c,m=this.yb,p=this.a.la(),n=a.viewHints,q=a.extent;void 0!==b.extent&&(q=ic(q,b.extent));n[Kd]||n[1]||cc(q)||(e=p.U(q,g,d,e.projection))&&Xi(this,e)&&(l=e,m=vm(this,e),this.yb&&a.postRenderFunctions.push(function(a,b){a.isContextLost()||a.deleteTexture(b)}.bind(null,c,this.yb)));l&&(c=this.f.c.j,wm(this,c.width,c.height,d,f,g,h,l.G()),this.o=null,d=this.v,Qh(d),Wh(d,1,-1),Xh(d,0,
+-1),this.c=l,this.yb=m,Zi(a.attributions,l.j),$i(a,p));return!!l};function wm(a,b,c,d,e,f,g,h){b*=f;c*=f;a=a.u;Qh(a);Wh(a,2*d/b,2*d/c);Vh(a,-g);Xh(a,h[0]-e[0],h[1]-e[1]);Wh(a,(h[2]-h[0])/2,(h[3]-h[1])/2);Xh(a,1,1)}um.prototype.te=function(a,b){return void 0!==this.Ba(a,b,0,mc,this)};
+um.prototype.Hf=function(a,b,c,d){if(this.c&&this.c.a())if(this.a.la()instanceof sm){var e=Uh(b.pixelToCoordinateTransform,a.slice());if(this.Ba(e,b,0,mc,this))return c.call(d,this.a,null)}else{e=[this.c.a().width,this.c.a().height];if(!this.o){var f=b.size;b=Ph();Xh(b,-1,-1);Wh(b,2/f[0],2/f[1]);Xh(b,0,f[1]);Wh(b,1,-1);var f=Zh(this.u.slice()),g=Ph();Xh(g,0,e[1]);Wh(g,1,-1);Wh(g,e[0]/2,e[1]/2);Xh(g,1,1);Sh(g,f);Sh(g,b);this.o=g}a=Uh(this.o,a.slice());if(!(0>a[0]||a[0]>e[0]||0>a[1]||a[1]>e[1])&&(this.l||
+(this.l=De(1,1)),this.l.clearRect(0,0,1,1),this.l.drawImage(this.c.a(),a[0],a[1],1,1,0,0,1,1),e=this.l.getImageData(0,0,1,1).data,0<e[3]))return c.call(d,this.a,e)}};function xm(){this.b="precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}"}v(xm,ak);var ym=new xm;function zm(){this.b="varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}"}v(zm,bk);var Am=new zm;function Bm(a,b){this.g=a.getUniformLocation(b,"e");this.f=a.getUniformLocation(b,"d");this.b=a.getAttribLocation(b,"b");this.a=a.getAttribLocation(b,"c")};function Cm(a,b){Ul.call(this,a,b);this.L=ym;this.Z=Am;this.c=null;this.D=new mk([0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0]);this.A=this.l=null;this.o=-1;this.P=[0,0]}v(Cm,Ul);k=Cm.prototype;k.oa=function(){pk(this.f.c,this.D);Ul.prototype.oa.call(this)};k.hf=function(a,b,c){var d=this.f;return function(e,f){return Vi(a,b,e,f,function(a){var b=d.a.b.hasOwnProperty(a.bb());b&&(c[e]||(c[e]={}),c[e][a.Ca.toString()]=a);return b})}};k.If=function(){Ul.prototype.If.call(this);this.c=null};
+k.Jf=function(a,b,c){var d=this.f,e=c.b,f=a.viewState,g=f.projection,h=this.a,l=h.la(),m=l.Db(g),p=m.Ec(f.resolution),n=m.Ha(p),q=l.Dd(p,a.pixelRatio,g),r=q[0]/Zd(m.Za(p),this.P)[0],u=n/r,w=l.jb(r)*l.qf(g),y=f.center,z=a.extent,A=ie(m,z,n);if(this.l&&Vd(this.l,A)&&this.o==l.g)u=this.A;else{var O=[A.ca-A.ea+1,A.ja-A.ga+1],Ja=ka(Math.max(O[0]*q[0],O[1]*q[1])),O=u*Ja,ca=m.Kc(p),Ma=ca[0]+A.ea*q[0]*u,u=ca[1]+A.ga*q[1]*u,u=[Ma,u,Ma+O,u+O];Vl(this,a,Ja);e.viewport(0,0,Ja,Ja);e.clearColor(0,0,0,0);e.clear(16384);
+e.disable(3042);Ja=qk(c,this.L,this.Z);c.Lc(Ja);this.c||(this.c=new Bm(e,Ja));kk(c,34962,this.D);e.enableVertexAttribArray(this.c.b);e.vertexAttribPointer(this.c.b,2,5126,!1,16,0);e.enableVertexAttribArray(this.c.a);e.vertexAttribPointer(this.c.a,2,5126,!1,16,8);e.uniform1i(this.c.g,0);c={};c[p]={};var D=this.hf(l,g,c),La=h.c(),Ja=!0,Ma=Hb(),kb=new Td(0,0,0,0),W,Ra,Pb;for(Ra=A.ea;Ra<=A.ca;++Ra)for(Pb=A.ga;Pb<=A.ja;++Pb){ca=l.Dc(p,Ra,Pb,r,g);if(void 0!==b.extent&&(W=m.Na(ca.Ca,Ma),!jc(W,b.extent)))continue;
+W=ca.W();(W=W==cg||4==W||3==W&&!La)||(ca=bg(ca));W=ca.W();if(W==cg){if(d.a.b.hasOwnProperty(ca.bb())){c[p][ca.Ca.toString()]=ca;continue}}else if(4==W||3==W&&!La)continue;Ja=!1;W=ge(m,ca.Ca,D,kb,Ma);W||(ca=he(m,ca.Ca,kb,Ma))&&D(p+1,ca)}b=Object.keys(c).map(Number);b.sort(Ya);for(var D=new Float32Array(4),fc,La=0,kb=b.length;La<kb;++La)for(fc in Ra=c[b[La]],Ra)ca=Ra[fc],W=m.Na(ca.Ca,Ma),D[0]=2*(W[2]-W[0])/O,D[1]=2*(W[3]-W[1])/O,D[2]=2*(W[0]-u[0])/O-1,D[3]=2*(W[1]-u[1])/O-1,e.uniform4fv(this.c.f,D),
+Dm(d,ca,q,w*r),e.drawArrays(5,0,4);Ja?(this.l=A,this.A=u,this.o=l.g):(this.A=this.l=null,this.o=-1,a.animate=!0)}aj(a.usedTiles,l,p,A);var Wc=d.j;bj(a,l,m,r,g,z,p,h.f(),function(a){a.W()!=cg||d.a.b.hasOwnProperty(a.bb())||a.bb()in Wc.a||Wc.c([a,ke(m,a.Ca),m.Ha(a.Ca[0]),q,w*r])},this);Yi(a,l);$i(a,l);e=this.v;Qh(e);Xh(e,(Math.round(y[0]/n)*n-u[0])/(u[2]-u[0]),(Math.round(y[1]/n)*n-u[1])/(u[3]-u[1]));0!==f.rotation&&Vh(e,f.rotation);Wh(e,a.size[0]*f.resolution/(u[2]-u[0]),a.size[1]*f.resolution/(u[3]-
+u[1]));Xh(e,-.5,-.5);return!0};k.Hf=function(a,b,c,d){if(this.i){a=Uh(this.v,[a[0]/b.size[0],(b.size[1]-a[1])/b.size[1]].slice());a=[a[0]*this.j,a[1]*this.j];b=this.f.c.b;b.bindFramebuffer(b.FRAMEBUFFER,this.i);var e=new Uint8Array(4);b.readPixels(a[0],a[1],1,1,b.RGBA,b.UNSIGNED_BYTE,e);if(0<e[3])return c.call(d,this.a,e)}};function Em(a,b){Ul.call(this,a,b);this.o=!1;this.P=-1;this.L=NaN;this.A=Hb();this.l=this.c=this.D=null}v(Em,Ul);k=Em.prototype;k.Dh=function(a,b,c){this.l=b;var d=a.viewState,e=this.c,f=a.size,g=a.pixelRatio,h=this.f.g;e&&!e.g()&&(h.enable(h.SCISSOR_TEST),h.scissor(0,0,f[0]*g,f[1]*g),e.f(c,d.center,d.resolution,d.rotation,f,g,b.opacity,b.me?a.skippedFeatureUids:{}),h.disable(h.SCISSOR_TEST))};k.oa=function(){var a=this.c;a&&(Il(a,this.f.c)(),this.c=null);Ul.prototype.oa.call(this)};
+k.Ba=function(a,b,c,d,e){if(this.c&&this.l){c=b.viewState;var f=this.a,g={};return this.c.Ba(a,this.f.c,c.center,c.resolution,c.rotation,b.size,b.pixelRatio,this.l.opacity,{},function(a){var b=x(a).toString();if(!(b in g))return g[b]=!0,d.call(e,a,f)})}};k.te=function(a,b){if(this.c&&this.l){var c=b.viewState;return Nl(this.c,a,this.f.c,c.resolution,c.rotation,b.pixelRatio,this.l.opacity,b.skippedFeatureUids)}return!1};
+k.Hf=function(a,b,c,d){a=Uh(b.pixelToCoordinateTransform,a.slice());if(this.te(a,b))return c.call(d,this.a,null)};k.Eh=function(){Wi(this)};
+k.Jf=function(a,b,c){function d(a){var b,c=a.Gc();c?b=c.call(a,m):(c=e.j)&&(b=c(a,m));if(b){if(b){c=!1;if(Array.isArray(b))for(var d=b.length-1;0<=d;--d)c=Lj(q,a,b[d],Kj(m,p),this.Eh,this)||c;else c=Lj(q,a,b,Kj(m,p),this.Eh,this)||c;a=c}else a=!1;this.o=this.o||a}}var e=this.a;b=e.la();Zi(a.attributions,b.j);$i(a,b);var f=a.viewHints[Kd],g=a.viewHints[1],h=e.Z,l=e.fa;if(!this.o&&!h&&f||!l&&g)return!0;var g=a.extent,h=a.viewState,f=h.projection,m=h.resolution,p=a.pixelRatio,h=e.g,n=e.i,l=e.get("renderOrder");
+void 0===l&&(l=Jj);g=Jb(g,n*m);if(!this.o&&this.L==m&&this.P==h&&this.D==l&&Ob(this.A,g))return!0;this.c&&a.postRenderFunctions.push(Il(this.c,c));this.o=!1;var q=new Hl(.5*m/p,g,e.i);b.Ed(g,m,f);if(l){var r=[];b.Qb(g,function(a){r.push(a)},this);r.sort(l);r.forEach(d,this)}else b.Qb(g,d,this);Jl(q,c);this.L=m;this.P=h;this.D=l;this.A=g;this.c=q;return!0};function Fm(){this.f=0;this.b={};this.g=this.a=null}k=Fm.prototype;k.clear=function(){this.f=0;this.b={};this.g=this.a=null};k.forEach=function(a,b){for(var c=this.a;c;)a.call(b,c.Rc,c.jc,this),c=c.Gb};k.get=function(a){a=this.b[a];ha(void 0!==a,15);if(a===this.g)return a.Rc;a===this.a?(this.a=this.a.Gb,this.a.fd=null):(a.Gb.fd=a.fd,a.fd.Gb=a.Gb);a.Gb=null;a.fd=this.g;this.g=this.g.Gb=a;return a.Rc};
+k.pop=function(){var a=this.a;delete this.b[a.jc];a.Gb&&(a.Gb.fd=null);this.a=a.Gb;this.a||(this.g=null);--this.f;return a.Rc};k.replace=function(a,b){this.get(a);this.b[a].Rc=b};k.set=function(a,b){ha(!(a in this.b),16);var c={jc:a,Gb:null,fd:this.g,Rc:b};this.g?this.g.Gb=c:this.a=c;this.g=c;this.b[a]=c;++this.f};function Gm(a,b){$h.call(this,0,b);this.b=document.createElement("CANVAS");this.b.style.width="100%";this.b.style.height="100%";this.b.className="ol-unselectable";a.insertBefore(this.b,a.childNodes[0]||null);this.u=this.A=0;this.C=De();this.o=!0;this.g=ef(this.b,{antialias:!0,depth:!0,failIfMajorPerformanceCaveat:!0,preserveDrawingBuffer:!1,stencil:!0});this.c=new xk(this.b,this.g);B(this.b,"webglcontextlost",this.Um,this);B(this.b,"webglcontextrestored",this.Vm,this);this.a=new Fm;this.H=null;this.j=
+new dg(function(a){var b=a[1];a=a[2];var e=b[0]-this.H[0],b=b[1]-this.H[1];return 65536*Math.log(a)+Math.sqrt(e*e+b*b)/a}.bind(this),function(a){return a[0].bb()});this.D=function(){if(0!==this.j.b.length){hg(this.j);var a=eg(this.j);Dm(this,a[0],a[3],a[4])}return!1}.bind(this);this.i=0;Hm(this)}v(Gm,$h);
+function Dm(a,b,c,d){var e=a.g,f=b.bb();if(a.a.b.hasOwnProperty(f))a=a.a.get(f),e.bindTexture(3553,a.yb),9729!=a.gh&&(e.texParameteri(3553,10240,9729),a.gh=9729),9729!=a.ih&&(e.texParameteri(3553,10241,9729),a.ih=9729);else{var g=e.createTexture();e.bindTexture(3553,g);if(0<d){var h=a.C.canvas,l=a.C;a.A!==c[0]||a.u!==c[1]?(h.width=c[0],h.height=c[1],a.A=c[0],a.u=c[1]):l.clearRect(0,0,c[0],c[1]);l.drawImage(b.ub(),d,d,c[0],c[1],0,0,c[0],c[1]);e.texImage2D(3553,0,6408,6408,5121,h)}else e.texImage2D(3553,
+0,6408,6408,5121,b.ub());e.texParameteri(3553,10240,9729);e.texParameteri(3553,10241,9729);e.texParameteri(3553,10242,33071);e.texParameteri(3553,10243,33071);a.a.set(f,{yb:g,gh:9729,ih:9729})}}k=Gm.prototype;k.Ag=function(a){return a instanceof ei?new um(this,a):a instanceof F?new Cm(this,a):a instanceof G?new Em(this,a):null};function Im(a,b,c){var d=a.l;if(Oa(d,b)){a=a.c;var e=c.viewState;d.b(new Jh(b,new Ol(a,e.center,e.resolution,e.rotation,c.size,c.extent,c.pixelRatio),c,null,a))}}
+k.oa=function(){var a=this.g;a.isContextLost()||this.a.forEach(function(b){b&&a.deleteTexture(b.yb)});Ha(this.c);$h.prototype.oa.call(this)};k.Nj=function(a,b){for(var c=this.g,d;1024<this.a.f-this.i;){if(d=this.a.a.Rc)c.deleteTexture(d.yb);else if(+this.a.a.jc==b.index)break;else--this.i;this.a.pop()}};k.Y=function(){return"webgl"};k.Um=function(a){a.preventDefault();this.a.clear();this.i=0;a=this.f;for(var b in a)a[b].If()};k.Vm=function(){Hm(this);this.l.render()};
+function Hm(a){a=a.g;a.activeTexture(33984);a.blendFuncSeparate(770,771,1,771);a.disable(2884);a.disable(2929);a.disable(3089);a.disable(2960)}
+k.ag=function(a){var b=this.c,c=this.g;if(c.isContextLost())return!1;if(!a)return this.o&&(this.b.style.display="none",this.o=!1),!1;this.H=a.focus;this.a.set((-a.index).toString(),null);++this.i;Im(this,"precompose",a);var d=[],e=a.layerStatesArray;eb(e);var f=a.viewState.resolution,g,h,l,m;g=0;for(h=e.length;g<h;++g)m=e[g],Lh(m,f)&&"ready"==m.Ei&&(l=ci(this,m.layer),l.Jf(a,m,b)&&d.push(m));e=a.size[0]*a.pixelRatio;f=a.size[1]*a.pixelRatio;if(this.b.width!=e||this.b.height!=f)this.b.width=e,this.b.height=
+f;c.bindFramebuffer(36160,null);c.clearColor(0,0,0,0);c.clear(16384);c.enable(3042);c.viewport(0,0,this.b.width,this.b.height);g=0;for(h=d.length;g<h;++g)m=d[g],l=ci(this,m.layer),l.Dh(a,m,b);this.o||(this.b.style.display="",this.o=!0);ai(a);1024<this.a.f-this.i&&a.postRenderFunctions.push(this.Nj.bind(this));0!==this.j.b.length&&(a.postRenderFunctions.push(this.D),a.animate=!0);Im(this,"postcompose",a);di(this,a);a.postRenderFunctions.push(bi)};
+k.Ba=function(a,b,c,d,e,f,g){var h;if(this.g.isContextLost())return!1;var l=b.viewState,m=b.layerStatesArray,p;for(p=m.length-1;0<=p;--p){h=m[p];var n=h.layer;if(Lh(h,l.resolution)&&f.call(g,n)&&(h=ci(this,n).Ba(a,b,c,d,e)))return h}};k.Ch=function(a,b,c,d,e){c=!1;if(this.g.isContextLost())return!1;var f=b.viewState,g=b.layerStatesArray,h;for(h=g.length-1;0<=h;--h){var l=g[h],m=l.layer;if(Lh(l,f.resolution)&&d.call(e,m)&&(c=ci(this,m).te(a,b)))return!0}return c};
+k.Bh=function(a,b,c,d,e){if(this.g.isContextLost())return!1;var f=b.viewState,g,h=b.layerStatesArray,l;for(l=h.length-1;0<=l;--l){g=h[l];var m=g.layer;if(Lh(g,f.resolution)&&e.call(d,m)&&(g=ci(this,m).Hf(a,b,c,d)))return g}};var Jm=["canvas","webgl"];
+function I(a){Sa.call(this);var b=Km(a);this.Xe=void 0!==a.loadTilesWhileAnimating?a.loadTilesWhileAnimating:!1;this.Ve=void 0!==a.loadTilesWhileInteracting?a.loadTilesWhileInteracting:!1;this.cf=void 0!==a.pixelRatio?a.pixelRatio:mf;this.bf=b.logos;this.sa=function(){this.i=void 0;this.Zo.call(this,Date.now())}.bind(this);this.Ob=Ph();this.df=Ph();this.fc=0;this.a=null;this.Ua=Hb();this.D=this.L=this.P=null;this.f=document.createElement("DIV");this.f.className="ol-viewport"+(rf?" ol-touch":"");this.f.style.position=
+"relative";this.f.style.overflow="hidden";this.f.style.width="100%";this.f.style.height="100%";this.f.style.msTouchAction="none";this.f.style.touchAction="none";this.A=document.createElement("DIV");this.A.className="ol-overlaycontainer";this.f.appendChild(this.A);this.u=document.createElement("DIV");this.u.className="ol-overlaycontainer-stopevent";a="click dblclick mousedown touchstart mspointerdown pointerdown mousewheel wheel".split(" ");for(var c=0,d=a.length;c<d;++c)B(this.u,a[c],Ka);this.f.appendChild(this.u);
+this.Fa=new Yf(this);for(var e in af)B(this.Fa,af[e],this.Zg,this);this.na=b.keyboardEventTarget;this.v=null;B(this.f,"wheel",this.ad,this);B(this.f,"mousewheel",this.ad,this);this.l=b.controls;this.j=b.interactions;this.o=b.overlays;this.Mf={};this.C=new b.ap(this.f,this);this.U=null;this.Z=[];this.Ja=[];this.xa=new ig(this.Gk.bind(this),this.kl.bind(this));this.fa={};B(this,Ua(Lm),this.Tk,this);B(this,Ua(Mm),this.ll,this);B(this,Ua(Nm),this.hl,this);B(this,Ua(Om),this.jl,this);this.I(b.values);
+this.l.forEach(function(a){a.setMap(this)},this);B(this.l,ue,function(a){a.element.setMap(this)},this);B(this.l,ve,function(a){a.element.setMap(null)},this);this.j.forEach(function(a){a.setMap(this)},this);B(this.j,ue,function(a){a.element.setMap(this)},this);B(this.j,ve,function(a){a.element.setMap(null)},this);this.o.forEach(this.wg,this);B(this.o,ue,function(a){this.wg(a.element)},this);B(this.o,ve,function(a){var b=a.element.i;void 0!==b&&delete this.Mf[b.toString()];a.element.setMap(null)},this)}
+v(I,Sa);k=I.prototype;k.Bj=function(a){this.l.push(a)};k.Cj=function(a){this.j.push(a)};k.ug=function(a){this.Bc().cd().push(a)};k.vg=function(a){this.o.push(a)};k.wg=function(a){var b=a.i;void 0!==b&&(this.Mf[b.toString()]=a);a.setMap(this)};k.Ij=function(a){this.render();Array.prototype.push.apply(this.Z,arguments)};
+k.oa=function(){Ha(this.Fa);Ha(this.C);Ea(this.f,"wheel",this.ad,this);Ea(this.f,"mousewheel",this.ad,this);void 0!==this.c&&(window.removeEventListener("resize",this.c,!1),this.c=void 0);this.i&&(cancelAnimationFrame(this.i),this.i=void 0);this.ph(null);Sa.prototype.oa.call(this)};k.ae=function(a,b,c){if(this.a)return a=this.Sa(a),c=void 0!==c?c:{},this.C.Ba(a,this.a,void 0!==c.hitTolerance?c.hitTolerance*this.a.pixelRatio:0,b,null,void 0!==c.layerFilter?c.layerFilter:mc,null)};
+k.Wl=function(a,b,c,d,e){if(this.a)return this.C.Bh(a,this.a,b,void 0!==c?c:null,void 0!==d?d:mc,void 0!==e?e:null)};k.ml=function(a,b){if(!this.a)return!1;var c=this.Sa(a);b=void 0!==b?b:{};return this.C.Ch(c,this.a,void 0!==b.hitTolerance?b.hitTolerance*this.a.pixelRatio:0,void 0!==b.layerFilter?b.layerFilter:mc,null)};k.ck=function(a){return this.Sa(this.ce(a))};k.ce=function(a){var b=this.f.getBoundingClientRect();a=a.changedTouches?a.changedTouches[0]:a;return[a.clientX-b.left,a.clientY-b.top]};
+k.vf=function(){return this.get(Om)};k.Cc=function(){var a=this.vf();return void 0!==a?"string"===typeof a?document.getElementById(a):a:null};k.Sa=function(a){var b=this.a;return b?Uh(b.pixelToCoordinateTransform,a.slice()):null};k.ak=function(){return this.l};k.vk=function(){return this.o};k.uk=function(a){a=this.Mf[a.toString()];return void 0!==a?a:null};k.hk=function(){return this.j};k.Bc=function(){return this.get(Lm)};k.oh=function(){return this.Bc().cd()};
+k.Ga=function(a){var b=this.a;return b?Uh(b.coordinateToPixelTransform,a.slice(0,2)):null};k.nb=function(){return this.get(Nm)};k.aa=function(){return this.get(Mm)};k.Ik=function(){return this.f};k.Gk=function(a,b,c,d){var e=this.a;if(!(e&&b in e.wantedTiles&&e.wantedTiles[b][a.bb()]))return Infinity;a=c[0]-e.focus[0];c=c[1]-e.focus[1];return 65536*Math.log(d)+Math.sqrt(a*a+c*c)/d};k.ad=function(a,b){var c=new $e(b||a.type,this,a);this.Zg(c)};
+k.Zg=function(a){if(this.a){this.U=a.coordinate;a.frameState=this.a;var b=this.j.a,c;if(!1!==this.b(a))for(c=b.length-1;0<=c;c--){var d=b[c];if(d.f()&&!d.handleEvent(a))break}}};k.fl=function(){var a=this.a,b=this.xa;if(0!==b.b.length){var c=16,d=c;if(a){var e=a.viewHints;e[Kd]&&(c=this.Xe?8:0,d=2);e[1]&&(c=this.Ve?8:0,d=2)}b.j<c&&(hg(b),jg(b,c,d))}b=this.Ja;c=0;for(d=b.length;c<d;++c)b[c](this,a);b.length=0};k.hl=function(){this.render()};
+k.jl=function(){var a;this.vf()&&(a=this.Cc());if(this.v){for(var b=0,c=this.v.length;b<c;++b)ya(this.v[b]);this.v=null}a?(a.appendChild(this.f),a=this.na?this.na:a,this.v=[B(a,"keydown",this.ad,this),B(a,"keypress",this.ad,this)],this.c||(this.c=this.ld.bind(this),window.addEventListener("resize",this.c,!1))):(Fe(this.f),void 0!==this.c&&(window.removeEventListener("resize",this.c,!1),this.c=void 0));this.ld()};k.kl=function(){this.render()};k.bh=function(){this.render()};
+k.ll=function(){this.P&&(ya(this.P),this.P=null);this.L&&(ya(this.L),this.L=null);var a=this.aa();a&&(this.P=B(a,Xa,this.bh,this),this.L=B(a,"change",this.bh,this));this.render()};k.Tk=function(){this.D&&(this.D.forEach(ya),this.D=null);var a=this.Bc();a&&(this.D=[B(a,Xa,this.render,this),B(a,"change",this.render,this)]);this.render()};k.$o=function(){this.i&&cancelAnimationFrame(this.i);this.sa()};k.render=function(){void 0===this.i&&(this.i=requestAnimationFrame(this.sa))};k.To=function(a){return this.l.remove(a)};
+k.Uo=function(a){return this.j.remove(a)};k.Wo=function(a){return this.Bc().cd().remove(a)};k.Xo=function(a){return this.o.remove(a)};
+k.Zo=function(a){var b,c,d,e=this.nb(),f=this.aa(),g=Hb(),h=null;if(void 0!==e&&0<e[0]&&0<e[1]&&f&&Sd(f)){var h=Md(f,this.a?this.a.viewHints:void 0),l=this.Bc().sf(),m={};b=0;for(c=l.length;b<c;++b)m[x(l[b].layer)]=l[b];d=f.W();h={animate:!1,attributions:{},coordinateToPixelTransform:this.Ob,extent:g,focus:this.U?this.U:d.center,index:this.fc++,layerStates:m,layerStatesArray:l,logos:ta({},this.bf),pixelRatio:this.cf,pixelToCoordinateTransform:this.df,postRenderFunctions:[],size:e,skippedFeatureUids:this.fa,
+tileQueue:this.xa,time:a,usedTiles:{},viewState:d,viewHints:h,wantedTiles:{}}}if(h){a=this.Z;b=e=0;for(c=a.length;b<c;++b)f=a[b],f(this,h)&&(a[e++]=f);a.length=e;h.extent=hc(d.center,d.resolution,d.rotation,h.size,g)}this.a=h;this.C.ag(h);h&&(h.animate&&this.render(),Array.prototype.push.apply(this.Ja,h.postRenderFunctions),0!==this.Z.length||h.viewHints[Kd]||h.viewHints[1]||Vb(h.extent,this.Ua)||(this.b(new Ge("moveend",this,h)),Kb(h.extent,this.Ua)));this.b(new Ge("postrender",this,h));setTimeout(this.fl.bind(this),
+0)};k.vi=function(a){this.set(Lm,a)};k.eg=function(a){this.set(Nm,a)};k.ph=function(a){this.set(Om,a)};k.mp=function(a){this.set(Mm,a)};k.Di=function(a){a=x(a).toString();this.fa[a]=!0;this.render()};k.ld=function(){var a=this.Cc();if(a){var b=getComputedStyle(a);this.eg([a.offsetWidth-parseFloat(b.borderLeftWidth)-parseFloat(b.paddingLeft)-parseFloat(b.paddingRight)-parseFloat(b.borderRightWidth),a.offsetHeight-parseFloat(b.borderTopWidth)-parseFloat(b.paddingTop)-parseFloat(b.paddingBottom)-parseFloat(b.borderBottomWidth)])}else this.eg(void 0)};
+k.Ji=function(a){a=x(a).toString();delete this.fa[a];this.render()};
+function Km(a){var b=null;void 0!==a.keyboardEventTarget&&(b="string"===typeof a.keyboardEventTarget?document.getElementById(a.keyboardEventTarget):a.keyboardEventTarget);var c={},d={};if(void 0===a.logo||"boolean"===typeof a.logo&&a.logo)d["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszWWMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvYasvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvXH1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1VkbMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLPVcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqTacrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaarldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+HizeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDnBAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSFhYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJREFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxCBrb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7ahgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCnB3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDgq82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC"]="https://openlayers.org/";
+else{var e=a.logo;"string"===typeof e?d[e]="":e instanceof HTMLElement?d[x(e).toString()]=e:e&&(ha("string"==typeof e.href,44),ha("string"==typeof e.src,45),d[e.src]=e.href)}e=a.layers instanceof yh?a.layers:new yh({layers:a.layers});c[Lm]=e;c[Om]=a.target;c[Mm]=void 0!==a.view?a.view:new Fd;var e=$h,f;void 0!==a.renderer?(Array.isArray(a.renderer)?f=a.renderer:"string"===typeof a.renderer?f=[a.renderer]:ha(!1,46),0<=f.indexOf("dom")&&(f=f.concat(Jm))):f=Jm;var g,h;g=0;for(h=f.length;g<h;++g){var l=
+f[g];if("canvas"==l){if(of){e=Sj;break}}else if("webgl"==l&&ff){e=Gm;break}}void 0!==a.controls?Array.isArray(a.controls)?f=new qe(a.controls.slice()):(ha(a.controls instanceof qe,47),f=a.controls):f=Ue();void 0!==a.interactions?Array.isArray(a.interactions)?g=new qe(a.interactions.slice()):(ha(a.interactions instanceof qe,48),g=a.interactions):g=ph();void 0!==a.overlays?Array.isArray(a.overlays)?a=new qe(a.overlays.slice()):(ha(a.overlays instanceof qe,49),a=a.overlays):a=new qe;return{controls:f,
+interactions:g,keyboardEventTarget:b,logos:d,overlays:a,ap:e,values:c}}var Lm="layergroup",Nm="size",Om="target",Mm="view";Ih();function Pm(a){Sa.call(this);this.i=a.id;this.o=void 0!==a.insertFirst?a.insertFirst:!0;this.v=void 0!==a.stopEvent?a.stopEvent:!0;this.f=document.createElement("DIV");this.f.className="ol-overlay-container";this.f.style.position="absolute";this.autoPan=void 0!==a.autoPan?a.autoPan:!1;this.j=a.autoPanAnimation||{};this.l=void 0!==a.autoPanMargin?a.autoPanMargin:20;this.a={Xd:"",le:"",Je:"",Oe:"",visible:!0};this.c=null;B(this,Ua(Qm),this.Ok,this);B(this,Ua(Rm),this.Yk,this);B(this,Ua(Sm),this.bl,
+this);B(this,Ua(Tm),this.dl,this);B(this,Ua(Um),this.el,this);void 0!==a.element&&this.pi(a.element);this.xi(void 0!==a.offset?a.offset:[0,0]);this.Ai(void 0!==a.positioning?a.positioning:Vm);void 0!==a.position&&this.Ef(a.position)}v(Pm,Sa);k=Pm.prototype;k.be=function(){return this.get(Qm)};k.Xl=function(){return this.i};k.oe=function(){return this.get(Rm)};k.Tg=function(){return this.get(Sm)};k.qh=function(){return this.get(Tm)};k.Ug=function(){return this.get(Um)};
+k.Ok=function(){for(var a=this.f;a.lastChild;)a.removeChild(a.lastChild);(a=this.be())&&this.f.appendChild(a)};k.Yk=function(){this.c&&(Fe(this.f),ya(this.c),this.c=null);var a=this.oe();a&&(this.c=B(a,"postrender",this.render,this),Wm(this),a=this.v?a.u:a.A,this.o?a.insertBefore(this.f,a.childNodes[0]||null):a.appendChild(this.f))};k.render=function(){Wm(this)};k.bl=function(){Wm(this)};
+k.dl=function(){Wm(this);if(void 0!==this.get(Tm)&&this.autoPan){var a=this.oe();if(void 0!==a&&a.Cc()){var b=Xm(a.Cc(),a.nb()),c=this.be(),d=c.offsetWidth,e=c.currentStyle||getComputedStyle(c),d=d+(parseInt(e.marginLeft,10)+parseInt(e.marginRight,10)),e=c.offsetHeight,f=c.currentStyle||getComputedStyle(c),e=e+(parseInt(f.marginTop,10)+parseInt(f.marginBottom,10)),g=Xm(c,[d,e]),c=this.l;Ob(b,g)||(d=g[0]-b[0],e=b[2]-g[2],f=g[1]-b[1],g=b[3]-g[3],b=[0,0],0>d?b[0]=d-c:0>e&&(b[0]=Math.abs(e)+c),0>f?b[1]=
+f-c:0>g&&(b[1]=Math.abs(g)+c),0===b[0]&&0===b[1])||(c=a.aa().fb(),c=a.Ga(c),b=[c[0]+b[0],c[1]+b[1]],a.aa().animate({center:a.Sa(b),duration:this.j.duration,easing:this.j.easing}))}}};k.el=function(){Wm(this)};k.pi=function(a){this.set(Qm,a)};k.setMap=function(a){this.set(Rm,a)};k.xi=function(a){this.set(Sm,a)};k.Ef=function(a){this.set(Tm,a)};function Xm(a,b){var c=a.getBoundingClientRect(),d=c.left+window.pageXOffset,c=c.top+window.pageYOffset;return[d,c,d+b[0],c+b[1]]}
+k.Ai=function(a){this.set(Um,a)};function Ym(a,b){a.a.visible!==b&&(a.f.style.display=b?"":"none",a.a.visible=b)}
+function Wm(a){var b=a.oe(),c=a.qh();if(void 0!==b&&b.a&&void 0!==c){var c=b.Ga(c),d=b.nb(),b=a.f.style,e=a.Tg(),f=a.Ug(),g=e[0],e=e[1];if(f==Zm||f==$m||f==an)""!==a.a.le&&(a.a.le=b.left=""),g=Math.round(d[0]-c[0]-g)+"px",a.a.Je!=g&&(a.a.Je=b.right=g);else{""!==a.a.Je&&(a.a.Je=b.right="");if(f==bn||f==cn||f==dn)g-=a.f.offsetWidth/2;g=Math.round(c[0]+g)+"px";a.a.le!=g&&(a.a.le=b.left=g)}if(f==en||f==bn||f==Zm)""!==a.a.Oe&&(a.a.Oe=b.top=""),c=Math.round(d[1]-c[1]-e)+"px",a.a.Xd!=c&&(a.a.Xd=b.bottom=
+c);else{""!==a.a.Xd&&(a.a.Xd=b.bottom="");if(f==fn||f==cn||f==$m)e-=a.f.offsetHeight/2;c=Math.round(c[1]+e)+"px";a.a.Oe!=c&&(a.a.Oe=b.top=c)}Ym(a,!0)}else Ym(a,!1)}var en="bottom-left",bn="bottom-center",Zm="bottom-right",fn="center-left",cn="center-center",$m="center-right",Vm="top-left",dn="top-center",an="top-right",Qm="element",Rm="map",Sm="offset",Tm="position",Um="positioning";function gn(a){a=a?a:{};this.j=void 0!==a.collapsed?a.collapsed:!0;this.l=void 0!==a.collapsible?a.collapsible:!0;this.l||(this.j=!1);var b=void 0!==a.className?a.className:"ol-overviewmap",c=void 0!==a.tipLabel?a.tipLabel:"Overview map",d=void 0!==a.collapseLabel?a.collapseLabel:"\u00ab";"string"===typeof d?(this.o=document.createElement("span"),this.o.textContent=d):this.o=d;d=void 0!==a.label?a.label:"\u00bb";"string"===typeof d?(this.u=document.createElement("span"),this.u.textContent=d):this.u=
+d;var e=this.l&&!this.j?this.o:this.u,d=document.createElement("button");d.setAttribute("type","button");d.title=c;d.appendChild(e);B(d,"click",this.km,this);c=document.createElement("DIV");c.className="ol-overviewmap-map";var f=this.f=new I({controls:new qe,interactions:new qe,target:c,view:a.view});a.layers&&a.layers.forEach(function(a){f.ug(a)},this);e=document.createElement("DIV");e.className="ol-overviewmap-box";e.style.boxSizing="border-box";this.A=new Pm({position:[0,0],positioning:en,element:e});
+this.f.vg(this.A);e=document.createElement("div");e.className=b+" ol-unselectable ol-control"+(this.j&&this.l?" ol-collapsed":"")+(this.l?"":" ol-uncollapsible");e.appendChild(c);e.appendChild(d);Ie.call(this,{element:e,render:a.render?a.render:hn,target:a.target})}v(gn,Ie);k=gn.prototype;
+k.setMap=function(a){var b=this.a;a!==b&&(b&&(b=b.aa())&&Ea(b,Ua(Id),this.je,this),Ie.prototype.setMap.call(this,a),a&&(this.v.push(B(a,Xa,this.Zk,this)),0===this.f.oh().Ub()&&this.f.vi(a.Bc()),a=a.aa()))&&(B(a,Ua(Id),this.je,this),Sd(a)&&(this.f.ld(),jn(this)))};k.Zk=function(a){a.key===Mm&&((a=a.oldValue)&&Ea(a,Ua(Id),this.je,this),a=this.a.aa(),B(a,Ua(Id),this.je,this))};k.je=function(){this.f.aa().pe(this.a.aa().Ra())};
+function hn(){var a=this.a,b=this.f;if(a.a&&b.a){var c=a.nb(),a=a.aa().Uc(c),d=b.nb(),c=b.aa().Uc(d),e=b.Ga(ac(a)),f=b.Ga(Zb(a)),b=Math.abs(e[0]-f[0]),e=Math.abs(e[1]-f[1]),f=d[0],d=d[1];b<.1*f||e<.1*d||b>.75*f||e>.75*d?jn(this):Ob(c,a)||(a=this.f,c=this.a.aa(),a.aa().Mb(c.fb()))}kn(this)}function jn(a){var b=a.a;a=a.f;var c=b.nb(),b=b.aa().Uc(c),c=a.nb();a=a.aa();kc(b,1/(.1*Math.pow(2,Math.log(7.5)/Math.LN2/2)));a.lf(b,c)}
+function kn(a){var b=a.a,c=a.f;if(b.a&&c.a){var d=b.nb(),e=b.aa(),f=c.aa(),c=e.Ra(),b=a.A,g=a.A.be(),h=e.Uc(d),d=f.Oa(),e=Yb(h),f=$b(h),l;if(a=a.a.aa().fb())l=[e[0]-a[0],e[1]-a[1]],wb(l,c),rb(l,a);b.Ef(l);g&&(g.style.width=Math.abs((e[0]-f[0])/d)+"px",g.style.height=Math.abs((f[1]-e[1])/d)+"px")}}k.km=function(a){a.preventDefault();ln(this)};
+function ln(a){a.element.classList.toggle("ol-collapsed");a.j?Ee(a.o,a.u):Ee(a.u,a.o);a.j=!a.j;var b=a.f;a.j||b.a||(b.ld(),jn(a),Da(b,"postrender",function(){kn(this)},a))}k.jm=function(){return this.l};k.mm=function(a){this.l!==a&&(this.l=a,this.element.classList.toggle("ol-uncollapsible"),!a&&this.j&&ln(this))};k.lm=function(a){this.l&&this.j!==a&&ln(this)};k.im=function(){return this.j};k.wk=function(){return this.f};function mn(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-scale-line";this.l=document.createElement("DIV");this.l.className=b+"-inner";this.f=document.createElement("DIV");this.f.className=b+" ol-unselectable";this.f.appendChild(this.l);this.u=null;this.o=void 0!==a.minWidth?a.minWidth:64;this.j=!1;this.C=void 0;this.A="";Ie.call(this,{element:this.f,render:a.render?a.render:nn,target:a.target});B(this,Ua(on),this.L,this);this.D(a.units||pn)}v(mn,Ie);var qn=[1,2,5];mn.prototype.Eb=function(){return this.get(on)};
+function nn(a){(a=a.frameState)?this.u=a.viewState:this.u=null;rn(this)}mn.prototype.L=function(){rn(this)};mn.prototype.D=function(a){this.set(on,a)};
+function rn(a){var b=a.u;if(b){var c=b.projection,d=c.ic(),b=xc(c,b.resolution,b.center)*d,d=a.o*b,c="",e=a.Eb();e==sn?(c=qc.degrees,b/=c,d<c/60?(c="\u2033",b*=3600):d<c?(c="\u2032",b*=60):c="\u00b0"):e==tn?.9144>d?(c="in",b/=.0254):1609.344>d?(c="ft",b/=.3048):(c="mi",b/=1609.344):e==un?(b/=1852,c="nm"):e==pn?1>d?(c="mm",b*=1E3):1E3>d?c="m":(c="km",b/=1E3):e==vn?.9144>d?(c="in",b*=39.37):1609.344>d?(c="ft",b/=.30480061):(c="mi",b/=1609.3472):ha(!1,33);for(var e=3*Math.floor(Math.log(a.o*b)/Math.log(10)),
+f;;){f=qn[(e%3+3)%3]*Math.pow(10,Math.floor(e/3));d=Math.round(f/b);if(isNaN(d)){a.f.style.display="none";a.j=!1;return}if(d>=a.o)break;++e}b=f+" "+c;a.A!=b&&(a.l.innerHTML=b,a.A=b);a.C!=d&&(a.l.style.width=d+"px",a.C=d);a.j||(a.f.style.display="",a.j=!0)}else a.j&&(a.f.style.display="none",a.j=!1)}var on="units",sn="degrees",tn="imperial",un="nautical",pn="metric",vn="us";function wn(a){a=a?a:{};this.f=void 0;this.j=xn;this.u=[];this.C=this.o=0;this.U=null;this.fa=!1;this.Z=void 0!==a.duration?a.duration:200;var b=void 0!==a.className?a.className:"ol-zoomslider",c=document.createElement("button");c.setAttribute("type","button");c.className=b+"-thumb ol-unselectable";var d=document.createElement("div");d.className=b+" ol-unselectable ol-control";d.appendChild(c);this.l=new Tf(d);B(this.l,"pointerdown",this.Nk,this);B(this.l,"pointermove",this.Xg,this);B(this.l,"pointerup",
+this.Yg,this);B(d,"click",this.Mk,this);B(c,"click",Ka);Ie.call(this,{element:d,render:a.render?a.render:yn})}v(wn,Ie);wn.prototype.oa=function(){Ha(this.l);Ie.prototype.oa.call(this)};var xn=0;k=wn.prototype;k.setMap=function(a){Ie.prototype.setMap.call(this,a);a&&a.render()};
+function yn(a){if(a.frameState){if(!this.fa){var b=this.element,c=b.offsetWidth,d=b.offsetHeight,e=b.firstElementChild,f=getComputedStyle(e),b=e.offsetWidth+parseFloat(f.marginRight)+parseFloat(f.marginLeft),e=e.offsetHeight+parseFloat(f.marginTop)+parseFloat(f.marginBottom);this.U=[b,e];c>d?(this.j=1,this.C=c-b):(this.j=xn,this.o=d-e);this.fa=!0}a=a.frameState.viewState.resolution;a!==this.f&&(this.f=a,zn(this,a))}}
+k.Mk=function(a){var b=this.a.aa();a=An(this,ia(1===this.j?(a.offsetX-this.U[0]/2)/this.C:(a.offsetY-this.U[1]/2)/this.o,0,1));b.animate({resolution:b.constrainResolution(a),duration:this.Z,easing:Cb})};
+k.Nk=function(a){if(!this.A&&a.b.target===this.element.firstElementChild&&(Jd(this.a.aa(),1,1),this.D=a.clientX,this.L=a.clientY,this.A=!0,0===this.u.length)){a=this.Xg;var b=this.Yg;this.u.push(B(document,"mousemove",a,this),B(document,"touchmove",a,this),B(document,"pointermove",a,this),B(document,"mouseup",b,this),B(document,"touchend",b,this),B(document,"pointerup",b,this))}};
+k.Xg=function(a){if(this.A){var b=this.element.firstElementChild;this.f=An(this,ia(1===this.j?(a.clientX-this.D+parseInt(b.style.left,10))/this.C:(a.clientY-this.L+parseInt(b.style.top,10))/this.o,0,1));this.a.aa().Oc(this.f);zn(this,this.f);this.D=a.clientX;this.L=a.clientY}};k.Yg=function(){if(this.A){var a=this.a.aa();Jd(a,1,-1);a.animate({resolution:a.constrainResolution(this.f),duration:this.Z,easing:Cb});this.A=!1;this.L=this.D=void 0;this.u.forEach(ya);this.u.length=0}};
+function zn(a,b){var c;c=1-Rd(a.a.aa())(b);var d=a.element.firstElementChild;1==a.j?d.style.left=a.C*c+"px":d.style.top=a.o*c+"px"}function An(a,b){return Qd(a.a.aa())(1-b)};function Bn(a){a=a?a:{};this.f=a.extent?a.extent:null;var b=void 0!==a.className?a.className:"ol-zoom-extent",c=void 0!==a.label?a.label:"E",d=void 0!==a.tipLabel?a.tipLabel:"Fit to extent",e=document.createElement("button");e.setAttribute("type","button");e.title=d;e.appendChild("string"===typeof c?document.createTextNode(c):c);B(e,"click",this.j,this);c=document.createElement("div");c.className=b+" ol-unselectable ol-control";c.appendChild(e);Ie.call(this,{element:c,target:a.target})}v(Bn,Ie);
+Bn.prototype.j=function(a){a.preventDefault();var b=this.a;a=b.aa();var c=this.f?this.f:a.o.G(),b=b.nb();a.lf(c,b)};function Cn(a){Sa.call(this);a=a?a:{};this.a=null;B(this,Ua(Dn),this.Kl,this);this.Cf(void 0!==a.tracking?a.tracking:!1)}v(Cn,Sa);k=Cn.prototype;k.oa=function(){this.Cf(!1);Sa.prototype.oa.call(this)};
+k.io=function(a){if(null!==a.alpha){var b=na(a.alpha);this.set(En,b);"boolean"===typeof a.absolute&&a.absolute?this.set(Fn,b):"number"===typeof a.webkitCompassHeading&&-1!=a.webkitCompassAccuracy&&this.set(Fn,na(a.webkitCompassHeading))}null!==a.beta&&this.set(Gn,na(a.beta));null!==a.gamma&&this.set(Hn,na(a.gamma));this.s()};k.Vj=function(){return this.get(En)};k.Yj=function(){return this.get(Gn)};k.ek=function(){return this.get(Hn)};k.Jl=function(){return this.get(Fn)};k.kh=function(){return this.get(Dn)};
+k.Kl=function(){if(pf){var a=this.kh();a&&!this.a?this.a=B(window,"deviceorientation",this.io,this):a||null===this.a||(ya(this.a),this.a=null)}};k.Cf=function(a){this.set(Dn,a)};var En="alpha",Gn="beta",Hn="gamma",Fn="heading",Dn="tracking";function J(a){Sa.call(this);this.f=void 0;this.a="geometry";this.i=null;this.j=void 0;this.c=null;B(this,Ua(this.a),this.he,this);void 0!==a&&(a instanceof Rc||!a?this.Pa(a):this.I(a))}v(J,Sa);k=J.prototype;k.clone=function(){var a=new J(this.R());a.Nc(this.a);var b=this.V();b&&a.Pa(b.clone());(b=this.i)&&a.Df(b);return a};k.V=function(){return this.get(this.a)};k.Ll=function(){return this.f};k.gk=function(){return this.a};k.Ml=function(){return this.i};k.Gc=function(){return this.j};k.Nl=function(){this.s()};
+k.he=function(){this.c&&(ya(this.c),this.c=null);var a=this.V();a&&(this.c=B(a,"change",this.Nl,this));this.s()};k.Pa=function(a){this.set(this.a,a)};k.Df=function(a){this.j=(this.i=a)?In(a):void 0;this.s()};k.cc=function(a){this.f=a;this.s()};k.Nc=function(a){Ea(this,Ua(this.a),this.he,this);this.a=a;B(this,Ua(this.a),this.he,this);this.he()};function In(a){if("function"!==typeof a){var b;Array.isArray(a)?b=a:(ha(a instanceof xi,41),b=[a]);a=function(){return b}}return a};var Jn=document.implementation.createDocument("","",null);function Kn(a,b){return Jn.createElementNS(a,b)}function Ln(a,b){return Mn(a,b,[]).join("")}function Mn(a,b,c){if(a.nodeType==Node.CDATA_SECTION_NODE||a.nodeType==Node.TEXT_NODE)b?c.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):c.push(a.nodeValue);else for(a=a.firstChild;a;a=a.nextSibling)Mn(a,b,c);return c}function Nn(a){return a instanceof Document}function On(a){return a instanceof Node}
+function Pn(a){return(new DOMParser).parseFromString(a,"application/xml")}function Qn(a,b){return function(c,d){var e=a.call(b,c,d);void 0!==e&&bb(d[d.length-1],e)}}function Rn(a,b){return function(c,d){var e=a.call(void 0!==b?b:this,c,d);void 0!==e&&d[d.length-1].push(e)}}function Sn(a,b){return function(c,d){var e=a.call(void 0!==b?b:this,c,d);void 0!==e&&(d[d.length-1]=e)}}
+function Tn(a){return function(b,c){var d=a.call(this,b,c);if(void 0!==d){var e=c[c.length-1],f=b.localName,g;f in e?g=e[f]:g=e[f]=[];g.push(d)}}}function K(a,b){return function(c,d){var e=a.call(this,c,d);void 0!==e&&(d[d.length-1][void 0!==b?b:c.localName]=e)}}function L(a,b){return function(c,d,e){a.call(void 0!==b?b:this,c,d,e);e[e.length-1].node.appendChild(c)}}
+function Un(a){var b,c;return function(d,e,f){if(!b){b={};var g={};g[d.localName]=a;b[d.namespaceURI]=g;c=Vn(d.localName)}Wn(b,c,e,f)}}function Vn(a,b){return function(c,d,e){c=d[d.length-1].node;d=a;void 0===d&&(d=e);e=b;void 0===b&&(e=c.namespaceURI);return Kn(e,d)}}var Xn=Vn();function Yn(a,b){for(var c=b.length,d=Array(c),e=0;e<c;++e)d[e]=a[b[e]];return d}function M(a,b,c){c=void 0!==c?c:{};var d,e;d=0;for(e=a.length;d<e;++d)c[a[d]]=b;return c}
+function Zn(a,b,c,d){for(b=b.firstElementChild;b;b=b.nextElementSibling){var e=a[b.namespaceURI];void 0!==e&&(e=e[b.localName])&&e.call(d,b,c)}}function N(a,b,c,d,e){d.push(a);Zn(b,c,d,e);return d.pop()}function Wn(a,b,c,d,e,f){for(var g=(void 0!==e?e:c).length,h,l,m=0;m<g;++m)h=c[m],void 0!==h&&(l=b.call(f,h,d,void 0!==e?e[m]:void 0),void 0!==l&&a[l.namespaceURI][l.localName].call(f,l,h,d))}function $n(a,b,c,d,e,f,g){e.push(a);Wn(b,c,d,e,f,g);e.pop()};function ao(a,b,c,d){return function(e,f,g){var h=new XMLHttpRequest;h.open("GET","function"===typeof a?a(e,f,g):a,!0);"arraybuffer"==b.Y()&&(h.responseType="arraybuffer");h.onload=function(){if(!h.status||200<=h.status&&300>h.status){var a=b.Y(),e;"json"==a||"text"==a?e=h.responseText:"xml"==a?(e=h.responseXML)||(e=Pn(h.responseText)):"arraybuffer"==a&&(e=h.response);e?c.call(this,b.La(e,{featureProjection:g}),b.Wa(e)):d.call(this)}else d.call(this)}.bind(this);h.send()}}
+function bo(a,b){return ao(a,b,function(a){this.Tc(a)},ea)};function co(){this.j=this.defaultDataProjection=null}function eo(a,b,c){var d;c&&(d={dataProjection:c.dataProjection?c.dataProjection:a.Wa(b),featureProjection:c.featureProjection});return fo(a,d)}function fo(a,b){return ta({dataProjection:a.defaultDataProjection,featureProjection:a.j},b)}
+function go(a,b,c){var d=c?zc(c.featureProjection):null,e=c?zc(c.dataProjection):null,f;d&&e&&!Mc(d,e)?a instanceof Rc?f=(b?a.clone():a).ob(b?d:e,b?e:d):f=Qc(b?a.slice():a,b?d:e,b?e:d):f=a;if(b&&c&&c.decimals){var g=Math.pow(10,c.decimals);a=function(a){for(var b=0,c=a.length;b<c;++b)a[b]=Math.round(a[b]*g)/g;return a};Array.isArray(f)?a(f):f.sc(a)}return f};function ho(){co.call(this)}v(ho,co);function io(a){return"string"===typeof a?(a=JSON.parse(a))?a:null:null!==a?a:null}k=ho.prototype;k.Y=function(){return"json"};k.bc=function(a,b){return this.gd(io(a),eo(this,a,b))};k.La=function(a,b){return this.Sf(io(a),eo(this,a,b))};k.hd=function(a,b){return this.ci(io(a),eo(this,a,b))};k.Wa=function(a){return this.ii(io(a))};k.Od=function(a,b){return JSON.stringify(this.md(a,b))};k.ec=function(a,b){return JSON.stringify(this.Se(a,b))};
+k.od=function(a,b){return JSON.stringify(this.Te(a,b))};function jo(a,b,c,d,e,f){var g=NaN,h=NaN,l=(c-b)/d;if(0!==l)if(1==l)g=a[b],h=a[b+1];else if(2==l)g=(1-e)*a[b]+e*a[b+d],h=(1-e)*a[b+1]+e*a[b+d+1];else{var h=a[b],l=a[b+1],m=0,g=[0],p;for(p=b+d;p<c;p+=d){var n=a[p],q=a[p+1],m=m+Math.sqrt((n-h)*(n-h)+(q-l)*(q-l));g.push(m);h=n;l=q}c=e*m;l=0;m=g.length;for(p=!1;l<m;)e=l+(m-l>>1),h=+Ya(g[e],c),0>h?l=e+1:(m=e,p=!h);e=p?l:~l;0>e?(c=(c-g[-e-2])/(g[-e-1]-g[-e-2]),b+=(-e-2)*d,g=pa(a[b],a[b+d],c),h=pa(a[b+1],a[b+d+1],c)):(g=a[b+e*d],h=a[b+e*d+1])}return f?(f[0]=
+g,f[1]=h,f):[g,h]}function ko(a,b,c,d,e,f){if(c==b)return null;if(e<a[b+d-1])return f?(c=a.slice(b,b+d),c[d-1]=e,c):null;if(a[c-1]<e)return f?(c=a.slice(c-d,c),c[d-1]=e,c):null;if(e==a[b+d-1])return a.slice(b,b+d);b/=d;for(c/=d;b<c;)f=b+c>>1,e<a[(f+1)*d-1]?c=f:b=f+1;c=a[b*d-1];if(e==c)return a.slice((b-1)*d,(b-1)*d+d);f=(e-c)/(a[(b+1)*d-1]-c);c=[];var g;for(g=0;g<d-1;++g)c.push(pa(a[(b-1)*d+g],a[b*d+g],f));c.push(e);return c}
+function lo(a,b,c,d,e,f){var g=0;if(f)return ko(a,g,b[b.length-1],c,d,e);if(d<a[c-1])return e?(a=a.slice(0,c),a[c-1]=d,a):null;if(a[a.length-1]<d)return e?(a=a.slice(a.length-c),a[c-1]=d,a):null;e=0;for(f=b.length;e<f;++e){var h=b[e];if(g!=h){if(d<a[g+c-1])break;if(d<=a[h-1])return ko(a,g,h,c,d,!1);g=h}}return null};function P(a,b){Uc.call(this);this.c=null;this.A=this.C=this.l=-1;this.qa(a,b)}v(P,Uc);k=P.prototype;k.Dj=function(a){this.B?bb(this.B,a):this.B=a.slice();this.s()};k.clone=function(){var a=new P(null);a.da(this.ka,this.B.slice());return a};k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;this.A!=this.g&&(this.C=Math.sqrt(bd(this.B,0,this.B.length,this.a,0)),this.A=this.g);return dd(this.B,0,this.B.length,this.a,this.C,!1,a,b,c,d)};
+k.Sj=function(a,b){return td(this.B,0,this.B.length,this.a,a,b)};k.pm=function(a,b){return"XYM"!=this.ka&&"XYZM"!=this.ka?null:ko(this.B,0,this.B.length,this.a,a,void 0!==b?b:!1)};k.$=function(){return id(this.B,0,this.B.length,this.a)};k.Kg=function(a,b){return jo(this.B,0,this.B.length,this.a,a,b)};k.qm=function(){var a=this.B,b=this.a,c=a[0],d=a[1],e=0,f;for(f=0+b;f<this.B.length;f+=b)var g=a[f],h=a[f+1],e=e+Math.sqrt((g-c)*(g-c)+(h-d)*(h-d)),c=g,d=h;return e};
+function Qi(a){a.l!=a.g&&(a.c=a.Kg(.5,a.c),a.l=a.g);return a.c}k.$c=function(a){var b=[];b.length=kd(this.B,0,this.B.length,this.a,a,b,0);a=new P(null);a.da("XY",b);return a};k.Y=function(){return"LineString"};k.Ta=function(a){return ud(this.B,0,this.B.length,this.a,a)};k.qa=function(a,b){a?(Yc(this,b,a,1),this.B||(this.B=[]),this.B.length=gd(this.B,0,a,this.a),this.s()):this.da("XY",null)};k.da=function(a,b){Xc(this,a,b);this.s()};function Q(a,b){Uc.call(this);this.c=[];this.l=this.A=-1;this.qa(a,b)}v(Q,Uc);k=Q.prototype;k.Ej=function(a){this.B?bb(this.B,a.ia().slice()):this.B=a.ia().slice();this.c.push(this.B.length);this.s()};k.clone=function(){var a=new Q(null);a.da(this.ka,this.B.slice(),this.c.slice());return a};k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;this.l!=this.g&&(this.A=Math.sqrt(cd(this.B,0,this.c,this.a,0)),this.l=this.g);return ed(this.B,0,this.c,this.a,this.A,!1,a,b,c,d)};
+k.sm=function(a,b,c){return"XYM"!=this.ka&&"XYZM"!=this.ka||0===this.B.length?null:lo(this.B,this.c,this.a,a,void 0!==b?b:!1,void 0!==c?c:!1)};k.$=function(){return jd(this.B,0,this.c,this.a)};k.Kb=function(){return this.c};k.mk=function(a){if(0>a||this.c.length<=a)return null;var b=new P(null);b.da(this.ka,this.B.slice(0===a?0:this.c[a-1],this.c[a]));return b};
+k.Yc=function(){var a=this.B,b=this.c,c=this.ka,d=[],e=0,f,g;f=0;for(g=b.length;f<g;++f){var h=b[f],l=new P(null);l.da(c,a.slice(e,h));d.push(l);e=h}return d};function Ri(a){var b=[],c=a.B,d=0,e=a.c;a=a.a;var f,g;f=0;for(g=e.length;f<g;++f){var h=e[f],d=jo(c,d,h,a,.5);bb(b,d);d=h}return b}k.$c=function(a){var b=[],c=[],d=this.B,e=this.c,f=this.a,g=0,h=0,l,m;l=0;for(m=e.length;l<m;++l){var p=e[l],h=kd(d,g,p,f,a,b,h);c.push(h);g=p}b.length=h;a=new Q(null);a.da("XY",b,c);return a};k.Y=function(){return"MultiLineString"};
+k.Ta=function(a){a:{var b=this.B,c=this.c,d=this.a,e=0,f,g;f=0;for(g=c.length;f<g;++f){if(ud(b,e,c[f],d,a)){a=!0;break a}e=c[f]}a=!1}return a};k.qa=function(a,b){if(a){Yc(this,b,a,2);this.B||(this.B=[]);var c=hd(this.B,0,a,this.a,this.c);this.B.length=0===c.length?0:c[c.length-1];this.s()}else this.da("XY",null,this.c)};k.da=function(a,b,c){Xc(this,a,b);this.c=c;this.s()};
+function mo(a,b){var c=a.ka,d=[],e=[],f,g;f=0;for(g=b.length;f<g;++f){var h=b[f];0===f&&(c=h.ka);bb(d,h.ia());e.push(d.length)}a.da(c,d,e)};function R(a,b){Uc.call(this);this.qa(a,b)}v(R,Uc);k=R.prototype;k.Gj=function(a){this.B?bb(this.B,a.ia()):this.B=a.ia().slice();this.s()};k.clone=function(){var a=new R(null);a.da(this.ka,this.B.slice());return a};k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;var e=this.B,f=this.a,g,h,l;g=0;for(h=e.length;g<h;g+=f)if(l=ma(a,b,e[g],e[g+1]),l<d){d=l;for(l=0;l<f;++l)c[l]=e[g+l];c.length=f}return d};k.$=function(){return id(this.B,0,this.B.length,this.a)};
+k.yk=function(a){var b=this.B?this.B.length/this.a:0;if(0>a||b<=a)return null;b=new C(null);b.da(this.ka,this.B.slice(a*this.a,(a+1)*this.a));return b};k.re=function(){var a=this.B,b=this.ka,c=this.a,d=[],e,f;e=0;for(f=a.length;e<f;e+=c){var g=new C(null);g.da(b,a.slice(e,e+c));d.push(g)}return d};k.Y=function(){return"MultiPoint"};k.Ta=function(a){var b=this.B,c=this.a,d,e,f,g;d=0;for(e=b.length;d<e;d+=c)if(f=b[d],g=b[d+1],Nb(a,f,g))return!0;return!1};
+k.qa=function(a,b){a?(Yc(this,b,a,1),this.B||(this.B=[]),this.B.length=gd(this.B,0,a,this.a),this.s()):this.da("XY",null)};k.da=function(a,b){Xc(this,a,b);this.s()};function S(a,b){Uc.call(this);this.c=[];this.A=-1;this.C=null;this.P=this.D=this.L=-1;this.l=null;this.qa(a,b)}v(S,Uc);k=S.prototype;k.Hj=function(a){if(this.B){var b=this.B.length;bb(this.B,a.ia());a=a.Kb().slice();var c,d;c=0;for(d=a.length;c<d;++c)a[c]+=b}else this.B=a.ia().slice(),a=a.Kb().slice(),this.c.push();this.c.push(a);this.s()};k.clone=function(){for(var a=new S(null),b=this.c.length,c=Array(b),d=0;d<b;++d)c[d]=this.c[d].slice();no(a,this.ka,this.B.slice(),c);return a};
+k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;if(this.D!=this.g){var e=this.c,f=0,g=0,h,l;h=0;for(l=e.length;h<l;++h)var m=e[h],g=cd(this.B,f,m,this.a,g),f=m[m.length-1];this.L=Math.sqrt(g);this.D=this.g}e=Si(this);f=this.c;g=this.a;h=this.L;l=0;var m=[NaN,NaN],p,n;p=0;for(n=f.length;p<n;++p){var q=f[p];d=ed(e,l,q,g,h,!0,a,b,c,d,m);l=q[q.length-1]}return d};
+k.Hc=function(a,b){var c;a:{c=Si(this);var d=this.c,e=this.a,f=0;if(0!==d.length){var g,h;g=0;for(h=d.length;g<h;++g){var l=d[g];if(rd(c,f,l,e,a,b)){c=!0;break a}f=l[l.length-1]}}c=!1}return c};k.tm=function(){var a=Si(this),b=this.c,c=0,d=0,e,f;e=0;for(f=b.length;e<f;++e)var g=b[e],d=d+$c(a,c,g,this.a),c=g[g.length-1];return d};
+k.$=function(a){var b;void 0!==a?(b=Si(this).slice(),zd(b,this.c,this.a,a)):b=this.B;a=b;b=this.c;var c=this.a,d=0,e=[],f=0,g,h;g=0;for(h=b.length;g<h;++g){var l=b[g];e[f++]=jd(a,d,l,c,e[f]);d=l[l.length-1]}e.length=f;return e};
+function Ti(a){if(a.A!=a.g){var b=a.B,c=a.c,d=a.a,e=0,f=[],g,h;g=0;for(h=c.length;g<h;++g){var l=c[g],e=Tb(b,e,l[0],d);f.push((e[0]+e[2])/2,(e[1]+e[3])/2);e=l[l.length-1]}b=Si(a);c=a.c;d=a.a;g=0;h=[];l=0;for(e=c.length;l<e;++l){var m=c[l];h=sd(b,g,m,d,f,2*l,h);g=m[m.length-1]}a.C=h;a.A=a.g}return a.C}k.jk=function(){var a=new R(null);a.da("XY",Ti(this).slice());return a};
+function Si(a){if(a.P!=a.g){var b=a.B,c;a:{c=a.c;var d,e;d=0;for(e=c.length;d<e;++d)if(!xd(b,c[d],a.a,void 0)){c=!1;break a}c=!0}c?a.l=b:(a.l=b.slice(),a.l.length=zd(a.l,a.c,a.a));a.P=a.g}return a.l}k.$c=function(a){var b=[],c=[],d=this.B,e=this.c,f=this.a;a=Math.sqrt(a);var g=0,h=0,l,m;l=0;for(m=e.length;l<m;++l){var p=e[l],n=[],h=ld(d,g,p,f,a,b,h,n);c.push(n);g=p[p.length-1]}b.length=h;d=new S(null);no(d,"XY",b,c);return d};
+k.zk=function(a){if(0>a||this.c.length<=a)return null;var b;0===a?b=0:(b=this.c[a-1],b=b[b.length-1]);a=this.c[a].slice();var c=a[a.length-1];if(0!==b){var d,e;d=0;for(e=a.length;d<e;++d)a[d]-=b}d=new E(null);d.da(this.ka,this.B.slice(b,c),a);return d};k.Ad=function(){var a=this.ka,b=this.B,c=this.c,d=[],e=0,f,g,h,l;f=0;for(g=c.length;f<g;++f){var m=c[f].slice(),p=m[m.length-1];if(0!==e)for(h=0,l=m.length;h<l;++h)m[h]-=e;h=new E(null);h.da(a,b.slice(e,p),m);d.push(h);e=p}return d};k.Y=function(){return"MultiPolygon"};
+k.Ta=function(a){a:{var b=Si(this),c=this.c,d=this.a,e=0,f,g;f=0;for(g=c.length;f<g;++f){var h=c[f];if(vd(b,e,h,d,a)){a=!0;break a}e=h[h.length-1]}a=!1}return a};k.qa=function(a,b){if(a){Yc(this,b,a,3);this.B||(this.B=[]);var c=this.B,d=this.a,e=this.c,f=0,e=e?e:[],g=0,h,l;h=0;for(l=a.length;h<l;++h)f=hd(c,f,a[h],d,e[g]),e[g++]=f,f=f[f.length-1];e.length=g;0===e.length?this.B.length=0:(c=e[e.length-1],this.B.length=0===c.length?0:c[c.length-1]);this.s()}else no(this,"XY",null,this.c)};
+function no(a,b,c,d){Xc(a,b,c);a.c=d;a.s()}function oo(a,b){var c=a.ka,d=[],e=[],f,g,h;f=0;for(g=b.length;f<g;++f){var l=b[f];0===f&&(c=l.ka);var m=d.length;h=l.Kb();var p,n;p=0;for(n=h.length;p<n;++p)h[p]+=m;bb(d,l.ia());e.push(h)}no(a,c,d,e)};function po(a){a=a?a:{};co.call(this);this.b=a.geometryName}v(po,ho);
+function qo(a,b){if(!a)return null;var c;if("number"===typeof a.x&&"number"===typeof a.y)c="Point";else if(a.points)c="MultiPoint";else if(a.paths)c=1===a.paths.length?"LineString":"MultiLineString";else if(a.rings){var d=a.rings,e=ro(a),f=[];c=[];var g,h;g=0;for(h=d.length;g<h;++g){var l=ab(d[g]);wd(l,0,l.length,e.length)?f.push([d[g]]):c.push(d[g])}for(;c.length;){d=c.shift();e=!1;for(g=f.length-1;0<=g;g--)if(Ob((new md(f[g][0])).G(),(new md(d)).G())){f[g].push(d);e=!0;break}e||f.push([d.reverse()])}a=
+ta({},a);1===f.length?(c="Polygon",a.rings=f[0]):(c="MultiPolygon",a.rings=f)}return go((0,so[c])(a),!1,b)}function ro(a){var b="XY";!0===a.hasZ&&!0===a.hasM?b="XYZM":!0===a.hasZ?b="XYZ":!0===a.hasM&&(b="XYM");return b}function to(a){a=a.ka;return{hasZ:"XYZ"===a||"XYZM"===a,hasM:"XYM"===a||"XYZM"===a}}
+var so={Point:function(a){return void 0!==a.m&&void 0!==a.z?new C([a.x,a.y,a.z,a.m],"XYZM"):void 0!==a.z?new C([a.x,a.y,a.z],"XYZ"):void 0!==a.m?new C([a.x,a.y,a.m],"XYM"):new C([a.x,a.y])},LineString:function(a){return new P(a.paths[0],ro(a))},Polygon:function(a){return new E(a.rings,ro(a))},MultiPoint:function(a){return new R(a.points,ro(a))},MultiLineString:function(a){return new Q(a.paths,ro(a))},MultiPolygon:function(a){return new S(a.rings,ro(a))}},uo={Point:function(a){var b=a.$(),c;a=a.ka;
+"XYZ"===a?c={x:b[0],y:b[1],z:b[2]}:"XYM"===a?c={x:b[0],y:b[1],m:b[2]}:"XYZM"===a?c={x:b[0],y:b[1],z:b[2],m:b[3]}:"XY"===a?c={x:b[0],y:b[1]}:ha(!1,34);return c},LineString:function(a){var b=to(a);return{hasZ:b.hasZ,hasM:b.hasM,paths:[a.$()]}},Polygon:function(a){var b=to(a);return{hasZ:b.hasZ,hasM:b.hasM,rings:a.$(!1)}},MultiPoint:function(a){var b=to(a);return{hasZ:b.hasZ,hasM:b.hasM,points:a.$()}},MultiLineString:function(a){var b=to(a);return{hasZ:b.hasZ,hasM:b.hasM,paths:a.$()}},MultiPolygon:function(a){var b=
+to(a);a=a.$(!1);for(var c=[],d=0;d<a.length;d++)for(var e=a[d].length-1;0<=e;e--)c.push(a[d][e]);return{hasZ:b.hasZ,hasM:b.hasM,rings:c}}};k=po.prototype;k.gd=function(a,b){var c=qo(a.geometry,b),d=new J;this.b&&d.Nc(this.b);d.Pa(c);b&&b.zf&&a.attributes[b.zf]&&d.cc(a.attributes[b.zf]);a.attributes&&d.I(a.attributes);return d};
+k.Sf=function(a,b){var c=b?b:{};if(a.features){var d=[],e=a.features,f,g;c.zf=a.objectIdFieldName;f=0;for(g=e.length;f<g;++f)d.push(this.gd(e[f],c));return d}return[this.gd(a,c)]};k.ci=function(a,b){return qo(a,b)};k.ii=function(a){return a.spatialReference&&a.spatialReference.wkid?zc("EPSG:"+a.spatialReference.wkid):null};function vo(a,b){return(0,uo[a.Y()])(go(a,!0,b),b)}k.Te=function(a,b){return vo(a,fo(this,b))};
+k.md=function(a,b){b=fo(this,b);var c={},d=a.V();d&&(c.geometry=vo(d,b));d=a.R();delete d[a.a];c.attributes=wa(d)?{}:d;b&&b.featureProjection&&(c.spatialReference={wkid:zc(b.featureProjection).hb.split(":").pop()});return c};k.Se=function(a,b){b=fo(this,b);var c=[],d,e;d=0;for(e=a.length;d<e;++d)c.push(this.md(a[d],b));return{features:c}};function wo(a){this.Nb=a};function zo(a){this.Nb=a}v(zo,wo);function Ao(a,b,c){this.Nb=a;this.b=b;this.a=c}v(Ao,zo);function Bo(a,b){Ao.call(this,"And",a,b)}v(Bo,Ao);function Co(a,b,c){this.Nb="BBOX";this.geometryName=a;this.extent=b;this.srsName=c}v(Co,wo);function Do(a,b){this.Nb=a;this.b=b}v(Do,wo);function Eo(a,b,c,d){Do.call(this,a,b);this.g=c;this.a=d}v(Eo,Do);function Fo(a,b,c){Eo.call(this,"PropertyIsEqualTo",a,b,c)}v(Fo,Eo);function Go(a,b){Eo.call(this,"PropertyIsGreaterThan",a,b)}v(Go,Eo);function Ho(a,b){Eo.call(this,"PropertyIsGreaterThanOrEqualTo",a,b)}v(Ho,Eo);function Io(a,b,c,d){this.Nb=a;this.geometryName=b||"the_geom";this.geometry=c;this.srsName=d}v(Io,wo);function Jo(a,b,c){Io.call(this,"Intersects",a,b,c)}v(Jo,Io);function Ko(a,b,c){Do.call(this,"PropertyIsBetween",a);this.a=b;this.g=c}v(Ko,Do);function Lo(a,b,c,d,e,f){Do.call(this,"PropertyIsLike",a);this.f=b;this.i=void 0!==c?c:"*";this.c=void 0!==d?d:".";this.g=void 0!==e?e:"!";this.a=f}v(Lo,Do);function Mo(a){Do.call(this,"PropertyIsNull",a)}v(Mo,Do);function No(a,b){Eo.call(this,"PropertyIsLessThan",a,b)}v(No,Eo);function Oo(a,b){Eo.call(this,"PropertyIsLessThanOrEqualTo",a,b)}v(Oo,Eo);function Po(a){this.Nb="Not";this.condition=a}v(Po,zo);function Qo(a,b,c){Eo.call(this,"PropertyIsNotEqualTo",a,b,c)}v(Qo,Eo);function Ro(a,b){Ao.call(this,"Or",a,b)}v(Ro,Ao);function So(a,b,c){Io.call(this,"Within",a,b,c)}v(So,Io);function To(a,b){return new Bo(a,b)}function Uo(a,b,c){return new Co(a,b,c)};function Vo(a){Rc.call(this);this.f=a?a:null;Wo(this)}v(Vo,Rc);function Xo(a){var b=[],c,d;c=0;for(d=a.length;c<d;++c)b.push(a[c].clone());return b}function Yo(a){var b,c;if(a.f)for(b=0,c=a.f.length;b<c;++b)Ea(a.f[b],"change",a.s,a)}function Wo(a){var b,c;if(a.f)for(b=0,c=a.f.length;b<c;++b)B(a.f[b],"change",a.s,a)}k=Vo.prototype;k.clone=function(){var a=new Vo(null);a.ti(this.f);return a};
+k.Ab=function(a,b,c,d){if(d<Lb(this.G(),a,b))return d;var e=this.f,f,g;f=0;for(g=e.length;f<g;++f)d=e[f].Ab(a,b,c,d);return d};k.Hc=function(a,b){var c=this.f,d,e;d=0;for(e=c.length;d<e;++d)if(c[d].Hc(a,b))return!0;return!1};k.Yd=function(a){Rb(Infinity,Infinity,-Infinity,-Infinity,a);for(var b=this.f,c=0,d=b.length;c<d;++c)Wb(a,b[c].G());return a};k.pf=function(){return Xo(this.f)};
+k.Bd=function(a){this.o!=this.g&&(ua(this.i),this.j=0,this.o=this.g);if(0>a||0!==this.j&&a<this.j)return this;var b=a.toString();if(this.i.hasOwnProperty(b))return this.i[b];var c=[],d=this.f,e=!1,f,g;f=0;for(g=d.length;f<g;++f){var h=d[f],l=h.Bd(a);c.push(l);l!==h&&(e=!0)}if(e)return a=new Vo(null),Yo(a),a.f=c,Wo(a),a.s(),this.i[b]=a;this.j=a;return this};k.Y=function(){return"GeometryCollection"};k.Ta=function(a){var b=this.f,c,d;c=0;for(d=b.length;c<d;++c)if(b[c].Ta(a))return!0;return!1};
+k.rotate=function(a,b){for(var c=this.f,d=0,e=c.length;d<e;++d)c[d].rotate(a,b);this.s()};k.scale=function(a,b,c){c||(c=gc(this.G()));for(var d=this.f,e=0,f=d.length;e<f;++e)d[e].scale(a,b,c);this.s()};k.ti=function(a){a=Xo(a);Yo(this);this.f=a;Wo(this);this.s()};k.sc=function(a){var b=this.f,c,d;c=0;for(d=b.length;c<d;++c)b[c].sc(a);this.s()};k.translate=function(a,b){var c=this.f,d,e;d=0;for(e=c.length;d<e;++d)c[d].translate(a,b);this.s()};k.oa=function(){Yo(this);Rc.prototype.oa.call(this)};function Zo(a){a=a?a:{};co.call(this);this.defaultDataProjection=zc(a.defaultDataProjection?a.defaultDataProjection:"EPSG:4326");a.featureProjection&&(this.j=zc(a.featureProjection));this.b=a.geometryName}v(Zo,ho);function $o(a,b){return a?go((0,ap[a.type])(a),!1,b):null}function bp(a,b){return(0,cp[a.Y()])(go(a,!0,b),b)}
+var ap={Point:function(a){return new C(a.coordinates)},LineString:function(a){return new P(a.coordinates)},Polygon:function(a){return new E(a.coordinates)},MultiPoint:function(a){return new R(a.coordinates)},MultiLineString:function(a){return new Q(a.coordinates)},MultiPolygon:function(a){return new S(a.coordinates)},GeometryCollection:function(a,b){var c=a.geometries.map(function(a){return $o(a,b)});return new Vo(c)}},cp={Point:function(a){return{type:"Point",coordinates:a.$()}},LineString:function(a){return{type:"LineString",
+coordinates:a.$()}},Polygon:function(a,b){var c;b&&(c=b.rightHanded);return{type:"Polygon",coordinates:a.$(c)}},MultiPoint:function(a){return{type:"MultiPoint",coordinates:a.$()}},MultiLineString:function(a){return{type:"MultiLineString",coordinates:a.$()}},MultiPolygon:function(a,b){var c;b&&(c=b.rightHanded);return{type:"MultiPolygon",coordinates:a.$(c)}},GeometryCollection:function(a,b){return{type:"GeometryCollection",geometries:a.f.map(function(a){var d=ta({},b);delete d.featureProjection;return bp(a,
+d)})}},Circle:function(){return{type:"GeometryCollection",geometries:[]}}};k=Zo.prototype;k.gd=function(a,b){var c;c="Feature"===a.type?a:{type:"Feature",geometry:a};var d=$o(c.geometry,b),e=new J;this.b&&e.Nc(this.b);e.Pa(d);void 0!==c.id&&e.cc(c.id);c.properties&&e.I(c.properties);return e};k.Sf=function(a,b){var c;if("FeatureCollection"===a.type){c=[];var d=a.features,e,f;e=0;for(f=d.length;e<f;++e)c.push(this.gd(d[e],b))}else c=[this.gd(a,b)];return c};k.ci=function(a,b){return $o(a,b)};
+k.ii=function(a){a=a.crs;var b;a?"name"==a.type?b=zc(a.properties.name):"EPSG"==a.type?b=zc("EPSG:"+a.properties.code):ha(!1,36):b=this.defaultDataProjection;return b};k.md=function(a,b){b=fo(this,b);var c={type:"Feature"},d=a.f;void 0!==d&&(c.id=d);(d=a.V())?c.geometry=bp(d,b):c.geometry=null;d=a.R();delete d[a.a];wa(d)?c.properties=null:c.properties=d;return c};k.Se=function(a,b){b=fo(this,b);var c=[],d,e;d=0;for(e=a.length;d<e;++d)c.push(this.md(a[d],b));return{type:"FeatureCollection",features:c}};
+k.Te=function(a,b){return bp(a,fo(this,b))};function dp(){this.f=new XMLSerializer;co.call(this)}v(dp,co);k=dp.prototype;k.Y=function(){return"xml"};k.bc=function(a,b){if(Nn(a))return ep(this,a,b);if(On(a))return this.ai(a,b);if("string"===typeof a){var c=Pn(a);return ep(this,c,b)}return null};function ep(a,b,c){a=fp(a,b,c);return 0<a.length?a[0]:null}k.La=function(a,b){if(Nn(a))return fp(this,a,b);if(On(a))return this.oc(a,b);if("string"===typeof a){var c=Pn(a);return fp(this,c,b)}return[]};
+function fp(a,b,c){var d=[];for(b=b.firstChild;b;b=b.nextSibling)b.nodeType==Node.ELEMENT_NODE&&bb(d,a.oc(b,c));return d}k.hd=function(a,b){if(Nn(a))return this.u(a,b);if(On(a)){var c=this.Ee(a,[eo(this,a,b?b:{})]);return c?c:null}return"string"===typeof a?(c=Pn(a),this.u(c,b)):null};k.Wa=function(a){return Nn(a)?this.Xf(a):On(a)?this.He(a):"string"===typeof a?(a=Pn(a),this.Xf(a)):null};k.Xf=function(){return this.defaultDataProjection};k.He=function(){return this.defaultDataProjection};
+k.Od=function(a,b){var c=this.C(a,b);return this.f.serializeToString(c)};k.ec=function(a,b){var c=this.a(a,b);return this.f.serializeToString(c)};k.od=function(a,b){var c=this.H(a,b);return this.f.serializeToString(c)};function gp(a){a=a?a:{};this.featureType=a.featureType;this.featureNS=a.featureNS;this.srsName=a.srsName;this.schemaLocation="";this.b={};this.b["http://www.opengis.net/gml"]={featureMember:Sn(gp.prototype.Id),featureMembers:Sn(gp.prototype.Id)};dp.call(this)}v(gp,dp);var hp=/^[\s\xa0]*$/;k=gp.prototype;
+k.Id=function(a,b){var c=a.localName,d=null;if("FeatureCollection"==c)"http://www.opengis.net/wfs"===a.namespaceURI?d=N([],this.b,a,b,this):d=N(null,this.b,a,b,this);else if("featureMembers"==c||"featureMember"==c){var e=b[0],f=e.featureType,g=e.featureNS,h,l;if(!f&&a.childNodes){f=[];g={};h=0;for(l=a.childNodes.length;h<l;++h){var m=a.childNodes[h];if(1===m.nodeType){var p=m.nodeName.split(":").pop();if(-1===f.indexOf(p)){var n="",q=0,m=m.namespaceURI,r;for(r in g){if(g[r]===m){n=r;break}++q}n||
+(n="p"+q,g[n]=m);f.push(n+":"+p)}}}"featureMember"!=c&&(e.featureType=f,e.featureNS=g)}"string"===typeof g&&(h=g,g={},g.p0=h);var e={},f=Array.isArray(f)?f:[f],u;for(u in g){p={};h=0;for(l=f.length;h<l;++h)(-1===f[h].indexOf(":")?"p0":f[h].split(":")[0])===u&&(p[f[h].split(":").pop()]="featureMembers"==c?Rn(this.Rf,this):Sn(this.Rf,this));e[g[u]]=p}"featureMember"==c?d=N(void 0,e,a,b):d=N([],e,a,b)}null===d&&(d=[]);return d};
+k.Ee=function(a,b){var c=b[0];c.srsName=a.firstElementChild.getAttribute("srsName");var d=N(null,this.lg,a,b,this);if(d)return go(d,!1,c)};
+k.Rf=function(a,b){var c,d;(d=a.getAttribute("fid"))||(d=a.getAttributeNS("http://www.opengis.net/gml","id")||"");var e={},f;for(c=a.firstElementChild;c;c=c.nextElementSibling){var g=c.localName;if(0===c.childNodes.length||1===c.childNodes.length&&(3===c.firstChild.nodeType||4===c.firstChild.nodeType)){var h=Ln(c,!1);hp.test(h)&&(h=void 0);e[g]=h}else"boundedBy"!==g&&(f=g),e[g]=this.Ee(c,b)}c=new J(e);f&&c.Nc(f);d&&c.cc(d);return c};
+k.hi=function(a,b){var c=this.De(a,b);if(c){var d=new C(null);d.da("XYZ",c);return d}};k.fi=function(a,b){var c=N([],this.cj,a,b,this);if(c)return new R(c)};k.ei=function(a,b){var c=N([],this.bj,a,b,this);if(c){var d=new Q(null);mo(d,c);return d}};k.gi=function(a,b){var c=N([],this.dj,a,b,this);if(c){var d=new S(null);oo(d,c);return d}};k.Yh=function(a,b){Zn(this.gj,a,b,this)};k.dh=function(a,b){Zn(this.$i,a,b,this)};k.Zh=function(a,b){Zn(this.hj,a,b,this)};
+k.Fe=function(a,b){var c=this.De(a,b);if(c){var d=new P(null);d.da("XYZ",c);return d}};k.Do=function(a,b){var c=N(null,this.Qd,a,b,this);if(c)return c};k.di=function(a,b){var c=this.De(a,b);if(c){var d=new md(null);nd(d,"XYZ",c);return d}};k.Ge=function(a,b){var c=N([null],this.We,a,b,this);if(c&&c[0]){var d=new E(null),e=c[0],f=[e.length],g,h;g=1;for(h=c.length;g<h;++g)bb(e,c[g]),f.push(e.length);d.da("XYZ",e,f);return d}};k.De=function(a,b){return N(null,this.Qd,a,b,this)};
+k.cj={"http://www.opengis.net/gml":{pointMember:Rn(gp.prototype.Yh),pointMembers:Rn(gp.prototype.Yh)}};k.bj={"http://www.opengis.net/gml":{lineStringMember:Rn(gp.prototype.dh),lineStringMembers:Rn(gp.prototype.dh)}};k.dj={"http://www.opengis.net/gml":{polygonMember:Rn(gp.prototype.Zh),polygonMembers:Rn(gp.prototype.Zh)}};k.gj={"http://www.opengis.net/gml":{Point:Rn(gp.prototype.De)}};k.$i={"http://www.opengis.net/gml":{LineString:Rn(gp.prototype.Fe)}};k.hj={"http://www.opengis.net/gml":{Polygon:Rn(gp.prototype.Ge)}};
+k.Rd={"http://www.opengis.net/gml":{LinearRing:Sn(gp.prototype.Do)}};k.oc=function(a,b){var c={featureType:this.featureType,featureNS:this.featureNS};b&&ta(c,eo(this,a,b));return this.Id(a,[c])||[]};k.He=function(a){return zc(this.srsName?this.srsName:a.firstElementChild.getAttribute("srsName"))};function ip(a){a=Ln(a,!1);return jp(a)}function jp(a){if(a=/^\s*(true|1)|(false|0)\s*$/.exec(a))return void 0!==a[1]||!1}function kp(a){a=Ln(a,!1);a=Date.parse(a);return isNaN(a)?void 0:a/1E3}function lp(a){a=Ln(a,!1);return mp(a)}function mp(a){if(a=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(a))return parseFloat(a[1])}function np(a){a=Ln(a,!1);return op(a)}function op(a){if(a=/^\s*(\d+)\s*$/.exec(a))return parseInt(a[1],10)}function T(a){return Ln(a,!1).trim()}
+function pp(a,b){qp(a,b?"1":"0")}function rp(a,b){a.appendChild(Jn.createTextNode(b.toPrecision()))}function sp(a,b){a.appendChild(Jn.createTextNode(b.toString()))}function qp(a,b){a.appendChild(Jn.createTextNode(b))};function tp(a){a=a?a:{};gp.call(this,a);this.v=void 0!==a.surface?a.surface:!1;this.i=void 0!==a.curve?a.curve:!1;this.l=void 0!==a.multiCurve?a.multiCurve:!0;this.o=void 0!==a.multiSurface?a.multiSurface:!0;this.schemaLocation=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd"}v(tp,gp);k=tp.prototype;k.Ho=function(a,b){var c=N([],this.aj,a,b,this);if(c){var d=new Q(null);mo(d,c);return d}};
+k.Io=function(a,b){var c=N([],this.ej,a,b,this);if(c){var d=new S(null);oo(d,c);return d}};k.Cg=function(a,b){Zn(this.Xi,a,b,this)};k.Gi=function(a,b){Zn(this.lj,a,b,this)};k.Lo=function(a,b){return N([null],this.fj,a,b,this)};k.Oo=function(a,b){return N([null],this.kj,a,b,this)};k.Mo=function(a,b){return N([null],this.We,a,b,this)};k.Go=function(a,b){return N([null],this.Qd,a,b,this)};k.ql=function(a,b){var c=N(void 0,this.Rd,a,b,this);c&&b[b.length-1].push(c)};
+k.Oj=function(a,b){var c=N(void 0,this.Rd,a,b,this);c&&(b[b.length-1][0]=c)};k.ji=function(a,b){var c=N([null],this.mj,a,b,this);if(c&&c[0]){var d=new E(null),e=c[0],f=[e.length],g,h;g=1;for(h=c.length;g<h;++g)bb(e,c[g]),f.push(e.length);d.da("XYZ",e,f);return d}};k.$h=function(a,b){var c=N([null],this.Yi,a,b,this);if(c){var d=new P(null);d.da("XYZ",c);return d}};k.Co=function(a,b){var c=N([null],this.Zi,a,b,this);return Rb(c[1][0],c[1][1],c[2][0],c[2][1])};
+k.Eo=function(a,b){for(var c=Ln(a,!1),d=/^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/,e=[],f;f=d.exec(c);)e.push(parseFloat(f[1])),c=c.substr(f[0].length);if(""===c){c=b[0].srsName;d="enu";c&&(d=zc(c).b);if("neu"===d)for(c=0,d=e.length;c<d;c+=3)f=e[c],e[c]=e[c+1],e[c+1]=f;c=e.length;2==c&&e.push(0);return 0===c?void 0:e}};
+k.Vf=function(a,b){var c=Ln(a,!1).replace(/^\s*|\s*$/g,""),d=b[0].srsName,e=a.parentNode.getAttribute("srsDimension"),f="enu";d&&(f=zc(d).b);c=c.split(/\s+/);d=2;a.getAttribute("srsDimension")?d=op(a.getAttribute("srsDimension")):a.getAttribute("dimension")?d=op(a.getAttribute("dimension")):e&&(d=op(e));for(var g,h,l=[],m=0,p=c.length;m<p;m+=d)e=parseFloat(c[m]),g=parseFloat(c[m+1]),h=3===d?parseFloat(c[m+2]):0,"en"===f.substr(0,2)?l.push(e,g,h):l.push(g,e,h);return l};
+k.Qd={"http://www.opengis.net/gml":{pos:Sn(tp.prototype.Eo),posList:Sn(tp.prototype.Vf)}};k.We={"http://www.opengis.net/gml":{interior:tp.prototype.ql,exterior:tp.prototype.Oj}};
+k.lg={"http://www.opengis.net/gml":{Point:Sn(gp.prototype.hi),MultiPoint:Sn(gp.prototype.fi),LineString:Sn(gp.prototype.Fe),MultiLineString:Sn(gp.prototype.ei),LinearRing:Sn(gp.prototype.di),Polygon:Sn(gp.prototype.Ge),MultiPolygon:Sn(gp.prototype.gi),Surface:Sn(tp.prototype.ji),MultiSurface:Sn(tp.prototype.Io),Curve:Sn(tp.prototype.$h),MultiCurve:Sn(tp.prototype.Ho),Envelope:Sn(tp.prototype.Co)}};k.aj={"http://www.opengis.net/gml":{curveMember:Rn(tp.prototype.Cg),curveMembers:Rn(tp.prototype.Cg)}};
+k.ej={"http://www.opengis.net/gml":{surfaceMember:Rn(tp.prototype.Gi),surfaceMembers:Rn(tp.prototype.Gi)}};k.Xi={"http://www.opengis.net/gml":{LineString:Rn(gp.prototype.Fe),Curve:Rn(tp.prototype.$h)}};k.lj={"http://www.opengis.net/gml":{Polygon:Rn(gp.prototype.Ge),Surface:Rn(tp.prototype.ji)}};k.mj={"http://www.opengis.net/gml":{patches:Sn(tp.prototype.Lo)}};k.Yi={"http://www.opengis.net/gml":{segments:Sn(tp.prototype.Oo)}};k.Zi={"http://www.opengis.net/gml":{lowerCorner:Rn(tp.prototype.Vf),upperCorner:Rn(tp.prototype.Vf)}};
+k.fj={"http://www.opengis.net/gml":{PolygonPatch:Sn(tp.prototype.Mo)}};k.kj={"http://www.opengis.net/gml":{LineStringSegment:Sn(tp.prototype.Go)}};function up(a,b,c){c=c[c.length-1].srsName;b=b.$();for(var d=b.length,e=Array(d),f,g=0;g<d;++g){f=b[g];var h=g,l="enu";c&&(l=zc(c).b);e[h]="en"===l.substr(0,2)?f[0]+" "+f[1]:f[1]+" "+f[0]}qp(a,e.join(" "))}
+k.Ti=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=Kn(a.namespaceURI,"pos");a.appendChild(d);c=c[c.length-1].srsName;a="enu";c&&(a=zc(c).b);b=b.$();qp(d,"en"===a.substr(0,2)?b[0]+" "+b[1]:b[1]+" "+b[0])};var vp={"http://www.opengis.net/gml":{lowerCorner:L(qp),upperCorner:L(qp)}};k=tp.prototype;k.zp=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);$n({node:a},vp,Xn,[b[0]+" "+b[1],b[2]+" "+b[3]],c,["lowerCorner","upperCorner"],this)};
+k.Qi=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=Kn(a.namespaceURI,"posList");a.appendChild(d);up(d,b,c)};k.jj=function(a,b){var c=b[b.length-1],d=c.node,e=c.exteriorWritten;void 0===e&&(c.exteriorWritten=!0);return Kn(d.namespaceURI,void 0!==e?"interior":"exterior")};
+k.Ue=function(a,b,c){var d=c[c.length-1].srsName;"PolygonPatch"!==a.nodeName&&d&&a.setAttribute("srsName",d);"Polygon"===a.nodeName||"PolygonPatch"===a.nodeName?(b=b.Zc(),$n({node:a,srsName:d},wp,this.jj,b,c,void 0,this)):"Surface"===a.nodeName&&(d=Kn(a.namespaceURI,"patches"),a.appendChild(d),a=Kn(d.namespaceURI,"PolygonPatch"),d.appendChild(a),this.Ue(a,b,c))};
+k.Qe=function(a,b,c){var d=c[c.length-1].srsName;"LineStringSegment"!==a.nodeName&&d&&a.setAttribute("srsName",d);"LineString"===a.nodeName||"LineStringSegment"===a.nodeName?(d=Kn(a.namespaceURI,"posList"),a.appendChild(d),up(d,b,c)):"Curve"===a.nodeName&&(d=Kn(a.namespaceURI,"segments"),a.appendChild(d),a=Kn(d.namespaceURI,"LineStringSegment"),d.appendChild(a),this.Qe(a,b,c))};
+k.Si=function(a,b,c){var d=c[c.length-1],e=d.srsName,d=d.surface;e&&a.setAttribute("srsName",e);b=b.Ad();$n({node:a,srsName:e,surface:d},xp,this.c,b,c,void 0,this)};k.Ap=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);b=b.re();$n({node:a,srsName:d},yp,Vn("pointMember"),b,c,void 0,this)};k.Ri=function(a,b,c){var d=c[c.length-1],e=d.srsName,d=d.curve;e&&a.setAttribute("srsName",e);b=b.Yc();$n({node:a,srsName:e,curve:d},zp,this.c,b,c,void 0,this)};
+k.Ui=function(a,b,c){var d=Kn(a.namespaceURI,"LinearRing");a.appendChild(d);this.Qi(d,b,c)};k.Vi=function(a,b,c){var d=this.g(b,c);d&&(a.appendChild(d),this.Ue(d,b,c))};k.Bp=function(a,b,c){var d=Kn(a.namespaceURI,"Point");a.appendChild(d);this.Ti(d,b,c)};k.Pi=function(a,b,c){var d=this.g(b,c);d&&(a.appendChild(d),this.Qe(d,b,c))};
+k.pd=function(a,b,c){var d=c[c.length-1],e=ta({},d);e.node=a;var f;Array.isArray(b)?d.dataProjection?f=Qc(b,d.featureProjection,d.dataProjection):f=b:f=go(b,!0,d);$n(e,Ap,this.g,[f],c,void 0,this)};
+k.Ni=function(a,b,c){var d=b.f;d&&a.setAttribute("fid",d);var d=c[c.length-1],e=d.featureNS,f=b.a;d.Mc||(d.Mc={},d.Mc[e]={});var g=b.R();b=[];var h=[],l;for(l in g){var m=g[l];null!==m&&(b.push(l),h.push(m),l==f||m instanceof Rc?l in d.Mc[e]||(d.Mc[e][l]=L(this.pd,this)):l in d.Mc[e]||(d.Mc[e][l]=L(qp)))}l=ta({},d);l.node=a;$n(l,d.Mc,Vn(void 0,e),h,c,b)};
+var xp={"http://www.opengis.net/gml":{surfaceMember:L(tp.prototype.Vi),polygonMember:L(tp.prototype.Vi)}},yp={"http://www.opengis.net/gml":{pointMember:L(tp.prototype.Bp)}},zp={"http://www.opengis.net/gml":{lineStringMember:L(tp.prototype.Pi),curveMember:L(tp.prototype.Pi)}},wp={"http://www.opengis.net/gml":{exterior:L(tp.prototype.Ui),interior:L(tp.prototype.Ui)}},Ap={"http://www.opengis.net/gml":{Curve:L(tp.prototype.Qe),MultiCurve:L(tp.prototype.Ri),Point:L(tp.prototype.Ti),MultiPoint:L(tp.prototype.Ap),
+LineString:L(tp.prototype.Qe),MultiLineString:L(tp.prototype.Ri),LinearRing:L(tp.prototype.Qi),Polygon:L(tp.prototype.Ue),MultiPolygon:L(tp.prototype.Si),Surface:L(tp.prototype.Ue),MultiSurface:L(tp.prototype.Si),Envelope:L(tp.prototype.zp)}},Bp={MultiLineString:"lineStringMember",MultiCurve:"curveMember",MultiPolygon:"polygonMember",MultiSurface:"surfaceMember"};tp.prototype.c=function(a,b){return Kn("http://www.opengis.net/gml",Bp[b[b.length-1].node.nodeName])};
+tp.prototype.g=function(a,b){var c=b[b.length-1],d=c.multiSurface,e=c.surface,f=c.curve,c=c.multiCurve,g;Array.isArray(a)?g="Envelope":(g=a.Y(),"MultiPolygon"===g&&!0===d?g="MultiSurface":"Polygon"===g&&!0===e?g="Surface":"LineString"===g&&!0===f?g="Curve":"MultiLineString"===g&&!0===c&&(g="MultiCurve"));return Kn("http://www.opengis.net/gml",g)};
+tp.prototype.H=function(a,b){b=fo(this,b);var c=Kn("http://www.opengis.net/gml","geom"),d={node:c,srsName:this.srsName,curve:this.i,surface:this.v,multiSurface:this.o,multiCurve:this.l};b&&ta(d,b);this.pd(c,a,[d]);return c};
+tp.prototype.a=function(a,b){b=fo(this,b);var c=Kn("http://www.opengis.net/gml","featureMembers");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.schemaLocation);var d={srsName:this.srsName,curve:this.i,surface:this.v,multiSurface:this.o,multiCurve:this.l,featureNS:this.featureNS,featureType:this.featureType};b&&ta(d,b);var d=[d],e=d[d.length-1],f=e.featureType,g=e.featureNS,h={};h[g]={};h[g][f]=L(this.Ni,this);e=ta({},e);e.node=c;$n(e,h,Vn(f,g),a,d);return c};function Cp(a){a=a?a:{};gp.call(this,a);this.b["http://www.opengis.net/gml"].featureMember=Rn(gp.prototype.Id);this.schemaLocation=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd"}v(Cp,gp);k=Cp.prototype;
+k.bi=function(a,b){var c=Ln(a,!1).replace(/^\s*|\s*$/g,""),d=b[0].srsName,e=a.parentNode.getAttribute("srsDimension"),f="enu";d&&(d=zc(d))&&(f=d.b);c=c.split(/[\s,]+/);d=2;a.getAttribute("srsDimension")?d=op(a.getAttribute("srsDimension")):a.getAttribute("dimension")?d=op(a.getAttribute("dimension")):e&&(d=op(e));for(var g,h,l=[],m=0,p=c.length;m<p;m+=d)e=parseFloat(c[m]),g=parseFloat(c[m+1]),h=3===d?parseFloat(c[m+2]):0,"en"===f.substr(0,2)?l.push(e,g,h):l.push(g,e,h);return l};
+k.Ao=function(a,b){var c=N([null],this.Wi,a,b,this);return Rb(c[1][0],c[1][1],c[1][3],c[1][4])};k.ol=function(a,b){var c=N(void 0,this.Rd,a,b,this);c&&b[b.length-1].push(c)};k.jo=function(a,b){var c=N(void 0,this.Rd,a,b,this);c&&(b[b.length-1][0]=c)};k.Qd={"http://www.opengis.net/gml":{coordinates:Sn(Cp.prototype.bi)}};k.We={"http://www.opengis.net/gml":{innerBoundaryIs:Cp.prototype.ol,outerBoundaryIs:Cp.prototype.jo}};k.Wi={"http://www.opengis.net/gml":{coordinates:Rn(Cp.prototype.bi)}};
+k.lg={"http://www.opengis.net/gml":{Point:Sn(gp.prototype.hi),MultiPoint:Sn(gp.prototype.fi),LineString:Sn(gp.prototype.Fe),MultiLineString:Sn(gp.prototype.ei),LinearRing:Sn(gp.prototype.di),Polygon:Sn(gp.prototype.Ge),MultiPolygon:Sn(gp.prototype.gi),Box:Sn(Cp.prototype.Ao)}};function Dp(a){a=a?a:{};dp.call(this);this.defaultDataProjection=zc("EPSG:4326");this.b=a.readExtensions}v(Dp,dp);var Ep=[null,"http://www.topografix.com/GPX/1/0","http://www.topografix.com/GPX/1/1"];function Fp(a,b,c,d){a.push(parseFloat(c.getAttribute("lon")),parseFloat(c.getAttribute("lat")));"ele"in d?(a.push(d.ele),delete d.ele,b.hasZ=!0):a.push(0);"time"in d?(a.push(d.time),delete d.time,b.hasM=!0):a.push(0);return a}
+function Gp(a,b,c){var d="XY",e=2;a.hasZ&&a.hasM?(d="XYZM",e=4):a.hasZ?(d="XYZ",e=3):a.hasM&&(d="XYM",e=3);if(4!==e){var f,g;f=0;for(g=b.length/4;f<g;f++)b[f*e]=b[4*f],b[f*e+1]=b[4*f+1],a.hasZ&&(b[f*e+2]=b[4*f+2]),a.hasM&&(b[f*e+2]=b[4*f+3]);b.length=b.length/4*e;if(c)for(f=0,g=c.length;f<g;f++)c[f]=c[f]/4*e}return d}function Hp(a,b){var c=b[b.length-1],d=a.getAttribute("href");null!==d&&(c.link=d);Zn(Ip,a,b)}function Jp(a,b){b[b.length-1].extensionsNode_=a}
+function Kp(a,b){var c=b[0],d=N({flatCoordinates:[],layoutOptions:{}},Lp,a,b);if(d){var e=d.flatCoordinates;delete d.flatCoordinates;var f=d.layoutOptions;delete d.layoutOptions;var f=Gp(f,e),g=new P(null);g.da(f,e);go(g,!1,c);c=new J(g);c.I(d);return c}}
+function Mp(a,b){var c=b[0],d=N({flatCoordinates:[],ends:[],layoutOptions:{}},Np,a,b);if(d){var e=d.flatCoordinates;delete d.flatCoordinates;var f=d.ends;delete d.ends;var g=d.layoutOptions;delete d.layoutOptions;var g=Gp(g,e,f),h=new Q(null);h.da(g,e,f);go(h,!1,c);c=new J(h);c.I(d);return c}}function Op(a,b){var c=b[0],d=N({},Pp,a,b);if(d){var e={},f=Fp([],e,a,d),e=Gp(e,f),f=new C(f,e);go(f,!1,c);c=new J(f);c.I(d);return c}}
+var Qp={rte:Kp,trk:Mp,wpt:Op},Rp=M(Ep,{rte:Rn(Kp),trk:Rn(Mp),wpt:Rn(Op)}),Ip=M(Ep,{text:K(T,"linkText"),type:K(T,"linkType")}),Lp=M(Ep,{name:K(T),cmt:K(T),desc:K(T),src:K(T),link:Hp,number:K(np),extensions:Jp,type:K(T),rtept:function(a,b){var c=N({},Sp,a,b);if(c){var d=b[b.length-1];Fp(d.flatCoordinates,d.layoutOptions,a,c)}}}),Sp=M(Ep,{ele:K(lp),time:K(kp)}),Np=M(Ep,{name:K(T),cmt:K(T),desc:K(T),src:K(T),link:Hp,number:K(np),type:K(T),extensions:Jp,trkseg:function(a,b){var c=b[b.length-1];Zn(Tp,
+a,b);c.ends.push(c.flatCoordinates.length)}}),Tp=M(Ep,{trkpt:function(a,b){var c=N({},Up,a,b);if(c){var d=b[b.length-1];Fp(d.flatCoordinates,d.layoutOptions,a,c)}}}),Up=M(Ep,{ele:K(lp),time:K(kp)}),Pp=M(Ep,{ele:K(lp),time:K(kp),magvar:K(lp),geoidheight:K(lp),name:K(T),cmt:K(T),desc:K(T),src:K(T),link:Hp,sym:K(T),type:K(T),fix:K(T),sat:K(np),hdop:K(lp),vdop:K(lp),pdop:K(lp),ageofdgpsdata:K(lp),dgpsid:K(np),extensions:Jp});
+function Vp(a,b){b||(b=[]);for(var c=0,d=b.length;c<d;++c){var e=b[c];if(a.b){var f=e.get("extensionsNode_")||null;a.b(e,f)}e.set("extensionsNode_",void 0)}}Dp.prototype.ai=function(a,b){if(!Za(Ep,a.namespaceURI))return null;var c=Qp[a.localName];if(!c)return null;c=c(a,[eo(this,a,b)]);if(!c)return null;Vp(this,[c]);return c};Dp.prototype.oc=function(a,b){if(!Za(Ep,a.namespaceURI))return[];if("gpx"==a.localName){var c=N([],Rp,a,[eo(this,a,b)]);if(c)return Vp(this,c),c}return[]};
+function Wp(a,b,c){a.setAttribute("href",b);b=c[c.length-1].properties;$n({node:a},Xp,Xn,[b.linkText,b.linkType],c,Yp)}function Zp(a,b,c){var d=c[c.length-1],e=d.node.namespaceURI,f=d.properties;a.setAttributeNS(null,"lat",b[1]);a.setAttributeNS(null,"lon",b[0]);switch(d.geometryLayout){case "XYZM":0!==b[3]&&(f.time=b[3]);case "XYZ":0!==b[2]&&(f.ele=b[2]);break;case "XYM":0!==b[2]&&(f.time=b[2])}b="rtept"==a.nodeName?$p[e]:aq[e];d=Yn(f,b);$n({node:a,properties:f},bq,Xn,d,c,b)}
+var Yp=["text","type"],Xp=M(Ep,{text:L(qp),type:L(qp)}),cq=M(Ep,"name cmt desc src link number type rtept".split(" ")),dq=M(Ep,{name:L(qp),cmt:L(qp),desc:L(qp),src:L(qp),link:L(Wp),number:L(sp),type:L(qp),rtept:Un(L(Zp))}),$p=M(Ep,["ele","time"]),eq=M(Ep,"name cmt desc src link number type trkseg".split(" ")),hq=M(Ep,{name:L(qp),cmt:L(qp),desc:L(qp),src:L(qp),link:L(Wp),number:L(sp),type:L(qp),trkseg:Un(L(function(a,b,c){$n({node:a,geometryLayout:b.ka,properties:{}},fq,gq,b.$(),c)}))}),gq=Vn("trkpt"),
+fq=M(Ep,{trkpt:L(Zp)}),aq=M(Ep,"ele time magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid".split(" ")),bq=M(Ep,{ele:L(rp),time:L(function(a,b){var c=new Date(1E3*b);a.appendChild(Jn.createTextNode(c.getUTCFullYear()+"-"+pb(c.getUTCMonth()+1)+"-"+pb(c.getUTCDate())+"T"+pb(c.getUTCHours())+":"+pb(c.getUTCMinutes())+":"+pb(c.getUTCSeconds())+"Z"))}),magvar:L(rp),geoidheight:L(rp),name:L(qp),cmt:L(qp),desc:L(qp),src:L(qp),link:L(Wp),sym:L(qp),type:L(qp),fix:L(qp),
+sat:L(sp),hdop:L(rp),vdop:L(rp),pdop:L(rp),ageofdgpsdata:L(rp),dgpsid:L(sp)}),iq={Point:"wpt",LineString:"rte",MultiLineString:"trk"};function jq(a,b){var c=a.V();if(c&&(c=iq[c.Y()]))return Kn(b[b.length-1].node.namespaceURI,c)}
+var kq=M(Ep,{rte:L(function(a,b,c){var d=c[0],e=b.R();a={node:a,properties:e};if(b=b.V())b=go(b,!0,d),a.geometryLayout=b.ka,e.rtept=b.$();d=cq[c[c.length-1].node.namespaceURI];e=Yn(e,d);$n(a,dq,Xn,e,c,d)}),trk:L(function(a,b,c){var d=c[0],e=b.R();a={node:a,properties:e};if(b=b.V())b=go(b,!0,d),e.trkseg=b.Yc();d=eq[c[c.length-1].node.namespaceURI];e=Yn(e,d);$n(a,hq,Xn,e,c,d)}),wpt:L(function(a,b,c){var d=c[0],e=c[c.length-1];e.properties=b.R();if(b=b.V())b=go(b,!0,d),e.geometryLayout=b.ka,Zp(a,b.$(),
+c)})});Dp.prototype.a=function(a,b){b=fo(this,b);var c=Kn("http://www.topografix.com/GPX/1/1","gpx");c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation","http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd");c.setAttribute("version","1.1");c.setAttribute("creator","OpenLayers 3");$n({node:c},kq,jq,a,[b]);return c};function lq(){co.call(this)}v(lq,co);function mq(a){return"string"===typeof a?a:""}k=lq.prototype;k.Y=function(){return"text"};k.bc=function(a,b){return this.Hd(mq(a),fo(this,b))};k.La=function(a,b){return this.Tf(mq(a),fo(this,b))};k.hd=function(a,b){return this.Jd(mq(a),fo(this,b))};k.Wa=function(){return this.defaultDataProjection};k.Od=function(a,b){return this.Re(a,fo(this,b))};k.ec=function(a,b){return this.Oi(a,fo(this,b))};k.od=function(a,b){return this.Pd(a,fo(this,b))};function nq(a){a=a?a:{};co.call(this);this.defaultDataProjection=zc("EPSG:4326");this.b=a.altitudeMode?a.altitudeMode:oq}v(nq,lq);var pq=/^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/,qq=/^H.([A-Z]{3}).*?:(.*)/,rq=/^HFDTE(\d{2})(\d{2})(\d{2})/,sq=/\r\n|\r|\n/;
+nq.prototype.Hd=function(a,b){var c=this.b,d=a.split(sq),e={},f=[],g=2E3,h=0,l=1,m=-1,p,n;p=0;for(n=d.length;p<n;++p){var q=d[p],r;if("B"==q.charAt(0)){if(r=pq.exec(q)){var q=parseInt(r[1],10),u=parseInt(r[2],10),w=parseInt(r[3],10),y=parseInt(r[4],10)+parseInt(r[5],10)/6E4;"S"==r[6]&&(y=-y);var z=parseInt(r[7],10)+parseInt(r[8],10)/6E4;"W"==r[9]&&(z=-z);f.push(z,y);c!=oq&&f.push(c==tq?parseInt(r[11],10):c==uq?parseInt(r[12],10):0);r=Date.UTC(g,h,l,q,u,w);r<m&&(r=Date.UTC(g,h,l+1,q,u,w));f.push(r/
+1E3);m=r}}else"H"==q.charAt(0)&&((r=rq.exec(q))?(l=parseInt(r[1],10),h=parseInt(r[2],10)-1,g=2E3+parseInt(r[3],10)):(r=qq.exec(q))&&(e[r[1]]=r[2].trim()))}if(0===f.length)return null;d=new P(null);d.da(c==oq?"XYM":"XYZM",f);c=new J(go(d,!1,b));c.I(e);return c};nq.prototype.Tf=function(a,b){var c=this.Hd(a,b);return c?[c]:[]};var uq="barometric",tq="gps",oq="none";function vq(a,b,c,d,e,f){Na.call(this);this.l=null;this.a=a?a:new Image;null!==d&&(this.a.crossOrigin=d);this.c=f?document.createElement("CANVAS"):null;this.j=f;this.i=null;this.f=e;this.g=c;this.o=b;this.v=!1;this.f==li&&wq(this)}v(vq,Na);function wq(a){var b=De(1,1);try{b.drawImage(a.a,0,0),b.getImageData(0,0,1,1)}catch(c){a.v=!0}}vq.prototype.H=function(){this.f=ki;this.i.forEach(ya);this.i=null;this.b("change")};
+vq.prototype.u=function(){this.f=li;this.g&&(this.a.width=this.g[0],this.a.height=this.g[1]);this.g=[this.a.width,this.a.height];this.i.forEach(ya);this.i=null;wq(this);if(!this.v&&null!==this.j){this.c.width=this.a.width;this.c.height=this.a.height;var a=this.c.getContext("2d");a.drawImage(this.a,0,0);for(var b=a.getImageData(0,0,this.a.width,this.a.height),c=b.data,d=this.j[0]/255,e=this.j[1]/255,f=this.j[2]/255,g=0,h=c.length;g<h;g+=4)c[g]*=d,c[g+1]*=e,c[g+2]*=f;a.putImageData(b,0,0)}this.b("change")};
+vq.prototype.load=function(){if(this.f==ji){this.f=mi;this.i=[Da(this.a,"error",this.H,this),Da(this.a,"load",this.u,this)];try{this.a.src=this.o}catch(a){this.H()}}};function xq(a){a=a||{};this.c=void 0!==a.anchor?a.anchor:[.5,.5];this.j=null;this.a=void 0!==a.anchorOrigin?a.anchorOrigin:yq;this.C=void 0!==a.anchorXUnits?a.anchorXUnits:zq;this.D=void 0!==a.anchorYUnits?a.anchorYUnits:zq;this.ra=void 0!==a.crossOrigin?a.crossOrigin:null;var b=void 0!==a.img?a.img:null,c=void 0!==a.imgSize?a.imgSize:null,d=a.src;ha(!(void 0!==d&&b),4);ha(!b||b&&c,5);void 0!==d&&0!==d.length||!b||(d=b.src||x(b).toString());ha(void 0!==d&&0<d.length,6);var e=void 0!==a.src?ji:li;
+this.f=void 0!==a.color?ye(a.color):null;var f=this.ra,g=this.f,h=Nh.get(d,f,g);h||(h=new vq(b,d,c,f,e,g),Nh.set(d,f,g,h));this.b=h;this.L=void 0!==a.offset?a.offset:[0,0];this.g=void 0!==a.offsetOrigin?a.offsetOrigin:yq;this.v=null;this.A=void 0!==a.size?a.size:null;ri.call(this,{opacity:void 0!==a.opacity?a.opacity:1,rotation:void 0!==a.rotation?a.rotation:0,scale:void 0!==a.scale?a.scale:1,snapToPixel:void 0!==a.snapToPixel?a.snapToPixel:!0,rotateWithView:void 0!==a.rotateWithView?a.rotateWithView:
+!1})}v(xq,ri);k=xq.prototype;
+k.clone=function(){var a=this.Ic(1),b;if(this.b.f===li)if("IMG"===a.tagName.toUpperCase())b=a.cloneNode(!0);else{b=document.createElement("canvas");var c=b.getContext("2d");b.width=a.width;b.height=a.height;c.drawImage(a,0,0)}return new xq({anchor:this.c.slice(),anchorOrigin:this.a,anchorXUnits:this.C,anchorYUnits:this.D,crossOrigin:this.ra,color:this.f&&this.f.slice?this.f.slice():this.f||void 0,img:b?b:void 0,imgSize:b?this.b.g.slice():void 0,src:b?void 0:this.b.o,offset:this.L.slice(),offsetOrigin:this.g,
+size:null!==this.A?this.A.slice():void 0,opacity:this.l,scale:this.i,snapToPixel:this.u,rotation:this.o,rotateWithView:this.H})};k.Ac=function(){if(this.j)return this.j;var a=this.c,b=this.ac();if(this.C==zq||this.D==zq){if(!b)return null;a=this.c.slice();this.C==zq&&(a[0]*=b[0]);this.D==zq&&(a[1]*=b[1])}if(this.a!=yq){if(!b)return null;a===this.c&&(a=this.c.slice());if(this.a==Aq||this.a==Bq)a[0]=-a[0]+b[0];if(this.a==Cq||this.a==Bq)a[1]=-a[1]+b[1]}return this.j=a};k.Gn=function(){return this.f};
+k.Ic=function(){var a=this.b;return a.c?a.c:a.a};k.de=function(){return this.b.g};k.xe=function(){return this.b.f};k.Lf=function(){var a=this.b;if(!a.l)if(a.v){var b=a.g[0],c=a.g[1],d=De(b,c);d.fillRect(0,0,b,c);a.l=d.canvas}else a.l=a.a;return a.l};k.Jc=function(){if(this.v)return this.v;var a=this.L;if(this.g!=yq){var b=this.ac(),c=this.b.g;if(!b||!c)return null;a=a.slice();if(this.g==Aq||this.g==Bq)a[0]=c[0]-b[0]-a[0];if(this.g==Cq||this.g==Bq)a[1]=c[1]-b[1]-a[1]}return this.v=a};k.Hn=function(){return this.b.o};
+k.ac=function(){return this.A?this.A:this.b.g};k.eh=function(a,b){return B(this.b,"change",a,b)};k.load=function(){this.b.load()};k.Ii=function(a,b){Ea(this.b,"change",a,b)};var zq="fraction",Cq="bottom-left",Bq="bottom-right",yq="top-left",Aq="top-right";function Dq(a){a=a||{};this.g=a.font;this.j=a.rotation;this.v=a.rotateWithView;this.a=a.scale;this.H=a.text;this.l=a.textAlign;this.o=a.textBaseline;this.b=void 0!==a.fill?a.fill:new vi({color:"#333"});this.f=void 0!==a.stroke?a.stroke:null;this.c=void 0!==a.offsetX?a.offsetX:0;this.i=void 0!==a.offsetY?a.offsetY:0}k=Dq.prototype;
+k.clone=function(){return new Dq({font:this.g,rotation:this.j,rotateWithView:this.v,scale:this.a,text:this.Ka(),textAlign:this.l,textBaseline:this.o,fill:this.b?this.b.clone():void 0,stroke:this.f?this.f.clone():void 0,offsetX:this.c,offsetY:this.i})};k.dk=function(){return this.g};k.sk=function(){return this.c};k.tk=function(){return this.i};k.Vn=function(){return this.b};k.Wn=function(){return this.v};k.Xn=function(){return this.j};k.Yn=function(){return this.a};k.Zn=function(){return this.f};
+k.Ka=function(){return this.H};k.Dk=function(){return this.l};k.Ek=function(){return this.o};k.si=function(a){this.g=a};k.yi=function(a){this.c=a};k.zi=function(a){this.i=a};k.Sh=function(a){this.b=a};k.$n=function(a){this.j=a};k.Th=function(a){this.a=a};k.Uh=function(a){this.f=a};k.Vh=function(a){this.H=a};k.Bi=function(a){this.l=a};k.lp=function(a){this.o=a};function Eq(a){a=a?a:{};dp.call(this);Fq||(Gq=[255,255,255,1],Hq=new vi({color:Gq}),Iq=[20,2],Jq=Kq="pixels",Lq=[64,64],Mq="https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png",Nq=.5,Oq=new xq({anchor:Iq,anchorOrigin:Cq,anchorXUnits:Kq,anchorYUnits:Jq,crossOrigin:"anonymous",rotation:0,scale:Nq,size:Lq,src:Mq}),Pq="NO_IMAGE",Qq=new wi({color:Gq,width:1}),Rq=new wi({color:[51,51,51,1],width:2}),Sq=new Dq({font:"bold 16px Helvetica",fill:Hq,stroke:Rq,scale:.8}),Tq=new xi({fill:Hq,image:Oq,
+text:Sq,stroke:Qq,zIndex:0}),Fq=[Tq]);this.defaultDataProjection=zc("EPSG:4326");this.g=a.defaultStyle?a.defaultStyle:Fq;this.c=void 0!==a.extractStyles?a.extractStyles:!0;this.l=void 0!==a.writeStyles?a.writeStyles:!0;this.b={};this.i=void 0!==a.showPointNames?a.showPointNames:!0}var Fq,Gq,Hq,Iq,Kq,Jq,Lq,Mq,Nq,Oq,Pq,Qq,Rq,Sq,Tq;v(Eq,dp);
+var Uq=["http://www.google.com/kml/ext/2.2"],Vq=[null,"http://earth.google.com/kml/2.0","http://earth.google.com/kml/2.1","http://earth.google.com/kml/2.2","http://www.opengis.net/kml/2.2"],Wq={fraction:zq,pixels:"pixels"};
+function Xq(a,b){var c,d=[0,0],e="start";a.a&&(c=a.a.de(),null===c&&(c=Lq),2==c.length&&(e=a.a.i,d[0]=e*c[0]/2,d[1]=-e*c[1]/2,e="left"));if(null!==a.Ka()){var f=a.Ka();c=f.clone();c.si(f.g||Sq.g);c.Th(f.a||Sq.a);c.Sh(f.b||Sq.b);c.Uh(f.f||Rq)}else c=Sq.clone();c.Vh(b);c.yi(d[0]);c.zi(d[1]);c.Bi(e);return new xi({text:c})}
+function Yq(a,b,c,d,e){return function(){var f=e,g="";f&&this.V()&&(f="Point"===this.V().Y());f&&(g=this.get("name"),f=f&&g);if(a)return f?(f=Xq(a[0],g),a.concat(f)):a;if(b){var h=Zq(b,c,d);return f?(f=Xq(h[0],g),h.concat(f)):h}return f?(f=Xq(c[0],g),c.concat(f)):c}}function Zq(a,b,c){return Array.isArray(a)?a:"string"===typeof a?(!(a in c)&&"#"+a in c&&(a="#"+a),Zq(c[a],b,c)):b}
+function $q(a){a=Ln(a,!1);if(a=/^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(a))return a=a[1],[parseInt(a.substr(6,2),16),parseInt(a.substr(4,2),16),parseInt(a.substr(2,2),16),parseInt(a.substr(0,2),16)/255]}function ar(a){a=Ln(a,!1);for(var b=[],c=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i,d;d=c.exec(a);)b.push(parseFloat(d[1]),parseFloat(d[2]),d[3]?parseFloat(d[3]):0),a=a.substr(d[0].length);return""!==a?void 0:b}
+function br(a){var b=Ln(a,!1).trim();return a.baseURI?(new URL(b,a.baseURI)).href:b}function cr(a){return lp(a)}function dr(a,b){return N(null,er,a,b)}function fr(a,b){var c=N({B:[],Mi:[]},gr,a,b);if(c){var d=c.B,c=c.Mi,e,f;e=0;for(f=Math.min(d.length,c.length);e<f;++e)d[4*e+3]=c[e];c=new P(null);c.da("XYZM",d);return c}}function hr(a,b){var c=N({},ir,a,b),d=N(null,jr,a,b);if(d){var e=new P(null);e.da("XYZ",d);e.I(c);return e}}
+function kr(a,b){var c=N({},ir,a,b),d=N(null,jr,a,b);if(d){var e=new E(null);e.da("XYZ",d,[d.length]);e.I(c);return e}}
+function lr(a,b){var c=N([],mr,a,b);if(!c)return null;if(0===c.length)return new Vo(c);var d,e=!0,f=c[0].Y(),g,h,l;h=1;for(l=c.length;h<l;++h)if(g=c[h],g.Y()!=f){e=!1;break}if(e)if("Point"==f){d=c[0];e=d.ka;f=d.ia();h=1;for(l=c.length;h<l;++h)g=c[h],bb(f,g.ia());d=new R(null);d.da(e,f);nr(d,c)}else"LineString"==f?(d=new Q(null),mo(d,c),nr(d,c)):"Polygon"==f?(d=new S(null),oo(d,c),nr(d,c)):"GeometryCollection"==f?d=new Vo(c):ha(!1,37);else d=new Vo(c);return d}
+function or(a,b){var c=N({},ir,a,b),d=N(null,jr,a,b);if(d){var e=new C(null);e.da("XYZ",d);e.I(c);return e}}function pr(a,b){var c=N({},ir,a,b),d=N([null],qr,a,b);if(d&&d[0]){var e=new E(null),f=d[0],g=[f.length],h,l;h=1;for(l=d.length;h<l;++h)bb(f,d[h]),g.push(f.length);e.da("XYZ",f,g);e.I(c);return e}}
+function rr(a,b){var c=N({},sr,a,b);if(!c)return null;var d="fillStyle"in c?c.fillStyle:Hq,e=c.fill;void 0===e||e||(d=null);e="imageStyle"in c?c.imageStyle:Oq;e==Pq&&(e=void 0);var f="textStyle"in c?c.textStyle:Sq,g="strokeStyle"in c?c.strokeStyle:Qq,c=c.outline;void 0===c||c||(g=null);return[new xi({fill:d,image:e,stroke:g,text:f,zIndex:void 0})]}
+function nr(a,b){var c=b.length,d=Array(b.length),e=Array(b.length),f,g,h,l;h=l=!1;for(g=0;g<c;++g)f=b[g],d[g]=f.get("extrude"),e[g]=f.get("altitudeMode"),h=h||void 0!==d[g],l=l||e[g];h&&a.set("extrude",d);l&&a.set("altitudeMode",e)}function tr(a,b){Zn(ur,a,b)}function vr(a,b){Zn(wr,a,b)}
+var xr=M(Vq,{displayName:K(T),value:K(T)}),ur=M(Vq,{Data:function(a,b){var c=a.getAttribute("name");Zn(xr,a,b);var d=b[b.length-1];null!==c?d[c]=d.value:null!==d.displayName&&(d[d.displayName]=d.value)},SchemaData:function(a,b){Zn(yr,a,b)}}),wr=M(Vq,{LatLonAltBox:function(a,b){var c=N({},zr,a,b);if(c){var d=b[b.length-1];d.extent=[parseFloat(c.west),parseFloat(c.south),parseFloat(c.east),parseFloat(c.north)];d.altitudeMode=c.altitudeMode;d.minAltitude=parseFloat(c.minAltitude);d.maxAltitude=parseFloat(c.maxAltitude)}},
+Lod:function(a,b){var c=N({},Ar,a,b);if(c){var d=b[b.length-1];d.minLodPixels=parseFloat(c.minLodPixels);d.maxLodPixels=parseFloat(c.maxLodPixels);d.minFadeExtent=parseFloat(c.minFadeExtent);d.maxFadeExtent=parseFloat(c.maxFadeExtent)}}}),zr=M(Vq,{altitudeMode:K(T),minAltitude:K(lp),maxAltitude:K(lp),north:K(lp),south:K(lp),east:K(lp),west:K(lp)}),Ar=M(Vq,{minLodPixels:K(lp),maxLodPixels:K(lp),minFadeExtent:K(lp),maxFadeExtent:K(lp)}),ir=M(Vq,{extrude:K(ip),altitudeMode:K(T)}),er=M(Vq,{coordinates:Sn(ar)}),
+qr=M(Vq,{innerBoundaryIs:function(a,b){var c=N(void 0,Br,a,b);c&&b[b.length-1].push(c)},outerBoundaryIs:function(a,b){var c=N(void 0,Cr,a,b);c&&(b[b.length-1][0]=c)}}),gr=M(Vq,{when:function(a,b){var c=b[b.length-1].Mi,d=Ln(a,!1),d=Date.parse(d);c.push(isNaN(d)?0:d)}},M(Uq,{coord:function(a,b){var c=b[b.length-1].B,d=Ln(a,!1);(d=/^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i.exec(d))?c.push(parseFloat(d[1]),parseFloat(d[2]),
+parseFloat(d[3]),0):c.push(0,0,0,0)}})),jr=M(Vq,{coordinates:Sn(ar)}),Dr=M(Vq,{href:K(br)},M(Uq,{x:K(lp),y:K(lp),w:K(lp),h:K(lp)})),Er=M(Vq,{Icon:K(function(a,b){var c=N({},Dr,a,b);return c?c:null}),heading:K(lp),hotSpot:K(function(a){var b=a.getAttribute("xunits"),c=a.getAttribute("yunits");return{x:parseFloat(a.getAttribute("x")),jg:Wq[b],y:parseFloat(a.getAttribute("y")),kg:Wq[c]}}),scale:K(cr)}),Br=M(Vq,{LinearRing:Sn(dr)}),Fr=M(Vq,{color:K($q),scale:K(cr)}),Gr=M(Vq,{color:K($q),width:K(lp)}),
+mr=M(Vq,{LineString:Rn(hr),LinearRing:Rn(kr),MultiGeometry:Rn(lr),Point:Rn(or),Polygon:Rn(pr)}),Hr=M(Uq,{Track:Rn(fr)}),Jr=M(Vq,{ExtendedData:tr,Region:vr,Link:function(a,b){Zn(Ir,a,b)},address:K(T),description:K(T),name:K(T),open:K(ip),phoneNumber:K(T),visibility:K(ip)}),Ir=M(Vq,{href:K(br)}),Cr=M(Vq,{LinearRing:Sn(dr)}),Kr=M(Vq,{Style:K(rr),key:K(T),styleUrl:K(br)}),Mr=M(Vq,{ExtendedData:tr,Region:vr,MultiGeometry:K(lr,"geometry"),LineString:K(hr,"geometry"),LinearRing:K(kr,"geometry"),Point:K(or,
+"geometry"),Polygon:K(pr,"geometry"),Style:K(rr),StyleMap:function(a,b){var c=N(void 0,Lr,a,b);if(c){var d=b[b.length-1];Array.isArray(c)?d.Style=c:"string"===typeof c?d.styleUrl=c:ha(!1,38)}},address:K(T),description:K(T),name:K(T),open:K(ip),phoneNumber:K(T),styleUrl:K(br),visibility:K(ip)},M(Uq,{MultiTrack:K(function(a,b){var c=N([],Hr,a,b);if(c){var d=new Q(null);mo(d,c);return d}},"geometry"),Track:K(fr,"geometry")})),Nr=M(Vq,{color:K($q),fill:K(ip),outline:K(ip)}),yr=M(Vq,{SimpleData:function(a,
+b){var c=a.getAttribute("name");if(null!==c){var d=T(a);b[b.length-1][c]=d}}}),sr=M(Vq,{IconStyle:function(a,b){var c=N({},Er,a,b);if(c){var d=b[b.length-1],e="Icon"in c?c.Icon:{},f=!("Icon"in c)||0<Object.keys(e).length,g,h=e.href;h?g=h:f&&(g=Mq);var l,m,p;(h=c.hotSpot)?(l=[h.x,h.y],m=h.jg,p=h.kg):g===Mq?(l=Iq,m=Kq,p=Jq):/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(g)&&(l=[.5,0],p=m=zq);var n,h=e.x,q=e.y;void 0!==h&&void 0!==q&&(n=[h,q]);var r,h=e.w,e=e.h;void 0!==h&&void 0!==e&&(r=[h,e]);var u,
+e=c.heading;void 0!==e&&(u=na(e));c=c.scale;f?(g==Mq&&(r=Lq,void 0===c&&(c=Nq)),f=new xq({anchor:l,anchorOrigin:Cq,anchorXUnits:m,anchorYUnits:p,crossOrigin:"anonymous",offset:n,offsetOrigin:Cq,rotation:u,scale:c,size:r,src:g}),d.imageStyle=f):d.imageStyle=Pq}},LabelStyle:function(a,b){var c=N({},Fr,a,b);c&&(b[b.length-1].textStyle=new Dq({fill:new vi({color:"color"in c?c.color:Gq}),scale:c.scale}))},LineStyle:function(a,b){var c=N({},Gr,a,b);c&&(b[b.length-1].strokeStyle=new wi({color:"color"in c?
+c.color:Gq,width:"width"in c?c.width:1}))},PolyStyle:function(a,b){var c=N({},Nr,a,b);if(c){var d=b[b.length-1];d.fillStyle=new vi({color:"color"in c?c.color:Gq});var e=c.fill;void 0!==e&&(d.fill=e);c=c.outline;void 0!==c&&(d.outline=c)}}}),Lr=M(Vq,{Pair:function(a,b){var c=N({},Kr,a,b);if(c){var d=c.key;d&&"normal"==d&&((d=c.styleUrl)&&(b[b.length-1]=d),(c=c.Style)&&(b[b.length-1]=c))}}});k=Eq.prototype;
+k.Qf=function(a,b){var c=M(Vq,{Document:Qn(this.Qf,this),Folder:Qn(this.Qf,this),Placemark:Rn(this.Wf,this),Style:this.Qo.bind(this),StyleMap:this.Po.bind(this)});if(c=N([],c,a,b,this))return c};k.Wf=function(a,b){var c=N({geometry:null},Mr,a,b);if(c){var d=new J,e=a.getAttribute("id");null!==e&&d.cc(e);var e=b[0],f=c.geometry;f&&go(f,!1,e);d.Pa(f);delete c.geometry;this.c&&d.Df(Yq(c.Style,c.styleUrl,this.g,this.b,this.i));delete c.Style;d.I(c);return d}};
+k.Qo=function(a,b){var c=a.getAttribute("id");if(null!==c){var d=rr(a,b);d&&(c=a.baseURI?(new URL("#"+c,a.baseURI)).href:"#"+c,this.b[c]=d)}};k.Po=function(a,b){var c=a.getAttribute("id");if(null!==c){var d=N(void 0,Lr,a,b);d&&(c=a.baseURI?(new URL("#"+c,a.baseURI)).href:"#"+c,this.b[c]=d)}};k.ai=function(a,b){if(!Za(Vq,a.namespaceURI))return null;var c=this.Wf(a,[eo(this,a,b)]);return c?c:null};
+k.oc=function(a,b){if(!Za(Vq,a.namespaceURI))return[];var c;c=a.localName;if("Document"==c||"Folder"==c)return(c=this.Qf(a,[eo(this,a,b)]))?c:[];if("Placemark"==c)return(c=this.Wf(a,[eo(this,a,b)]))?[c]:[];if("kml"==c){c=[];var d;for(d=a.firstElementChild;d;d=d.nextElementSibling){var e=this.oc(d,b);e&&bb(c,e)}return c}return[]};k.Jo=function(a){if(Nn(a))return Or(this,a);if(On(a))return Pr(this,a);if("string"===typeof a)return a=Pn(a),Or(this,a)};
+function Or(a,b){var c;for(c=b.firstChild;c;c=c.nextSibling)if(c.nodeType==Node.ELEMENT_NODE){var d=Pr(a,c);if(d)return d}}function Pr(a,b){var c;for(c=b.firstElementChild;c;c=c.nextElementSibling)if(Za(Vq,c.namespaceURI)&&"name"==c.localName)return T(c);for(c=b.firstElementChild;c;c=c.nextElementSibling){var d=c.localName;if(Za(Vq,c.namespaceURI)&&("Document"==d||"Folder"==d||"Placemark"==d||"kml"==d)&&(d=Pr(a,c)))return d}}
+k.Ko=function(a){var b=[];Nn(a)?bb(b,Qr(this,a)):On(a)?bb(b,Rr(this,a)):"string"===typeof a&&(a=Pn(a),bb(b,Qr(this,a)));return b};function Qr(a,b){var c,d=[];for(c=b.firstChild;c;c=c.nextSibling)c.nodeType==Node.ELEMENT_NODE&&bb(d,Rr(a,c));return d}
+function Rr(a,b){var c,d=[];for(c=b.firstElementChild;c;c=c.nextElementSibling)if(Za(Vq,c.namespaceURI)&&"NetworkLink"==c.localName){var e=N({},Jr,c,[]);d.push(e)}for(c=b.firstElementChild;c;c=c.nextElementSibling)e=c.localName,!Za(Vq,c.namespaceURI)||"Document"!=e&&"Folder"!=e&&"kml"!=e||bb(d,Rr(a,c));return d}k.No=function(a){var b=[];Nn(a)?bb(b,Sr(this,a)):On(a)?bb(b,this.Ie(a)):"string"===typeof a&&(a=Pn(a),bb(b,Sr(this,a)));return b};
+function Sr(a,b){var c,d=[];for(c=b.firstChild;c;c=c.nextSibling)c.nodeType==Node.ELEMENT_NODE&&bb(d,a.Ie(c));return d}k.Ie=function(a){var b,c=[];for(b=a.firstElementChild;b;b=b.nextElementSibling)if(Za(Vq,b.namespaceURI)&&"Region"==b.localName){var d=N({},wr,b,[]);c.push(d)}for(b=a.firstElementChild;b;b=b.nextElementSibling)a=b.localName,!Za(Vq,b.namespaceURI)||"Document"!=a&&"Folder"!=a&&"kml"!=a||bb(c,this.Ie(b));return c};
+function Tr(a,b){var c=ye(b),c=[255*(4==c.length?c[3]:1),c[2],c[1],c[0]],d;for(d=0;4>d;++d){var e=parseInt(c[d],10).toString(16);c[d]=1==e.length?"0"+e:e}qp(a,c.join(""))}function Ur(a,b,c){a={node:a};var d=b.Y(),e,f;"GeometryCollection"==d?(e=b.pf(),f=Vr):"MultiPoint"==d?(e=b.re(),f=Wr):"MultiLineString"==d?(e=b.Yc(),f=Xr):"MultiPolygon"==d?(e=b.Ad(),f=Yr):ha(!1,39);$n(a,Zr,f,e,c)}function $r(a,b,c){$n({node:a},as,bs,[b],c)}
+function cs(a,b,c){var d={node:a};b.f&&a.setAttribute("id",b.f);a=b.R();var e={address:1,description:1,name:1,open:1,phoneNumber:1,styleUrl:1,visibility:1};e[b.a]=1;var f=Object.keys(a||{}).sort().filter(function(a){return!e[a]});if(0<f.length){var g=Yn(a,f);$n(d,ds,es,[{names:f,values:g}],c)}if(f=b.Gc())if(f=f.call(b,0))f=Array.isArray(f)?f[0]:f,this.l&&(a.Style=f),(f=f.Ka())&&(a.name=f.Ka());f=fs[c[c.length-1].node.namespaceURI];a=Yn(a,f);$n(d,ds,Xn,a,c,f);a=c[0];(b=b.V())&&(b=go(b,!0,a));$n(d,
+ds,Vr,[b],c)}function gs(a,b,c){var d=b.ia();a={node:a};a.layout=b.ka;a.stride=b.pa();$n(a,hs,is,[d],c)}function js(a,b,c){b=b.Zc();var d=b.shift();a={node:a};$n(a,ks,ls,b,c);$n(a,ks,ms,[d],c)}function ns(a,b){rp(a,Math.round(1E6*b)/1E6)}
+var os=M(Vq,["Document","Placemark"]),rs=M(Vq,{Document:L(function(a,b,c){$n({node:a},ps,qs,b,c,void 0,this)}),Placemark:L(cs)}),ps=M(Vq,{Placemark:L(cs)}),ss=M(Vq,{Data:L(function(a,b,c){a.setAttribute("name",b.name);a={node:a};b=b.value;"object"==typeof b?(null!==b&&b.displayName&&$n(a,ss,Xn,[b.displayName],c,["displayName"]),null!==b&&b.value&&$n(a,ss,Xn,[b.value],c,["value"])):$n(a,ss,Xn,[b],c,["value"])}),value:L(function(a,b){qp(a,b)}),displayName:L(function(a,b){a.appendChild(Jn.createCDATASection(b))})}),
+ts={Point:"Point",LineString:"LineString",LinearRing:"LinearRing",Polygon:"Polygon",MultiPoint:"MultiGeometry",MultiLineString:"MultiGeometry",MultiPolygon:"MultiGeometry",GeometryCollection:"MultiGeometry"},us=M(Vq,["href"],M(Uq,["x","y","w","h"])),vs=M(Vq,{href:L(qp)},M(Uq,{x:L(rp),y:L(rp),w:L(rp),h:L(rp)})),ws=M(Vq,["scale","heading","Icon","hotSpot"]),ys=M(Vq,{Icon:L(function(a,b,c){a={node:a};var d=us[c[c.length-1].node.namespaceURI],e=Yn(b,d);$n(a,vs,Xn,e,c,d);d=us[Uq[0]];e=Yn(b,d);$n(a,vs,
+xs,e,c,d)}),heading:L(rp),hotSpot:L(function(a,b){a.setAttribute("x",b.x);a.setAttribute("y",b.y);a.setAttribute("xunits",b.jg);a.setAttribute("yunits",b.kg)}),scale:L(ns)}),zs=M(Vq,["color","scale"]),As=M(Vq,{color:L(Tr),scale:L(ns)}),Bs=M(Vq,["color","width"]),Cs=M(Vq,{color:L(Tr),width:L(rp)}),as=M(Vq,{LinearRing:L(gs)}),Zr=M(Vq,{LineString:L(gs),Point:L(gs),Polygon:L(js),GeometryCollection:L(Ur)}),fs=M(Vq,"name open visibility address phoneNumber description styleUrl Style".split(" ")),ds=M(Vq,
+{ExtendedData:L(function(a,b,c){a={node:a};var d=b.names;b=b.values;for(var e=d.length,f=0;f<e;f++)$n(a,ss,Ds,[{name:d[f],value:b[f]}],c)}),MultiGeometry:L(Ur),LineString:L(gs),LinearRing:L(gs),Point:L(gs),Polygon:L(js),Style:L(function(a,b,c){a={node:a};var d={},e=b.f,f=b.g,g=b.a;b=b.Ka();g instanceof xq&&(d.IconStyle=g);b&&(d.LabelStyle=b);f&&(d.LineStyle=f);e&&(d.PolyStyle=e);b=Es[c[c.length-1].node.namespaceURI];d=Yn(d,b);$n(a,Fs,Xn,d,c,b)}),address:L(qp),description:L(qp),name:L(qp),open:L(pp),
+phoneNumber:L(qp),styleUrl:L(qp),visibility:L(pp)}),hs=M(Vq,{coordinates:L(function(a,b,c){c=c[c.length-1];var d=c.layout;c=c.stride;var e;"XY"==d||"XYM"==d?e=2:"XYZ"==d||"XYZM"==d?e=3:ha(!1,34);var f,g=b.length,h="";if(0<g){h+=b[0];for(d=1;d<e;++d)h+=","+b[d];for(f=c;f<g;f+=c)for(h+=" "+b[f],d=1;d<e;++d)h+=","+b[f+d]}qp(a,h)})}),ks=M(Vq,{outerBoundaryIs:L($r),innerBoundaryIs:L($r)}),Gs=M(Vq,{color:L(Tr)}),Es=M(Vq,["IconStyle","LabelStyle","LineStyle","PolyStyle"]),Fs=M(Vq,{IconStyle:L(function(a,
+b,c){a={node:a};var d={},e=b.ac(),f=b.de(),g={href:b.b.o};if(e){g.w=e[0];g.h=e[1];var h=b.Ac(),l=b.Jc();l&&f&&0!==l[0]&&l[1]!==e[1]&&(g.x=l[0],g.y=f[1]-(l[1]+e[1]));h&&0!==h[0]&&h[1]!==e[1]&&(d.hotSpot={x:h[0],jg:"pixels",y:e[1]-h[1],kg:"pixels"})}d.Icon=g;e=b.i;1!==e&&(d.scale=e);b=b.o;0!==b&&(d.heading=b);b=ws[c[c.length-1].node.namespaceURI];d=Yn(d,b);$n(a,ys,Xn,d,c,b)}),LabelStyle:L(function(a,b,c){a={node:a};var d={},e=b.b;e&&(d.color=e.b);(b=b.a)&&1!==b&&(d.scale=b);b=zs[c[c.length-1].node.namespaceURI];
+d=Yn(d,b);$n(a,As,Xn,d,c,b)}),LineStyle:L(function(a,b,c){a={node:a};var d=Bs[c[c.length-1].node.namespaceURI];b=Yn({color:b.b,width:b.f},d);$n(a,Cs,Xn,b,c,d)}),PolyStyle:L(function(a,b,c){$n({node:a},Gs,Hs,[b.b],c)})});function xs(a,b,c){return Kn(Uq[0],"gx:"+c)}function qs(a,b){return Kn(b[b.length-1].node.namespaceURI,"Placemark")}function Vr(a,b){if(a)return Kn(b[b.length-1].node.namespaceURI,ts[a.Y()])}
+var Hs=Vn("color"),is=Vn("coordinates"),Ds=Vn("Data"),es=Vn("ExtendedData"),ls=Vn("innerBoundaryIs"),Wr=Vn("Point"),Xr=Vn("LineString"),bs=Vn("LinearRing"),Yr=Vn("Polygon"),ms=Vn("outerBoundaryIs");
+Eq.prototype.a=function(a,b){b=fo(this,b);var c=Kn(Vq[4],"kml");c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:gx",Uq[0]);c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation","http://www.opengis.net/kml/2.2 https://developers.google.com/kml/schema/kml22gx.xsd");var d={node:c},e={};1<a.length?e.Document=a:1==a.length&&(e.Placemark=a[0]);var f=os[c.namespaceURI],
+e=Yn(e,f);$n(d,rs,Xn,e,[b],f,this);return c};(function(){var a={},b={ma:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ma=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Rp=c()}})(function(){return function d(a,b,g){function h(m,n){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!n&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ma:{}};a[m][0].call(q.ma,function(b){var d=
+a[m][1][b];return h(d?d:b)},q,q.ma,d,a,b,g)}return b[m].ma}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b,f){f.read=function(a,b,d,e,f){var n;n=8*f-e-1;var q=(1<<n)-1,r=q>>1,u=-7;f=d?f-1:0;var w=d?-1:1,y=a[b+f];f+=w;d=y&(1<<-u)-1;y>>=-u;for(u+=n;0<u;d=256*d+a[b+f],f+=w,u-=8);n=d&(1<<-u)-1;d>>=-u;for(u+=e;0<u;n=256*n+a[b+f],f+=w,u-=8);if(0===d)d=1-r;else{if(d===q)return n?NaN:Infinity*(y?-1:1);n+=Math.pow(2,e);d-=r}return(y?-1:1)*n*Math.pow(2,d-
+e)};f.write=function(a,b,d,e,f,n){var q,r=8*n-f-1,u=(1<<r)-1,w=u>>1,y=23===f?Math.pow(2,-24)-Math.pow(2,-77):0;n=e?0:n-1;var z=e?1:-1,A=0>b||0===b&&0>1/b?1:0;b=Math.abs(b);isNaN(b)||Infinity===b?(b=isNaN(b)?1:0,e=u):(e=Math.floor(Math.log(b)/Math.LN2),1>b*(q=Math.pow(2,-e))&&(e--,q*=2),b=1<=e+w?b+y/q:b+y*Math.pow(2,1-w),2<=b*q&&(e++,q/=2),e+w>=u?(b=0,e=u):1<=e+w?(b=(b*q-1)*Math.pow(2,f),e+=w):(b=b*Math.pow(2,w-1)*Math.pow(2,f),e=0));for(;8<=f;a[d+n]=b&255,n+=z,b/=256,f-=8);e=e<<f|b;for(r+=f;0<r;a[d+
+n]=e&255,n+=z,e/=256,r-=8);a[d+n-z]|=128*A}},{}],2:[function(a,b){function f(a){this.gc=ArrayBuffer.isView&&ArrayBuffer.isView(a)?a:new Uint8Array(a||0);this.type=this.ha=0;this.length=this.gc.length}function g(a,b,d){var e=d.gc,f,g;g=e[d.ha++];f=(g&112)>>4;if(128>g)return h(a,f,b);g=e[d.ha++];f|=(g&127)<<3;if(128>g)return h(a,f,b);g=e[d.ha++];f|=(g&127)<<10;if(128>g)return h(a,f,b);g=e[d.ha++];f|=(g&127)<<17;if(128>g)return h(a,f,b);g=e[d.ha++];f|=(g&127)<<24;if(128>g)return h(a,f,b);g=e[d.ha++];
+if(128>g)return h(a,f|(g&1)<<31,b);throw Error("Expected varint not more than 10 bytes");}function h(a,b,d){return d?4294967296*b+(a>>>0):4294967296*(b>>>0)+(a>>>0)}b.ma=f;var l=a("ieee754");f.f=0;f.g=1;f.b=2;f.a=5;f.prototype={Uf:function(a,b,d){for(d=d||this.length;this.ha<d;){var e=this.Ia(),f=e>>3,g=this.ha;this.type=e&7;a(f,b,this);this.ha===g&&this.qp(e)}return b},Fo:function(){var a=l.read(this.gc,this.ha,!0,23,4);this.ha+=4;return a},Bo:function(){var a=l.read(this.gc,this.ha,!0,52,8);this.ha+=
+8;return a},Ia:function(a){var b=this.gc,d,e;e=b[this.ha++];d=e&127;if(128>e)return d;e=b[this.ha++];d|=(e&127)<<7;if(128>e)return d;e=b[this.ha++];d|=(e&127)<<14;if(128>e)return d;e=b[this.ha++];d|=(e&127)<<21;if(128>e)return d;e=b[this.ha];return g(d|(e&15)<<28,a,this)},Ro:function(){return this.Ia(!0)},Kd:function(){var a=this.Ia();return 1===a%2?(a+1)/-2:a/2},zo:function(){return!!this.Ia()},Yf:function(){for(var a=this.Ia()+this.ha,b=this.gc,d="",e=this.ha;e<a;){var f=b[e],g=null,h=239<f?4:223<
+f?3:191<f?2:1;if(e+h>a)break;var l,z,A;if(1===h)128>f&&(g=f);else if(2===h)l=b[e+1],128===(l&192)&&(g=(f&31)<<6|l&63,127>=g&&(g=null));else if(3===h){if(l=b[e+1],z=b[e+2],128===(l&192)&&128===(z&192)&&(g=(f&15)<<12|(l&63)<<6|z&63,2047>=g||55296<=g&&57343>=g))g=null}else 4===h&&(l=b[e+1],z=b[e+2],A=b[e+3],128===(l&192)&&128===(z&192)&&128===(A&192)&&(g=(f&15)<<18|(l&63)<<12|(z&63)<<6|A&63,65535>=g||1114112<=g))&&(g=null);null===g?(g=65533,h=1):65535<g&&(g-=65536,d+=String.fromCharCode(g>>>10&1023|
+55296),g=56320|g&1023);d+=String.fromCharCode(g);e+=h}this.ha=a;return d},qp:function(a){a&=7;if(a===f.f)for(;127<this.gc[this.ha++];);else if(a===f.b)this.ha=this.Ia()+this.ha;else if(a===f.a)this.ha+=4;else if(a===f.g)this.ha+=8;else throw Error("Unimplemented type: "+a);}}},{ieee754:1}]},{},[2])(2)});gl=b.ma})();(function(){var a={},b={ma:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ma=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Up=c()}})(function(){return function d(a,b,g){function h(m,n){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!n&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ma:{}};a[m][0].call(q.ma,function(b){var d=
+a[m][1][b];return h(d?d:b)},q,q.ma,d,a,b,g)}return b[m].ma}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b){function f(a,b){this.x=a;this.y=b}b.ma=f;f.prototype={clone:function(){return new f(this.x,this.y)},add:function(a){return this.clone().oj(a)},rotate:function(a){return this.clone().yj(a)},round:function(){return this.clone().zj()},angle:function(){return Math.atan2(this.y,this.x)},oj:function(a){this.x+=a.x;this.y+=a.y;return this},yj:function(a){var b=
+Math.cos(a);a=Math.sin(a);var d=a*this.x+b*this.y;this.x=b*this.x-a*this.y;this.y=d;return this},zj:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this}};f.b=function(a){return a instanceof f?a:Array.isArray(a)?new f(a[0],a[1]):a}},{}],2:[function(a,b){b.ma.nj=a("./lib/vectortile.js");b.ma.Op=a("./lib/vectortilefeature.js");b.ma.Pp=a("./lib/vectortilelayer.js")},{"./lib/vectortile.js":3,"./lib/vectortilefeature.js":4,"./lib/vectortilelayer.js":5}],3:[function(a,b){function f(a,
+b,d){3===a&&(a=new g(d,d.Ia()+d.ha),a.length&&(b[a.name]=a))}var g=a("./vectortilelayer");b.ma=function(a,b){this.layers=a.Uf(f,{},b)}},{"./vectortilelayer":5}],4:[function(a,b){function f(a,b,d,e,f){this.properties={};this.extent=d;this.type=0;this.rc=a;this.Ye=-1;this.Td=e;this.Vd=f;a.Uf(g,this,b)}function g(a,b,d){if(1==a)b.id=d.Ia();else if(2==a)for(a=d.Ia()+d.ha;d.ha<a;){var e=b.Td[d.Ia()],f=b.Vd[d.Ia()];b.properties[e]=f}else 3==a?b.type=d.Ia():4==a&&(b.Ye=d.ha)}var h=a("point-geometry");b.ma=
+f;f.b=["Unknown","Point","LineString","Polygon"];f.prototype.fh=function(){var a=this.rc;a.ha=this.Ye;for(var b=a.Ia()+a.ha,d=1,e=0,f=0,g=0,u=[],w;a.ha<b;)if(e||(e=a.Ia(),d=e&7,e>>=3),e--,1===d||2===d)f+=a.Kd(),g+=a.Kd(),1===d&&(w&&u.push(w),w=[]),w.push(new h(f,g));else if(7===d)w&&w.push(w[0].clone());else throw Error("unknown command "+d);w&&u.push(w);return u};f.prototype.bbox=function(){var a=this.rc;a.ha=this.Ye;for(var b=a.Ia()+a.ha,d=1,e=0,f=0,g=0,h=Infinity,w=-Infinity,y=Infinity,z=-Infinity;a.ha<
+b;)if(e||(e=a.Ia(),d=e&7,e>>=3),e--,1===d||2===d)f+=a.Kd(),g+=a.Kd(),f<h&&(h=f),f>w&&(w=f),g<y&&(y=g),g>z&&(z=g);else if(7!==d)throw Error("unknown command "+d);return[h,y,w,z]}},{"point-geometry":1}],5:[function(a,b){function f(a,b){this.version=1;this.name=null;this.extent=4096;this.length=0;this.rc=a;this.Td=[];this.Vd=[];this.Sd=[];a.Uf(g,this,b);this.length=this.Sd.length}function g(a,b,d){15===a?b.version=d.Ia():1===a?b.name=d.Yf():5===a?b.extent=d.Ia():2===a?b.Sd.push(d.ha):3===a?b.Td.push(d.Yf()):
+4===a&&b.Vd.push(h(d))}function h(a){for(var b=null,d=a.Ia()+a.ha;a.ha<d;)b=a.Ia()>>3,b=1===b?a.Yf():2===b?a.Fo():3===b?a.Bo():4===b?a.Ro():5===b?a.Ia():6===b?a.Kd():7===b?a.zo():null;return b}var l=a("./vectortilefeature.js");b.ma=f;f.prototype.feature=function(a){if(0>a||a>=this.Sd.length)throw Error("feature index out of bounds");this.rc.ha=this.Sd[a];a=this.rc.Ia()+this.rc.ha;return new l(this.rc,a,this.extent,this.Td,this.Vd)}},{"./vectortilefeature.js":4}]},{},[2])(2)});hl=b.ma})();function Is(a,b,c,d){this.g=a;this.b=b;this.c=c;this.f=d}k=Is.prototype;k.get=function(a){return this.f[a]};k.Kb=function(){return this.c};k.G=function(){this.a||(this.a="Point"===this.g?Sb(this.b):Tb(this.b,0,this.b.length,2));return this.a};k.Vb=function(){return this.b};k.ia=Is.prototype.Vb;k.V=function(){return this};k.Tm=function(){return this.f};k.Bd=Is.prototype.V;k.pa=function(){return 2};k.Gc=ea;k.Y=function(){return this.g};function Js(a){co.call(this);a=a?a:{};this.defaultDataProjection=new sc({code:"",units:"tile-pixels"});this.b=a.featureClass?a.featureClass:Is;this.g=a.geometryName?a.geometryName:"geometry";this.a=a.layerName?a.layerName:"layer";this.f=a.layers?a.layers:null}v(Js,co);Js.prototype.Y=function(){return"arraybuffer"};
+Js.prototype.La=function(a,b){var c=this.f,d=new gl(a),d=new hl.nj(d),e=[],f=this.b,g,h,l;for(l in d.layers)if(!c||-1!=c.indexOf(l)){g=d.layers[l];for(var m=0,p=g.length;m<p;++m){if(f===Is){var n=g.feature(m);h=l;var q=n.fh(),r=[],u=[];Ks(q,u,r);var w=n.type,y=void 0;1===w?y=1===q.length?"Point":"MultiPoint":2===w?y=1===q.length?"LineString":"MultiLineString":3===w&&(y="Polygon");n=n.properties;n[this.a]=h;h=new this.b(y,u,r,n)}else{q=g.feature(m);n=l;y=b;h=new this.b;r=q.id;u=q.properties;u[this.a]=
+n;n=q.type;if(0===n)n=null;else{var q=q.fh(),w=[],z=[];Ks(q,z,w);var A=void 0;1===n?A=1===q.length?new C(null):new R(null):2===n?1===q.length?A=new P(null):A=new Q(null):3===n&&(A=new E(null));A.da("XY",z,w);n=A}(y=go(n,!1,fo(this,y)))&&(u[this.g]=y);h.cc(r);h.I(u);h.Nc(this.g)}e.push(h)}}return e};Js.prototype.Wa=function(){return this.defaultDataProjection};Js.prototype.c=function(a){this.f=a};
+function Ks(a,b,c){for(var d=0,e=0,f=a.length;e<f;++e){var g=a[e],h,l;h=0;for(l=g.length;h<l;++h){var m=g[h];b.push(m.x,m.y)}d+=2*h;c.push(d)}};function Ls(){dp.call(this);this.defaultDataProjection=zc("EPSG:4326")}v(Ls,dp);function Ms(a,b){b[b.length-1].Nd[a.getAttribute("k")]=a.getAttribute("v")}
+var Ns=[null],Os=M(Ns,{nd:function(a,b){b[b.length-1].bd.push(a.getAttribute("ref"))},tag:Ms}),Qs=M(Ns,{node:function(a,b){var c=b[0],d=b[b.length-1],e=a.getAttribute("id"),f=[parseFloat(a.getAttribute("lon")),parseFloat(a.getAttribute("lat"))];d.jh[e]=f;var g=N({Nd:{}},Ps,a,b);wa(g.Nd)||(f=new C(f),go(f,!1,c),c=new J(f),c.cc(e),c.I(g.Nd),d.features.push(c))},way:function(a,b){for(var c=b[0],d=a.getAttribute("id"),e=N({bd:[],Nd:{}},Os,a,b),f=b[b.length-1],g=[],h=0,l=e.bd.length;h<l;h++)bb(g,f.jh[e.bd[h]]);
+e.bd[0]==e.bd[e.bd.length-1]?(h=new E(null),h.da("XY",g,[g.length])):(h=new P(null),h.da("XY",g));go(h,!1,c);c=new J(h);c.cc(d);c.I(e.Nd);f.features.push(c)}}),Ps=M(Ns,{tag:Ms});Ls.prototype.oc=function(a,b){var c=eo(this,a,b);return"osm"==a.localName&&(c=N({jh:{},features:[]},Qs,a,[c]),c.features)?c.features:[]};function Rs(a){return a.getAttributeNS("http://www.w3.org/1999/xlink","href")};function Ss(){}Ss.prototype.read=function(a){return Nn(a)?this.a(a):On(a)?this.b(a):"string"===typeof a?(a=Pn(a),this.a(a)):null};function Ts(){}v(Ts,Ss);Ts.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};Ts.prototype.b=function(a){return(a=N({},Us,a,[]))?a:null};
+var Vs=[null,"http://www.opengis.net/ows/1.1"],Us=M(Vs,{ServiceIdentification:K(function(a,b){return N({},Ws,a,b)}),ServiceProvider:K(function(a,b){return N({},Xs,a,b)}),OperationsMetadata:K(function(a,b){return N({},Ys,a,b)})}),Zs=M(Vs,{DeliveryPoint:K(T),City:K(T),AdministrativeArea:K(T),PostalCode:K(T),Country:K(T),ElectronicMailAddress:K(T)}),$s=M(Vs,{Value:Tn(function(a){return T(a)})}),at=M(Vs,{AllowedValues:K(function(a,b){return N({},$s,a,b)})}),ct=M(Vs,{Phone:K(function(a,b){return N({},
+bt,a,b)}),Address:K(function(a,b){return N({},Zs,a,b)})}),et=M(Vs,{HTTP:K(function(a,b){return N({},dt,a,b)})}),dt=M(Vs,{Get:Tn(function(a,b){var c=Rs(a);return c?N({href:c},ft,a,b):void 0}),Post:void 0}),gt=M(Vs,{DCP:K(function(a,b){return N({},et,a,b)})}),Ys=M(Vs,{Operation:function(a,b){var c=a.getAttribute("name"),d=N({},gt,a,b);d&&(b[b.length-1][c]=d)}}),bt=M(Vs,{Voice:K(T),Facsimile:K(T)}),ft=M(Vs,{Constraint:Tn(function(a,b){var c=a.getAttribute("name");return c?N({name:c},at,a,b):void 0})}),
+ht=M(Vs,{IndividualName:K(T),PositionName:K(T),ContactInfo:K(function(a,b){return N({},ct,a,b)})}),Ws=M(Vs,{Title:K(T),ServiceTypeVersion:K(T),ServiceType:K(T)}),Xs=M(Vs,{ProviderName:K(T),ProviderSite:K(Rs),ServiceContact:K(function(a,b){return N({},ht,a,b)})});function it(a,b,c,d){var e;void 0!==d?e=d:e=[];for(var f=d=0;f<b;){var g=a[f++];e[d++]=a[f++];e[d++]=g;for(g=2;g<c;++g)e[d++]=a[f++]}e.length=d};function jt(a){a=a?a:{};co.call(this);this.defaultDataProjection=zc("EPSG:4326");this.b=a.factor?a.factor:1E5;this.a=a.geometryLayout?a.geometryLayout:"XY"}v(jt,lq);function kt(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;var f,g;f=0;for(g=a.length;f<g;)for(d=0;d<b;++d,++f){var h=a[f],l=h-e[d];e[d]=h;a[f]=l}return lt(a,c?c:1E5)}function mt(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;a=nt(a,c?c:1E5);var f;c=0;for(f=a.length;c<f;)for(d=0;d<b;++d,++c)e[d]+=a[c],a[c]=e[d];return a}
+function lt(a,b){var c=b?b:1E5,d,e;d=0;for(e=a.length;d<e;++d)a[d]=Math.round(a[d]*c);c=0;for(d=a.length;c<d;++c)e=a[c],a[c]=0>e?~(e<<1):e<<1;c="";d=0;for(e=a.length;d<e;++d){for(var f=a[d],g,h="";32<=f;)g=(32|f&31)+63,h+=String.fromCharCode(g),f>>=5;h+=String.fromCharCode(f+63);c+=h}return c}
+function nt(a,b){var c=b?b:1E5,d=[],e=0,f=0,g,h;g=0;for(h=a.length;g<h;++g){var l=a.charCodeAt(g)-63,e=e|(l&31)<<f;32>l?(d.push(e),f=e=0):f+=5}e=0;for(f=d.length;e<f;++e)g=d[e],d[e]=g&1?~(g>>1):g>>1;e=0;for(f=d.length;e<f;++e)d[e]/=c;return d}k=jt.prototype;k.Hd=function(a,b){var c=this.Jd(a,b);return new J(c)};k.Tf=function(a,b){return[this.Hd(a,b)]};k.Jd=function(a,b){var c=Vc(this.a),d=mt(a,c,this.b);it(d,d.length,c,d);c=id(d,0,d.length,c);return go(new P(c,this.a),!1,fo(this,b))};
+k.Re=function(a,b){var c=a.V();if(c)return this.Pd(c,b);ha(!1,40);return""};k.Oi=function(a,b){return this.Re(a[0],b)};k.Pd=function(a,b){a=go(a,!0,fo(this,b));var c=a.ia(),d=a.pa();it(c,c.length,d,c);return kt(c,d,this.b)};function ot(a){a=a?a:{};co.call(this);this.defaultDataProjection=zc(a.defaultDataProjection?a.defaultDataProjection:"EPSG:4326")}v(ot,ho);function pt(a,b){var c=[],d,e,f,g;f=0;for(g=a.length;f<g;++f)d=a[f],0<f&&c.pop(),0<=d?e=b[d]:e=b[~d].slice().reverse(),c.push.apply(c,e);d=0;for(e=c.length;d<e;++d)c[d]=c[d].slice();return c}function qt(a,b,c,d,e){a=a.geometries;var f=[],g,h;g=0;for(h=a.length;g<h;++g)f[g]=rt(a[g],b,c,d,e);return f}
+function rt(a,b,c,d,e){var f=a.type,g=st[f];b="Point"===f||"MultiPoint"===f?g(a,c,d):g(a,b);c=new J;c.Pa(go(b,!1,e));void 0!==a.id&&c.cc(a.id);a.properties&&c.I(a.properties);return c}
+ot.prototype.Sf=function(a,b){if("Topology"==a.type){var c,d=null,e=null;a.transform&&(c=a.transform,d=c.scale,e=c.translate);var f=a.arcs;if(c){c=d;var g=e,h,l;h=0;for(l=f.length;h<l;++h){var m=f[h],p=c,n=g,q=0,r=0,u,w,y;w=0;for(y=m.length;w<y;++w)u=m[w],q+=u[0],r+=u[1],u[0]=q,u[1]=r,tt(u,p,n)}}c=[];g=va(a.objects);h=0;for(l=g.length;h<l;++h)"GeometryCollection"===g[h].type?(m=g[h],c.push.apply(c,qt(m,f,d,e,b))):(m=g[h],c.push(rt(m,f,d,e,b)));return c}return[]};
+function tt(a,b,c){a[0]=a[0]*b[0]+c[0];a[1]=a[1]*b[1]+c[1]}ot.prototype.Wa=function(){return this.defaultDataProjection};
+var st={Point:function(a,b,c){a=a.coordinates;b&&c&&tt(a,b,c);return new C(a)},LineString:function(a,b){var c=pt(a.arcs,b);return new P(c)},Polygon:function(a,b){var c=[],d,e;d=0;for(e=a.arcs.length;d<e;++d)c[d]=pt(a.arcs[d],b);return new E(c)},MultiPoint:function(a,b,c){a=a.coordinates;var d,e;if(b&&c)for(d=0,e=a.length;d<e;++d)tt(a[d],b,c);return new R(a)},MultiLineString:function(a,b){var c=[],d,e;d=0;for(e=a.arcs.length;d<e;++d)c[d]=pt(a.arcs[d],b);return new Q(c)},MultiPolygon:function(a,b){var c=
+[],d,e,f,g,h,l;h=0;for(l=a.arcs.length;h<l;++h){d=a.arcs[h];e=[];f=0;for(g=d.length;f<g;++f)e[f]=pt(d[f],b);c[h]=e}return new S(c)}};function ut(a){a=a?a:{};this.i=a.featureType;this.g=a.featureNS;this.b=a.gmlFormat?a.gmlFormat:new tp;this.c=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd";dp.call(this)}v(ut,dp);ut.prototype.oc=function(a,b){var c={featureType:this.i,featureNS:this.g};ta(c,eo(this,a,b?b:{}));c=[c];this.b.b["http://www.opengis.net/gml"].featureMember=Rn(gp.prototype.Id);(c=N([],this.b.b,a,c,this.b))||(c=[]);return c};
+ut.prototype.o=function(a){if(Nn(a))return vt(a);if(On(a))return N({},wt,a,[]);if("string"===typeof a)return a=Pn(a),vt(a)};ut.prototype.l=function(a){if(Nn(a))return xt(this,a);if(On(a))return yt(this,a);if("string"===typeof a)return a=Pn(a),xt(this,a)};function xt(a,b){for(var c=b.firstChild;c;c=c.nextSibling)if(c.nodeType==Node.ELEMENT_NODE)return yt(a,c)}var zt={"http://www.opengis.net/gml":{boundedBy:K(gp.prototype.Ee,"bounds")}};
+function yt(a,b){var c={},d=op(b.getAttribute("numberOfFeatures"));c.numberOfFeatures=d;return N(c,zt,b,[],a.b)}
+var At={"http://www.opengis.net/wfs":{totalInserted:K(np),totalUpdated:K(np),totalDeleted:K(np)}},Bt={"http://www.opengis.net/ogc":{FeatureId:Rn(function(a){return a.getAttribute("fid")})}},Ct={"http://www.opengis.net/wfs":{Feature:function(a,b){Zn(Bt,a,b)}}},wt={"http://www.opengis.net/wfs":{TransactionSummary:K(function(a,b){return N({},At,a,b)},"transactionSummary"),InsertResults:K(function(a,b){return N([],Ct,a,b)},"insertIds")}};
+function vt(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return N({},wt,a,[])}var Dt={"http://www.opengis.net/wfs":{PropertyName:L(qp)}};function Et(a,b){var c=Kn("http://www.opengis.net/ogc","Filter"),d=Kn("http://www.opengis.net/ogc","FeatureId");c.appendChild(d);d.setAttribute("fid",b);a.appendChild(c)}
+var Ft={"http://www.opengis.net/wfs":{Insert:L(function(a,b,c){var d=c[c.length-1],d=Kn(d.featureNS,d.featureType);a.appendChild(d);tp.prototype.Ni(d,b,c)}),Update:L(function(a,b,c){var d=c[c.length-1];ha(void 0!==b.f,27);var e=d.featureType,f=d.featurePrefix,f=f?f:"feature",g=d.featureNS;a.setAttribute("typeName",f+":"+e);a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+f,g);e=b.f;if(void 0!==e){for(var f=b.S(),g=[],h=0,l=f.length;h<l;h++){var m=b.get(f[h]);void 0!==m&&g.push({name:f[h],
+value:m})}$n({node:a,srsName:d.srsName},Ft,Vn("Property"),g,c);Et(a,e)}}),Delete:L(function(a,b,c){var d=c[c.length-1];ha(void 0!==b.f,26);c=d.featureType;var e=d.featurePrefix,e=e?e:"feature",d=d.featureNS;a.setAttribute("typeName",e+":"+c);a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+e,d);b=b.f;void 0!==b&&Et(a,b)}),Property:L(function(a,b,c){var d=Kn("http://www.opengis.net/wfs","Name");a.appendChild(d);qp(d,b.name);void 0!==b.value&&null!==b.value&&(d=Kn("http://www.opengis.net/wfs",
+"Value"),a.appendChild(d),b.value instanceof Rc?tp.prototype.pd(d,b.value,c):qp(d,b.value))}),Native:L(function(a,b){b.yp&&a.setAttribute("vendorId",b.yp);void 0!==b.cp&&a.setAttribute("safeToIgnore",b.cp);void 0!==b.value&&qp(a,b.value)})}};function Gt(a,b,c){a={node:a};var d=b.b;$n(a,Ht,Vn(d.Nb),[d],c);b=b.a;$n(a,Ht,Vn(b.Nb),[b],c)}function It(a,b){void 0!==b.a&&a.setAttribute("matchCase",b.a.toString());Jt(a,b.b);Kt(a,""+b.g)}
+function Lt(a,b,c){a=Kn("http://www.opengis.net/ogc",a);qp(a,c);b.appendChild(a)}function Jt(a,b){Lt("PropertyName",a,b)}function Kt(a,b){Lt("Literal",a,b)}
+var Ht={"http://www.opengis.net/wfs":{Query:L(function(a,b,c){var d=c[c.length-1],e=d.featurePrefix,f=d.featureNS,g=d.propertyNames,h=d.srsName;a.setAttribute("typeName",(e?e+":":"")+b);h&&a.setAttribute("srsName",h);f&&a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+e,f);b=ta({},d);b.node=a;$n(b,Dt,Vn("PropertyName"),g,c);if(d=d.filter)g=Kn("http://www.opengis.net/ogc","Filter"),a.appendChild(g),$n({node:g},Ht,Vn(d.Nb),[d],c)})},"http://www.opengis.net/ogc":{And:L(Gt),Or:L(Gt),Not:L(function(a,
+b,c){b=b.condition;$n({node:a},Ht,Vn(b.Nb),[b],c)}),BBOX:L(function(a,b,c){c[c.length-1].srsName=b.srsName;Jt(a,b.geometryName);tp.prototype.pd(a,b.extent,c)}),Intersects:L(function(a,b,c){c[c.length-1].srsName=b.srsName;Jt(a,b.geometryName);tp.prototype.pd(a,b.geometry,c)}),Within:L(function(a,b,c){c[c.length-1].srsName=b.srsName;Jt(a,b.geometryName);tp.prototype.pd(a,b.geometry,c)}),PropertyIsEqualTo:L(It),PropertyIsNotEqualTo:L(It),PropertyIsLessThan:L(It),PropertyIsLessThanOrEqualTo:L(It),PropertyIsGreaterThan:L(It),
+PropertyIsGreaterThanOrEqualTo:L(It),PropertyIsNull:L(function(a,b){Jt(a,b.b)}),PropertyIsBetween:L(function(a,b){Jt(a,b.b);var c=Kn("http://www.opengis.net/ogc","LowerBoundary");a.appendChild(c);Kt(c,""+b.a);c=Kn("http://www.opengis.net/ogc","UpperBoundary");a.appendChild(c);Kt(c,""+b.g)}),PropertyIsLike:L(function(a,b){a.setAttribute("wildCard",b.i);a.setAttribute("singleChar",b.c);a.setAttribute("escapeChar",b.g);void 0!==b.a&&a.setAttribute("matchCase",b.a.toString());Jt(a,b.b);Kt(a,""+b.f)})}};
+ut.prototype.v=function(a){var b=Kn("http://www.opengis.net/wfs","GetFeature");b.setAttribute("service","WFS");b.setAttribute("version","1.1.0");var c;if(a&&(a.handle&&b.setAttribute("handle",a.handle),a.outputFormat&&b.setAttribute("outputFormat",a.outputFormat),void 0!==a.maxFeatures&&b.setAttribute("maxFeatures",a.maxFeatures),a.resultType&&b.setAttribute("resultType",a.resultType),void 0!==a.startIndex&&b.setAttribute("startIndex",a.startIndex),void 0!==a.count&&b.setAttribute("count",a.count),
+c=a.filter,a.bbox)){ha(a.geometryName,12);var d=Uo(a.geometryName,a.bbox,a.srsName);c?c=To(c,d):c=d}b.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.c);c={node:b,srsName:a.srsName,featureNS:a.featureNS?a.featureNS:this.g,featurePrefix:a.featurePrefix,geometryName:a.geometryName,filter:c,propertyNames:a.propertyNames?a.propertyNames:[]};ha(Array.isArray(a.featureTypes),11);a=a.featureTypes;c=[c];d=ta({},c[c.length-1]);d.node=b;$n(d,Ht,Vn("Query"),a,c);return b};
+ut.prototype.A=function(a,b,c,d){var e=[],f=Kn("http://www.opengis.net/wfs","Transaction");f.setAttribute("service","WFS");f.setAttribute("version","1.1.0");var g,h;d&&(g=d.gmlOptions?d.gmlOptions:{},d.handle&&f.setAttribute("handle",d.handle));f.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.c);a&&(h={node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},ta(h,g),$n(h,Ft,Vn("Insert"),a,e));b&&(h={node:f,featureNS:d.featureNS,
+featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},ta(h,g),$n(h,Ft,Vn("Update"),b,e));c&&$n({node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},Ft,Vn("Delete"),c,e);d.nativeElements&&$n({node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},Ft,Vn("Native"),d.nativeElements,e);return f};
+ut.prototype.Xf=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.He(a);return null};ut.prototype.He=function(a){if(a.firstElementChild&&a.firstElementChild.firstElementChild)for(a=a.firstElementChild.firstElementChild,a=a.firstElementChild;a;a=a.nextElementSibling)if(0!==a.childNodes.length&&(1!==a.childNodes.length||3!==a.firstChild.nodeType)){var b=[{}];this.b.Ee(a,b);return zc(b.pop().srsName)}return null};function Mt(a){a=a?a:{};co.call(this);this.b=void 0!==a.splitCollection?a.splitCollection:!1}v(Mt,lq);function Nt(a){a=a.$();return 0===a.length?"":a.join(" ")}function Ot(a){a=a.$();for(var b=[],c=0,d=a.length;c<d;++c)b.push(a[c].join(" "));return b.join(",")}function Pt(a){var b=[];a=a.Zc();for(var c=0,d=a.length;c<d;++c)b.push("("+Ot(a[c])+")");return b.join(",")}
+function Qt(a){var b=a.Y(),c=(0,Rt[b])(a),b=b.toUpperCase();if(a instanceof Uc){a=a.ka;var d="";if("XYZ"===a||"XYZM"===a)d+="Z";if("XYM"===a||"XYZM"===a)d+="M";a=d;0<a.length&&(b+=" "+a)}return 0===c.length?b+" EMPTY":b+"("+c+")"}
+var Rt={Point:Nt,LineString:Ot,Polygon:Pt,MultiPoint:function(a){var b=[];a=a.re();for(var c=0,d=a.length;c<d;++c)b.push("("+Nt(a[c])+")");return b.join(",")},MultiLineString:function(a){var b=[];a=a.Yc();for(var c=0,d=a.length;c<d;++c)b.push("("+Ot(a[c])+")");return b.join(",")},MultiPolygon:function(a){var b=[];a=a.Ad();for(var c=0,d=a.length;c<d;++c)b.push("("+Pt(a[c])+")");return b.join(",")},GeometryCollection:function(a){var b=[];a=a.pf();for(var c=0,d=a.length;c<d;++c)b.push(Qt(a[c]));return b.join(",")}};
+k=Mt.prototype;k.Hd=function(a,b){var c=this.Jd(a,b);if(c){var d=new J;d.Pa(c);return d}return null};k.Tf=function(a,b){var c=[],d=this.Jd(a,b);this.b&&"GeometryCollection"==d.Y()?c=d.f:c=[d];for(var e=[],f=0,g=c.length;f<g;++f)d=new J,d.Pa(c[f]),e.push(d);return e};k.Jd=function(a,b){var c;c=new St(new Tt(a));Ut(c);return(c=Vt(c))?go(c,!1,b):null};k.Re=function(a,b){var c=a.V();return c?this.Pd(c,b):""};
+k.Oi=function(a,b){if(1==a.length)return this.Re(a[0],b);for(var c=[],d=0,e=a.length;d<e;++d)c.push(a[d].V());c=new Vo(c);return this.Pd(c,b)};k.Pd=function(a,b){return Qt(go(a,!0,b))};function Tt(a){this.a=a;this.b=-1}
+function Wt(a){var b=a.a.charAt(++a.b),c={position:a.b,value:b};if("("==b)c.type=2;else if(","==b)c.type=5;else if(")"==b)c.type=3;else if("0"<=b&&"9">=b||"."==b||"-"==b){c.type=4;var d,b=a.b,e=!1,f=!1;do{if("."==d)e=!0;else if("e"==d||"E"==d)f=!0;d=a.a.charAt(++a.b)}while("0"<=d&&"9">=d||"."==d&&(void 0===e||!e)||!f&&("e"==d||"E"==d)||f&&("-"==d||"+"==d));a=parseFloat(a.a.substring(b,a.b--));c.value=a}else if("a"<=b&&"z">=b||"A"<=b&&"Z">=b){c.type=1;b=a.b;do d=a.a.charAt(++a.b);while("a"<=d&&"z">=
+d||"A"<=d&&"Z">=d);a=a.a.substring(b,a.b--).toUpperCase();c.value=a}else{if(" "==b||"\t"==b||"\r"==b||"\n"==b)return Wt(a);if(""===b)c.type=6;else throw Error("Unexpected character: "+b);}return c}function St(a){this.g=a;this.a="XY"}function Ut(a){a.b=Wt(a.g)}function Xt(a,b){var c=a.b.type==b;c&&Ut(a);return c}
+function Vt(a){var b=a.b;if(Xt(a,1)){var b=b.value,c="XY",d=a.b;1==a.b.type&&(d=d.value,"Z"===d?c="XYZ":"M"===d?c="XYM":"ZM"===d&&(c="XYZM"),"XY"!==c&&Ut(a));a.a=c;if("GEOMETRYCOLLECTION"==b){a:{if(Xt(a,2)){b=[];do b.push(Vt(a));while(Xt(a,5));if(Xt(a,3)){a=b;break a}}else if(Yt(a)){a=[];break a}throw Error(Zt(a));}return new Vo(a)}d=$t[b];c=au[b];if(!d||!c)throw Error("Invalid geometry type: "+b);b=d.call(a);return new c(b,a.a)}throw Error(Zt(a));}k=St.prototype;
+k.Of=function(){if(Xt(this,2)){var a=bu(this);if(Xt(this,3))return a}else if(Yt(this))return null;throw Error(Zt(this));};k.Nf=function(){if(Xt(this,2)){var a=cu(this);if(Xt(this,3))return a}else if(Yt(this))return[];throw Error(Zt(this));};k.Pf=function(){if(Xt(this,2)){var a=du(this);if(Xt(this,3))return a}else if(Yt(this))return[];throw Error(Zt(this));};
+k.mo=function(){if(Xt(this,2)){var a;if(2==this.b.type)for(a=[this.Of()];Xt(this,5);)a.push(this.Of());else a=cu(this);if(Xt(this,3))return a}else if(Yt(this))return[];throw Error(Zt(this));};k.lo=function(){if(Xt(this,2)){var a=du(this);if(Xt(this,3))return a}else if(Yt(this))return[];throw Error(Zt(this));};k.no=function(){if(Xt(this,2)){for(var a=[this.Pf()];Xt(this,5);)a.push(this.Pf());if(Xt(this,3))return a}else if(Yt(this))return[];throw Error(Zt(this));};
+function bu(a){for(var b=[],c=a.a.length,d=0;d<c;++d){var e=a.b;if(Xt(a,4))b.push(e.value);else break}if(b.length==c)return b;throw Error(Zt(a));}function cu(a){for(var b=[bu(a)];Xt(a,5);)b.push(bu(a));return b}function du(a){for(var b=[a.Nf()];Xt(a,5);)b.push(a.Nf());return b}function Yt(a){var b=1==a.b.type&&"EMPTY"==a.b.value;b&&Ut(a);return b}function Zt(a){return"Unexpected `"+a.b.value+"` at position "+a.b.position+" in `"+a.g.a+"`"}
+var au={POINT:C,LINESTRING:P,POLYGON:E,MULTIPOINT:R,MULTILINESTRING:Q,MULTIPOLYGON:S},$t={POINT:St.prototype.Of,LINESTRING:St.prototype.Nf,POLYGON:St.prototype.Pf,MULTIPOINT:St.prototype.mo,MULTILINESTRING:St.prototype.lo,MULTIPOLYGON:St.prototype.no};function eu(){this.version=void 0}v(eu,Ss);eu.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};eu.prototype.b=function(a){this.version=a.getAttribute("version").trim();return(a=N({version:this.version},fu,a,[]))?a:null};function gu(a,b){return N({},hu,a,b)}function iu(a,b){return N({},ju,a,b)}function ku(a,b){var c=gu(a,b);if(c){var d=[op(a.getAttribute("width")),op(a.getAttribute("height"))];c.size=d;return c}}
+function lu(a,b){return N([],mu,a,b)}
+var nu=[null,"http://www.opengis.net/wms"],fu=M(nu,{Service:K(function(a,b){return N({},ou,a,b)}),Capability:K(function(a,b){return N({},pu,a,b)})}),pu=M(nu,{Request:K(function(a,b){return N({},qu,a,b)}),Exception:K(function(a,b){return N([],ru,a,b)}),Layer:K(function(a,b){return N({},su,a,b)})}),ou=M(nu,{Name:K(T),Title:K(T),Abstract:K(T),KeywordList:K(lu),OnlineResource:K(Rs),ContactInformation:K(function(a,b){return N({},tu,a,b)}),Fees:K(T),AccessConstraints:K(T),LayerLimit:K(np),MaxWidth:K(np),
+MaxHeight:K(np)}),tu=M(nu,{ContactPersonPrimary:K(function(a,b){return N({},uu,a,b)}),ContactPosition:K(T),ContactAddress:K(function(a,b){return N({},vu,a,b)}),ContactVoiceTelephone:K(T),ContactFacsimileTelephone:K(T),ContactElectronicMailAddress:K(T)}),uu=M(nu,{ContactPerson:K(T),ContactOrganization:K(T)}),vu=M(nu,{AddressType:K(T),Address:K(T),City:K(T),StateOrProvince:K(T),PostCode:K(T),Country:K(T)}),ru=M(nu,{Format:Rn(T)}),su=M(nu,{Name:K(T),Title:K(T),Abstract:K(T),KeywordList:K(lu),CRS:Tn(T),
+EX_GeographicBoundingBox:K(function(a,b){var c=N({},wu,a,b);if(c){var d=c.westBoundLongitude,e=c.southBoundLatitude,f=c.eastBoundLongitude,c=c.northBoundLatitude;return void 0===d||void 0===e||void 0===f||void 0===c?void 0:[d,e,f,c]}}),BoundingBox:Tn(function(a){var b=[mp(a.getAttribute("minx")),mp(a.getAttribute("miny")),mp(a.getAttribute("maxx")),mp(a.getAttribute("maxy"))],c=[mp(a.getAttribute("resx")),mp(a.getAttribute("resy"))];return{crs:a.getAttribute("CRS"),extent:b,res:c}}),Dimension:Tn(function(a){return{name:a.getAttribute("name"),
+units:a.getAttribute("units"),unitSymbol:a.getAttribute("unitSymbol"),"default":a.getAttribute("default"),multipleValues:jp(a.getAttribute("multipleValues")),nearestValue:jp(a.getAttribute("nearestValue")),current:jp(a.getAttribute("current")),values:T(a)}}),Attribution:K(function(a,b){return N({},xu,a,b)}),AuthorityURL:Tn(function(a,b){var c=gu(a,b);if(c)return c.name=a.getAttribute("name"),c}),Identifier:Tn(T),MetadataURL:Tn(function(a,b){var c=gu(a,b);if(c)return c.type=a.getAttribute("type"),
+c}),DataURL:Tn(gu),FeatureListURL:Tn(gu),Style:Tn(function(a,b){return N({},yu,a,b)}),MinScaleDenominator:K(lp),MaxScaleDenominator:K(lp),Layer:Tn(function(a,b){var c=b[b.length-1],d=N({},su,a,b);if(d){var e=jp(a.getAttribute("queryable"));void 0===e&&(e=c.queryable);d.queryable=void 0!==e?e:!1;e=op(a.getAttribute("cascaded"));void 0===e&&(e=c.cascaded);d.cascaded=e;e=jp(a.getAttribute("opaque"));void 0===e&&(e=c.opaque);d.opaque=void 0!==e?e:!1;e=jp(a.getAttribute("noSubsets"));void 0===e&&(e=c.noSubsets);
+d.noSubsets=void 0!==e?e:!1;(e=mp(a.getAttribute("fixedWidth")))||(e=c.fixedWidth);d.fixedWidth=e;(e=mp(a.getAttribute("fixedHeight")))||(e=c.fixedHeight);d.fixedHeight=e;["Style","CRS","AuthorityURL"].forEach(function(a){a in c&&(d[a]=(d[a]||[]).concat(c[a]))});"EX_GeographicBoundingBox BoundingBox Dimension Attribution MinScaleDenominator MaxScaleDenominator".split(" ").forEach(function(a){a in d||(d[a]=c[a])});return d}})}),xu=M(nu,{Title:K(T),OnlineResource:K(Rs),LogoURL:K(ku)}),wu=M(nu,{westBoundLongitude:K(lp),
+eastBoundLongitude:K(lp),southBoundLatitude:K(lp),northBoundLatitude:K(lp)}),qu=M(nu,{GetCapabilities:K(iu),GetMap:K(iu),GetFeatureInfo:K(iu)}),ju=M(nu,{Format:Tn(T),DCPType:Tn(function(a,b){return N({},zu,a,b)})}),zu=M(nu,{HTTP:K(function(a,b){return N({},Au,a,b)})}),Au=M(nu,{Get:K(gu),Post:K(gu)}),yu=M(nu,{Name:K(T),Title:K(T),Abstract:K(T),LegendURL:Tn(ku),StyleSheetURL:K(gu),StyleURL:K(gu)}),hu=M(nu,{Format:K(T),OnlineResource:K(Rs)}),mu=M(nu,{Keyword:Rn(T)});function Bu(a){a=a?a:{};this.g="http://mapserver.gis.umn.edu/mapserver";this.b=new Cp;this.c=a.layers?a.layers:null;dp.call(this)}v(Bu,dp);
+Bu.prototype.oc=function(a,b){var c={};b&&ta(c,eo(this,a,b));var d=[c];a.setAttribute("namespaceURI",this.g);var e=a.localName,c=[];if(0!==a.childNodes.length){if("msGMLOutput"==e)for(var f=0,g=a.childNodes.length;f<g;f++){var h=a.childNodes[f];if(h.nodeType===Node.ELEMENT_NODE){var l=d[0],m=h.localName.replace("_layer","");if(!this.c||Za(this.c,m)){m+="_feature";l.featureType=m;l.featureNS=this.g;var p={};p[m]=Rn(this.b.Rf,this.b);l=M([l.featureNS,null],p);h.setAttribute("namespaceURI",this.g);(h=
+N([],l,h,d,this.b))&&bb(c,h)}}}"FeatureCollection"==e&&(d=N([],this.b.b,a,[{}],this.b))&&(c=d)}return c};function Cu(){this.g=new Ts}v(Cu,Ss);Cu.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};Cu.prototype.b=function(a){var b=a.getAttribute("version").trim(),c=this.g.b(a);if(!c)return null;c.version=b;return(c=N(c,Du,a,[]))?c:null};function Eu(a){var b=T(a).split(" ");if(b&&2==b.length)return a=+b[0],b=+b[1],isNaN(a)||isNaN(b)?void 0:[a,b]}
+var Fu=[null,"http://www.opengis.net/wmts/1.0"],Gu=[null,"http://www.opengis.net/ows/1.1"],Du=M(Fu,{Contents:K(function(a,b){return N({},Hu,a,b)})}),Hu=M(Fu,{Layer:Tn(function(a,b){return N({},Iu,a,b)}),TileMatrixSet:Tn(function(a,b){return N({},Ju,a,b)})}),Iu=M(Fu,{Style:Tn(function(a,b){var c=N({},Ku,a,b);if(c){var d="true"===a.getAttribute("isDefault");c.isDefault=d;return c}}),Format:Tn(T),TileMatrixSetLink:Tn(function(a,b){return N({},Lu,a,b)}),Dimension:Tn(function(a,b){return N({},Mu,a,b)}),
+ResourceURL:Tn(function(a){var b=a.getAttribute("format"),c=a.getAttribute("template");a=a.getAttribute("resourceType");var d={};b&&(d.format=b);c&&(d.template=c);a&&(d.resourceType=a);return d})},M(Gu,{Title:K(T),Abstract:K(T),WGS84BoundingBox:K(function(a,b){var c=N([],Nu,a,b);return 2!=c.length?void 0:Gb(c)}),Identifier:K(T)})),Ku=M(Fu,{LegendURL:Tn(function(a){var b={};b.format=a.getAttribute("format");b.href=Rs(a);return b})},M(Gu,{Title:K(T),Identifier:K(T)})),Lu=M(Fu,{TileMatrixSet:K(T),TileMatrixSetLimits:K(function(a,
+b){return N([],Ou,a,b)})}),Ou=M(Fu,{TileMatrixLimits:Rn(function(a,b){return N({},Pu,a,b)})}),Pu=M(Fu,{TileMatrix:K(T),MinTileRow:K(np),MaxTileRow:K(np),MinTileCol:K(np),MaxTileCol:K(np)}),Mu=M(Fu,{Default:K(T),Value:Tn(T)},M(Gu,{Identifier:K(T)})),Nu=M(Gu,{LowerCorner:Rn(Eu),UpperCorner:Rn(Eu)}),Ju=M(Fu,{WellKnownScaleSet:K(T),TileMatrix:Tn(function(a,b){return N({},Qu,a,b)})},M(Gu,{SupportedCRS:K(T),Identifier:K(T)})),Qu=M(Fu,{TopLeftCorner:K(Eu),ScaleDenominator:K(lp),TileWidth:K(np),TileHeight:K(np),
+MatrixWidth:K(np),MatrixHeight:K(np)},M(Gu,{Identifier:K(T)}));function Ru(a){Sa.call(this);a=a||{};this.a=null;this.c=Oc;this.f=void 0;B(this,Ua(Su),this.Ql,this);B(this,Ua(Tu),this.Rl,this);void 0!==a.projection&&this.nh(zc(a.projection));void 0!==a.trackingOptions&&this.Ci(a.trackingOptions);this.ne(void 0!==a.tracking?a.tracking:!1)}v(Ru,Sa);k=Ru.prototype;k.oa=function(){this.ne(!1);Sa.prototype.oa.call(this)};k.Ql=function(){var a=this.lh();a&&(this.c=yc(zc("EPSG:4326"),a),this.a&&this.set(Uu,this.c(this.a)))};
+k.Rl=function(){if(qf){var a=this.mh();a&&void 0===this.f?this.f=navigator.geolocation.watchPosition(this.uo.bind(this),this.vo.bind(this),this.Wg()):a||void 0===this.f||(navigator.geolocation.clearWatch(this.f),this.f=void 0)}};
+k.uo=function(a){a=a.coords;this.set(Vu,a.accuracy);this.set(Wu,null===a.altitude?void 0:a.altitude);this.set(Xu,null===a.altitudeAccuracy?void 0:a.altitudeAccuracy);this.set(Yu,null===a.heading?void 0:na(a.heading));this.a?(this.a[0]=a.longitude,this.a[1]=a.latitude):this.a=[a.longitude,a.latitude];var b=this.c(this.a);this.set(Uu,b);this.set(Zu,null===a.speed?void 0:a.speed);a=Bd(Eh,this.a,a.accuracy);a.sc(this.c);this.set($u,a);this.s()};k.vo=function(a){a.type="error";this.ne(!1);this.b(a)};
+k.Tj=function(){return this.get(Vu)};k.Uj=function(){return this.get($u)||null};k.Wj=function(){return this.get(Wu)};k.Xj=function(){return this.get(Xu)};k.Ol=function(){return this.get(Yu)};k.Pl=function(){return this.get(Uu)};k.lh=function(){return this.get(Su)};k.Bk=function(){return this.get(Zu)};k.mh=function(){return this.get(Tu)};k.Wg=function(){return this.get(av)};k.nh=function(a){this.set(Su,a)};k.ne=function(a){this.set(Tu,a)};k.Ci=function(a){this.set(av,a)};
+var Vu="accuracy",$u="accuracyGeometry",Wu="altitude",Xu="altitudeAccuracy",Yu="heading",Uu="position",Su="projection",Zu="speed",Tu="tracking",av="trackingOptions";function bv(a,b,c){Uc.call(this);this.dg(a,b?b:0,c)}v(bv,Uc);k=bv.prototype;k.clone=function(){var a=new bv(null);Xc(a,this.ka,this.B.slice());a.s();return a};k.Ab=function(a,b,c,d){var e=this.B;a-=e[0];var f=b-e[1];b=a*a+f*f;if(b<d){if(0===b)for(d=0;d<this.a;++d)c[d]=e[d];else for(d=this.qe()/Math.sqrt(b),c[0]=e[0]+d*a,c[1]=e[1]+d*f,d=2;d<this.a;++d)c[d]=e[d];c.length=this.a;return b}return d};k.Hc=function(a,b){var c=this.B,d=a-c[0],c=b-c[1];return d*d+c*c<=cv(this)};
+k.Fd=function(){return this.B.slice(0,this.a)};k.Yd=function(a){var b=this.B,c=b[this.a]-b[0];return Rb(b[0]-c,b[1]-c,b[0]+c,b[1]+c,a)};k.qe=function(){return Math.sqrt(cv(this))};function cv(a){var b=a.B[a.a]-a.B[0];a=a.B[a.a+1]-a.B[1];return b*b+a*a}k.Y=function(){return"Circle"};k.Ta=function(a){var b=this.G();return jc(a,b)?(b=this.Fd(),a[0]<=b[0]&&a[2]>=b[0]||a[1]<=b[1]&&a[3]>=b[1]?!0:Xb(a,this.mb,this)):!1};
+k.nm=function(a){var b=this.a,c=a.slice();c[b]=c[0]+(this.B[b]-this.B[0]);var d;for(d=1;d<b;++d)c[b+d]=a[d];Xc(this,this.ka,c);this.s()};k.dg=function(a,b,c){if(a){Yc(this,c,a,0);this.B||(this.B=[]);c=this.B;a=fd(c,a);c[a++]=c[0]+b;var d;b=1;for(d=this.a;b<d;++b)c[a++]=c[b];c.length=a}else Xc(this,"XY",null);this.s()};k.om=function(a){this.B[this.a]=this.B[0]+a;this.s()};function dv(a,b,c){for(var d=[],e=a(0),f=a(1),g=b(e),h=b(f),l=[f,e],m=[h,g],p=[1,0],n={},q=1E5,r,u,w,y,z;0<--q&&0<p.length;)w=p.pop(),e=l.pop(),g=m.pop(),f=w.toString(),f in n||(d.push(g[0],g[1]),n[f]=!0),y=p.pop(),f=l.pop(),h=m.pop(),z=(w+y)/2,r=a(z),u=b(r),la(u[0],u[1],g[0],g[1],h[0],h[1])<c?(d.push(h[0],h[1]),f=y.toString(),n[f]=!0):(p.push(y,z,z,w),m.push(h,u,u,g),l.push(f,r,r,e));return d}function ev(a,b,c,d,e){var f=zc("EPSG:4326");return dv(function(d){return[a,b+(c-b)*d]},Nc(f,d),e)}
+function fv(a,b,c,d,e){var f=zc("EPSG:4326");return dv(function(d){return[b+(c-b)*d,a]},Nc(f,d),e)};function gv(a){a=a||{};this.c=this.l=null;this.g=this.i=Infinity;this.f=this.j=-Infinity;this.A=this.u=Infinity;this.D=this.C=-Infinity;this.va=void 0!==a.targetSize?a.targetSize:100;this.L=void 0!==a.maxLines?a.maxLines:100;this.b=[];this.a=[];this.ra=void 0!==a.strokeStyle?a.strokeStyle:hv;this.H=this.o=void 0;this.v=null;this.setMap(void 0!==a.map?a.map:null)}var hv=new wi({color:"rgba(0,0,0,0.2)"}),iv=[90,45,30,20,10,5,2,1,.5,.2,.1,.05,.01,.005,.002,.001];
+function jv(a,b,c,d,e,f,g){var h=g;b=ev(b,c,d,a.c,e);h=void 0!==a.b[h]?a.b[h]:new P(null);h.da("XY",b);jc(h.G(),f)&&(a.b[g++]=h);return g}function kv(a,b,c,d,e){var f=e;b=fv(b,a.f,a.g,a.c,c);f=void 0!==a.a[f]?a.a[f]:new P(null);f.da("XY",b);jc(f.G(),d)&&(a.a[e++]=f);return e}k=gv.prototype;k.Sl=function(){return this.l};k.qk=function(){return this.b};k.xk=function(){return this.a};
+k.ah=function(a){var b=a.vectorContext,c=a.frameState,d=c.extent;a=c.viewState;var e=a.center,f=a.projection,g=a.resolution;a=c.pixelRatio;a=g*g/(4*a*a);if(!this.c||!Mc(this.c,f)){var h=zc("EPSG:4326"),l=f.G(),m=f.i,p=Qc(m,h,f),n=m[2],q=m[1],r=m[0],u=p[3],w=p[2],y=p[1],p=p[0];this.i=m[3];this.g=n;this.j=q;this.f=r;this.u=u;this.A=w;this.C=y;this.D=p;this.o=Nc(h,f);this.H=Nc(f,h);this.v=this.H(gc(l));this.c=f}f.a&&(f=f.G(),h=dc(f),c=c.focus[0],c<f[0]||c>f[2])&&(c=h*Math.ceil((f[0]-c)/h),d=[d[0]+c,
+d[1],d[2]+c,d[3]]);c=this.v[0];f=this.v[1];h=-1;m=Math.pow(this.va*g,2);n=[];q=[];g=0;for(l=iv.length;g<l;++g){r=iv[g]/2;n[0]=c-r;n[1]=f-r;q[0]=c+r;q[1]=f+r;this.o(n,n);this.o(q,q);r=Math.pow(q[0]-n[0],2)+Math.pow(q[1]-n[1],2);if(r<=m)break;h=iv[g]}g=h;if(-1==g)this.b.length=this.a.length=0;else{c=this.H(e);e=c[0];c=c[1];f=this.L;h=[Math.max(d[0],this.D),Math.max(d[1],this.C),Math.min(d[2],this.A),Math.min(d[3],this.u)];h=Qc(h,this.c,"EPSG:4326");m=h[3];q=h[1];e=Math.floor(e/g)*g;n=ia(e,this.f,this.g);
+l=jv(this,n,q,m,a,d,0);for(h=0;n!=this.f&&h++<f;)n=Math.max(n-g,this.f),l=jv(this,n,q,m,a,d,l);n=ia(e,this.f,this.g);for(h=0;n!=this.g&&h++<f;)n=Math.min(n+g,this.g),l=jv(this,n,q,m,a,d,l);this.b.length=l;c=Math.floor(c/g)*g;e=ia(c,this.j,this.i);l=kv(this,e,a,d,0);for(h=0;e!=this.j&&h++<f;)e=Math.max(e-g,this.j),l=kv(this,e,a,d,l);e=ia(c,this.j,this.i);for(h=0;e!=this.i&&h++<f;)e=Math.min(e+g,this.i),l=kv(this,e,a,d,l);this.a.length=l}b.Ma(null,this.ra);a=0;for(e=this.b.length;a<e;++a)g=this.b[a],
+b.Pb(g,null);a=0;for(e=this.a.length;a<e;++a)g=this.a[a],b.Pb(g,null)};k.setMap=function(a){this.l&&(this.l.K("postcompose",this.ah,this),this.l.render());a&&(a.J("postcompose",this.ah,this),a.render());this.l=a};function lv(a,b,c,d,e){ag.call(this,a,b);this.l=c;this.g=new Image;null!==d&&(this.g.crossOrigin=d);this.i=null;this.o=e}v(lv,ag);k=lv.prototype;k.oa=function(){1==this.state&&mv(this);this.a&&Ha(this.a);this.state=5;this.s();ag.prototype.oa.call(this)};k.ub=function(){return this.g};k.bb=function(){return this.l};k.Tl=function(){this.state=3;mv(this);this.s()};k.Ul=function(){this.state=this.g.naturalWidth&&this.g.naturalHeight?cg:4;mv(this);this.s()};
+k.load=function(){if(0==this.state||3==this.state)this.state=1,this.s(),this.i=[Da(this.g,"error",this.Tl,this),Da(this.g,"load",this.Ul,this)],this.o(this,this.l)};function mv(a){a.i.forEach(ya);a.i=null};function nv(a){a=a?a:{};lg.call(this,{handleEvent:mc});this.i=a.formatConstructors?a.formatConstructors:[];this.l=a.projection?zc(a.projection):null;this.a=null;this.target=a.target?a.target:null}v(nv,lg);function ov(a){a=a.dataTransfer.files;var b,c,d;b=0;for(c=a.length;b<c;++b){d=a.item(b);var e=new FileReader;e.addEventListener("load",this.j.bind(this,d));e.readAsText(d)}}function pv(a){a.stopPropagation();a.preventDefault();a.dataTransfer.dropEffect="copy"}
+nv.prototype.j=function(a,b){var c=b.target.result,d=this.v,e=this.l;e||(e=d.aa().o);var d=this.i,f=[],g,h;g=0;for(h=d.length;g<h;++g){var l=new d[g];var m={featureProjection:e};try{f=l.La(c,m)}catch(p){f=null}if(f&&0<f.length)break}this.b(new qv(rv,a,f,e))};nv.prototype.setMap=function(a){this.a&&(this.a.forEach(ya),this.a=null);lg.prototype.setMap.call(this,a);a&&(a=this.target?this.target:a.f,this.a=[B(a,"drop",ov,this),B(a,"dragenter",pv,this),B(a,"dragover",pv,this),B(a,"drop",pv,this)])};
+var rv="addfeatures";function qv(a,b,c,d){Ia.call(this,a);this.features=c;this.file=b;this.projection=d}v(qv,Ia);function sv(a){a=a?a:{};Bg.call(this,{handleDownEvent:tv,handleDragEvent:uv,handleUpEvent:vv});this.o=a.condition?a.condition:xg;this.a=this.i=void 0;this.j=0;this.u=void 0!==a.duration?a.duration:400}v(sv,Bg);function uv(a){if(zg(a)){var b=a.map,c=b.nb(),d=a.pixel;a=d[0]-c[0]/2;d=c[1]/2-d[1];c=Math.atan2(d,a);a=Math.sqrt(a*a+d*d);b=b.aa();void 0!==this.i&&(d=c-this.i,ng(b,b.Ra()-d));this.i=c;void 0!==this.a&&(c=this.a*(b.Oa()/a),pg(b,c));void 0!==this.a&&(this.j=this.a/a);this.a=a}}
+function vv(a){if(!zg(a))return!0;a=a.map.aa();Jd(a,1,-1);var b=this.j-1,c=a.Ra(),c=a.constrainRotation(c,0);ng(a,c,void 0,void 0);var c=a.Oa(),d=this.u,c=a.constrainResolution(c,0,b);pg(a,c,void 0,d);this.j=0;return!1}function tv(a){return zg(a)&&this.o(a)?(Jd(a.map.aa(),1,1),this.a=this.i=void 0,!0):!1};function wv(){return[[-Infinity,-Infinity,Infinity,Infinity]]};function U(a){a=a||{};hm.call(this,{attributions:a.attributions,logo:a.logo,projection:void 0,state:"ready",wrapX:void 0!==a.wrapX?a.wrapX:!0});this.U=ea;this.P=a.format;this.xa=void 0==a.overlaps?!0:a.overlaps;this.Z=a.url;void 0!==a.loader?this.U=a.loader:void 0!==this.Z&&(ha(this.P,7),this.U=bo(this.Z,this.P));this.fc=void 0!==a.strategy?a.strategy:wv;var b=void 0!==a.useSpatialIndex?a.useSpatialIndex:!0;this.a=b?new jl:null;this.sa=new jl;this.i={};this.l={};this.o={};this.v={};this.c=null;var c,
+d;a.features instanceof qe?(c=a.features,d=c.a):Array.isArray(a.features)&&(d=a.features);b||void 0!==c||(c=new qe(d));void 0!==d&&xv(this,d);void 0!==c&&yv(this,c)}v(U,hm);k=U.prototype;k.gb=function(a){var b=x(a).toString();if(zv(this,b,a)){Av(this,b,a);var c=a.V();c?(b=c.G(),this.a&&this.a.Da(b,a)):this.i[b]=a;this.b(new Bv(Cv,a))}this.s()};function Av(a,b,c){a.v[b]=[B(c,"change",a.Mh,a),B(c,Xa,a.Mh,a)]}
+function zv(a,b,c){var d=!0,e=c.f;void 0!==e?e.toString()in a.l?d=!1:a.l[e.toString()]=c:(ha(!(b in a.o),30),a.o[b]=c);return d}k.Tc=function(a){xv(this,a);this.s()};function xv(a,b){var c,d,e,f,g=[],h=[],l=[];d=0;for(e=b.length;d<e;d++)f=b[d],c=x(f).toString(),zv(a,c,f)&&h.push(f);d=0;for(e=h.length;d<e;d++){f=h[d];c=x(f).toString();Av(a,c,f);var m=f.V();m?(c=m.G(),g.push(c),l.push(f)):a.i[c]=f}a.a&&a.a.load(g,l);d=0;for(e=h.length;d<e;d++)a.b(new Bv(Cv,h[d]))}
+function yv(a,b){var c=!1;B(a,Cv,function(a){c||(c=!0,b.push(a.feature),c=!1)});B(a,Dv,function(a){c||(c=!0,b.remove(a.feature),c=!1)});B(b,ue,function(a){c||(c=!0,this.gb(a.element),c=!1)},a);B(b,ve,function(a){c||(c=!0,this.rb(a.element),c=!1)},a);a.c=b}
+k.clear=function(a){if(a){for(var b in this.v)this.v[b].forEach(ya);this.c||(this.v={},this.l={},this.o={})}else if(this.a){this.a.forEach(this.$f,this);for(var c in this.i)this.$f(this.i[c])}this.c&&this.c.clear();this.a&&this.a.clear();this.sa.clear();this.i={};this.b(new Bv(Ev));this.s()};k.Fg=function(a,b){if(this.a)return this.a.forEach(a,b);if(this.c)return this.c.forEach(a,b)};function Fv(a,b,c){a.Qb([b[0],b[1],b[0],b[1]],function(a){if(a.V().mb(b))return c.call(void 0,a)})}
+k.Qb=function(a,b,c){if(this.a)return pl(this.a,a,b,c);if(this.c)return this.c.forEach(b,c)};k.Gg=function(a,b,c){return this.Qb(a,function(d){if(d.V().Ta(a)&&(d=b.call(c,d)))return d})};k.Og=function(){return this.c};k.we=function(){var a;this.c?a=this.c.a:this.a&&(a=ll(this.a),wa(this.i)||bb(a,va(this.i)));return a};k.Ng=function(a){var b=[];Fv(this,a,function(a){b.push(a)});return b};k.nf=function(a){return ml(this.a,a)};
+k.Jg=function(a,b){var c=a[0],d=a[1],e=null,f=[NaN,NaN],g=Infinity,h=[-Infinity,-Infinity,Infinity,Infinity],l=b?b:mc;pl(this.a,h,function(a){if(l(a)){var b=a.V(),n=g;g=b.Ab(c,d,f,g);g<n&&(e=a,a=Math.sqrt(g),h[0]=c-a,h[1]=d-a,h[2]=c+a,h[3]=d+a)}});return e};k.G=function(){return this.a.G()};k.Mg=function(a){a=this.l[a.toString()];return void 0!==a?a:null};k.Kh=function(){return this.P};k.Lh=function(){return this.Z};
+k.Mh=function(a){a=a.target;var b=x(a).toString(),c=a.V();c?(c=c.G(),b in this.i?(delete this.i[b],this.a&&this.a.Da(c,a)):this.a&&kl(this.a,c,a)):b in this.i||(this.a&&this.a.remove(a),this.i[b]=a);c=a.f;void 0!==c?(c=c.toString(),b in this.o?(delete this.o[b],this.l[c]=a):this.l[c]!==a&&(Gv(this,a),this.l[c]=a)):b in this.o||(Gv(this,a),this.o[b]=a);this.s();this.b(new Bv(Hv,a))};
+k.Ed=function(a,b,c){var d=this.sa;a=this.fc(a,b);var e,f;e=0;for(f=a.length;e<f;++e){var g=a[e];pl(d,g,function(a){return Ob(a.extent,g)})||(this.U.call(this,g,b,c),d.Da(g,{extent:g.slice()}))}};k.rb=function(a){var b=x(a).toString();b in this.i?delete this.i[b]:this.a&&this.a.remove(a);this.$f(a);this.s()};k.$f=function(a){var b=x(a).toString();this.v[b].forEach(ya);delete this.v[b];var c=a.f;void 0!==c?delete this.l[c.toString()]:delete this.o[b];this.b(new Bv(Dv,a))};
+function Gv(a,b){for(var c in a.l)if(a.l[c]===b){delete a.l[c];break}}function Bv(a,b){Ia.call(this,a);this.feature=b}v(Bv,Ia);var Cv="addfeature",Hv="changefeature",Ev="clear",Dv="removefeature";function Iv(a){Bg.call(this,{handleDownEvent:Jv,handleEvent:Kv,handleUpEvent:Lv});this.fa=null;this.u=!1;this.Ua=a.source?a.source:null;this.xa=a.features?a.features:null;this.Mj=a.snapTolerance?a.snapTolerance:12;this.U=a.type;this.i=Mv(this.U);this.Fa=a.minPoints?a.minPoints:this.i===Nv?3:2;this.na=a.maxPoints?a.maxPoints:Infinity;this.fc=a.finishCondition?a.finishCondition:mc;var b=a.geometryFunction;if(!b)if("Circle"===this.U)b=function(a,b){var c=b?b:new bv([NaN,NaN]);c.dg(a[0],Math.sqrt(yb(a[0],
+a[1])));return c};else{var c,d=this.i;d===Ov?c=C:d===Pv?c=P:d===Nv&&(c=E);b=function(a,b){var g=b;g?d===Nv?g.qa([a[0].concat([a[0][0]])]):g.qa(a):g=new c(a);return g}}this.D=b;this.P=this.C=this.a=this.L=this.j=this.o=null;this.Ob=a.clickTolerance?a.clickTolerance*a.clickTolerance:36;this.sa=new G({source:new U({useSpatialIndex:!1,wrapX:a.wrapX?a.wrapX:!1}),style:a.style?a.style:Qv()});this.Ja=a.geometryName;this.Lj=a.condition?a.condition:wg;this.Xe=a.freehand?mc:a.freehandCondition?a.freehandCondition:
+xg;B(this,Ua(mg),this.Li,this)}v(Iv,Bg);function Qv(){var a=Ci();return function(b){return a[b.V().Y()]}}k=Iv.prototype;k.setMap=function(a){Bg.prototype.setMap.call(this,a);this.Li()};function Kv(a){this.u=this.i!==Ov&&this.Xe(a);var b=!this.u;this.u&&"pointerdrag"===a.type&&null!==this.j?(Rv(this,a),b=!1):"pointermove"===a.type?b=Sv(this,a):"dblclick"===a.type&&(b=!1);return Cg.call(this,a)&&b}
+function Jv(a){return this.u?(this.fa=a.pixel,this.o||Tv(this,a),!0):this.Lj(a)?(this.fa=a.pixel,!0):!1}function Lv(a){var b=this.fa,c=a.pixel,d=b[0]-c[0],b=b[1]-c[1],d=d*d+b*b,b=!0,c=this.i===Uv;(this.u?d>this.Ob:d<=this.Ob)?(Sv(this,a),this.o?this.u||c?this.zd():Vv(this,a)?this.fc(a)&&this.zd():Rv(this,a):(Tv(this,a),this.i===Ov&&this.zd()),b=!1):c&&(this.o=null);return b}
+function Sv(a,b){if(a.o){var c=b.coordinate,d=a.j.V(),e;a.i===Ov?e=a.a:a.i===Nv?(e=a.a[0],e=e[e.length-1],Vv(a,b)&&(c=a.o.slice())):(e=a.a,e=e[e.length-1]);e[0]=c[0];e[1]=c[1];a.D(a.a,d);a.L&&a.L.V().qa(c);d instanceof E&&a.i!==Nv?(a.C||(a.C=new J(new P(null))),d=d.Qg(0),c=a.C.V(),c.da(d.ka,d.ia())):a.P&&(c=a.C.V(),c.qa(a.P));Wv(a)}else c=b.coordinate.slice(),a.L?a.L.V().qa(c):(a.L=new J(new C(c)),Wv(a));return!0}
+function Vv(a,b){var c=!1;if(a.j){var d=!1,e=[a.o];a.i===Pv?d=a.a.length>a.Fa:a.i===Nv&&(d=a.a[0].length>a.Fa,e=[a.a[0][0],a.a[0][a.a[0].length-2]]);if(d)for(var d=b.map,f=0,g=e.length;f<g;f++){var h=e[f],l=d.Ga(h),m=b.pixel,c=m[0]-l[0],l=m[1]-l[1];if(c=Math.sqrt(c*c+l*l)<=(a.u?1:a.Mj)){a.o=h;break}}}return c}
+function Tv(a,b){var c=b.coordinate;a.o=c;a.i===Ov?a.a=c.slice():a.i===Nv?(a.a=[[c.slice(),c.slice()]],a.P=a.a[0]):(a.a=[c.slice(),c.slice()],a.i===Uv&&(a.P=a.a));a.P&&(a.C=new J(new P(a.P)));c=a.D(a.a);a.j=new J;a.Ja&&a.j.Nc(a.Ja);a.j.Pa(c);Wv(a);a.b(new Xv(Yv,a.j))}
+function Rv(a,b){var c=b.coordinate,d=a.j.V(),e,f;a.i===Pv?(a.o=c.slice(),f=a.a,f.length>=a.na&&(a.u?f.pop():e=!0),f.push(c.slice()),a.D(f,d)):a.i===Nv&&(f=a.a[0],f.length>=a.na&&(a.u?f.pop():e=!0),f.push(c.slice()),e&&(a.o=f[0]),a.D(a.a,d));Wv(a);e&&a.zd()}k.Vo=function(){var a=this.j.V(),b,c;this.i===Pv?(b=this.a,b.splice(-2,1),this.D(b,a)):this.i===Nv&&(b=this.a[0],b.splice(-2,1),c=this.C.V(),c.qa(b),this.D(this.a,a));0===b.length&&(this.o=null);Wv(this)};
+k.zd=function(){var a=Zv(this),b=this.a,c=a.V();this.i===Pv?(b.pop(),this.D(b,c)):this.i===Nv&&(b[0].pop(),this.D(b,c),b=c.$());"MultiPoint"===this.U?a.Pa(new R([b])):"MultiLineString"===this.U?a.Pa(new Q([b])):"MultiPolygon"===this.U&&a.Pa(new S([b]));this.b(new Xv($v,a));this.xa&&this.xa.push(a);this.Ua&&this.Ua.gb(a)};function Zv(a){a.o=null;var b=a.j;b&&(a.j=null,a.L=null,a.C=null,a.sa.la().clear(!0));return b}
+k.vm=function(a){var b=a.V();this.j=a;this.a=b.$();a=this.a[this.a.length-1];this.o=a.slice();this.a.push(a.slice());Wv(this);this.b(new Xv(Yv,this.j))};k.Qc=nc;function Wv(a){var b=[];a.j&&b.push(a.j);a.C&&b.push(a.C);a.L&&b.push(a.L);a=a.sa.la();a.clear(!0);a.Tc(b)}k.Li=function(){var a=this.v,b=this.f();a&&b||Zv(this);this.sa.setMap(b?a:null)};
+function Mv(a){var b;"Point"===a||"MultiPoint"===a?b=Ov:"LineString"===a||"MultiLineString"===a?b=Pv:"Polygon"===a||"MultiPolygon"===a?b=Nv:"Circle"===a&&(b=Uv);return b}var Ov="Point",Pv="LineString",Nv="Polygon",Uv="Circle";function Xv(a,b){Ia.call(this,a);this.feature=b}v(Xv,Ia);var Yv="drawstart",$v="drawend";function aw(a){this.a=this.j=null;this.C=!1;this.D=this.o=null;a||(a={});a.extent&&this.i(a.extent);Bg.call(this,{handleDownEvent:bw,handleDragEvent:cw,handleEvent:dw,handleUpEvent:ew});this.u=new G({source:new U({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.boxStyle?a.boxStyle:fw(),updateWhileAnimating:!0,updateWhileInteracting:!0});this.L=new G({source:new U({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.pointerStyle?a.pointerStyle:gw(),updateWhileAnimating:!0,updateWhileInteracting:!0})}v(aw,Bg);
+function dw(a){if(!(a instanceof cf))return!0;if("pointermove"==a.type&&!this.A){var b=a.pixel,c=a.map,d=hw(this,b,c);d||(d=c.Sa(b));iw(this,d)}Cg.call(this,a);return!1}
+function bw(a){function b(a){var b=null,c=null;a[0]==e[0]?b=e[2]:a[0]==e[2]&&(b=e[0]);a[1]==e[1]?c=e[3]:a[1]==e[3]&&(c=e[1]);return null!==b&&null!==c?[b,c]:null}var c=a.pixel,d=a.map,e=this.G();(a=hw(this,c,d))&&e?(c=a[0]==e[0]||a[0]==e[2]?a[0]:null,d=a[1]==e[1]||a[1]==e[3]?a[1]:null,null!==c&&null!==d?this.a=jw(b(a)):null!==c?this.a=kw(b([c,e[1]]),b([c,e[3]])):null!==d&&(this.a=kw(b([e[0],d]),b([e[2],d])))):(a=d.Sa(c),this.i([a[0],a[1],a[0],a[1]]),this.a=jw(a));return!0}
+function cw(a){this.a&&(a=a.coordinate,this.i(this.a(a)),iw(this,a));return!0}function ew(){this.a=null;var a=this.G();a&&0!==bc(a)||this.i(null);return!1}function fw(){var a=Ci();return function(){return a.Polygon}}function gw(){var a=Ci();return function(){return a.Point}}function jw(a){return function(b){return Gb([a,b])}}function kw(a,b){return a[0]==b[0]?function(c){return Gb([a,[c[0],b[1]]])}:a[1]==b[1]?function(c){return Gb([a,[b[0],c[1]]])}:null}
+function hw(a,b,c){function d(a,b){return zb(e,a)-zb(e,b)}var e=c.Sa(b),f=a.G();if(f){f=[[[f[0],f[1]],[f[0],f[3]]],[[f[0],f[3]],[f[2],f[3]]],[[f[2],f[3]],[f[2],f[1]]],[[f[2],f[1]],[f[0],f[1]]]];f.sort(d);var f=f[0],g=sb(e,f),h=c.Ga(g);if(10>=Math.sqrt(yb(b,h)))return b=c.Ga(f[0]),c=c.Ga(f[1]),b=yb(h,b),c=yb(h,c),a.C=10>=Math.sqrt(Math.min(b,c)),a.C&&(g=b>c?f[1]:f[0]),g}return null}function iw(a,b){var c=a.D;c?c.V().qa(b):(c=new J(new C(b)),a.D=c,a.L.la().gb(c))}
+aw.prototype.setMap=function(a){this.u.setMap(a);this.L.setMap(a);Bg.prototype.setMap.call(this,a)};aw.prototype.G=function(){return this.j};aw.prototype.i=function(a){this.j=a?a:null;var b=this.o;b?a?b.Pa(Cd(a)):b.Pa(void 0):(this.o=b=a?new J(Cd(a)):new J({}),this.u.la().gb(b));this.b(new lw(this.j))};function lw(a){Ia.call(this,mw);this.b=a}v(lw,Ia);var mw="extentchanged";function nw(a){Bg.call(this,{handleDownEvent:ow,handleDragEvent:pw,handleEvent:qw,handleUpEvent:rw});this.Ua=a.condition?a.condition:Ag;this.xa=function(a){return wg(a)&&vg(a)};this.Ja=a.deleteCondition?a.deleteCondition:this.xa;this.Fa=this.a=null;this.sa=[0,0];this.C=this.L=!1;this.i=new jl;this.fa=void 0!==a.pixelTolerance?a.pixelTolerance:10;this.o=this.na=!1;this.j=[];this.D=new G({source:new U({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.style?a.style:sw(),updateWhileAnimating:!0,updateWhileInteracting:!0});
+this.U={Point:this.Cm,LineString:this.uh,LinearRing:this.uh,Polygon:this.Dm,MultiPoint:this.Am,MultiLineString:this.zm,MultiPolygon:this.Bm,GeometryCollection:this.ym};this.u=a.features;this.u.forEach(this.Gf,this);B(this.u,ue,this.wm,this);B(this.u,ve,this.xm,this);this.P=null}v(nw,Bg);k=nw.prototype;k.Gf=function(a){var b=a.V();b&&b.Y()in this.U&&this.U[b.Y()].call(this,a,b);(b=this.v)&&b.a&&tw(this,this.sa,b);B(a,"change",this.th,this)};function uw(a,b){a.C||(a.C=!0,a.b(new vw(ww,a.u,b)))}
+function xw(a,b){yw(a,b);a.a&&0===a.u.Ub()&&(a.D.la().rb(a.a),a.a=null);Ea(b,"change",a.th,a)}function yw(a,b){var c=a.i,d=[];c.forEach(function(a){b===a.feature&&d.push(a)});for(var e=d.length-1;0<=e;--e)c.remove(d[e])}k.Ea=function(a){this.a&&!a&&(this.D.la().rb(this.a),this.a=null);Bg.prototype.Ea.call(this,a)};k.setMap=function(a){this.D.setMap(a);Bg.prototype.setMap.call(this,a)};k.wm=function(a){this.Gf(a.element)};k.th=function(a){this.o||(a=a.target,xw(this,a),this.Gf(a))};
+k.xm=function(a){xw(this,a.element)};k.Cm=function(a,b){var c=b.$(),c={feature:a,geometry:b,ta:[c,c]};this.i.Da(b.G(),c)};k.Am=function(a,b){var c=b.$(),d,e,f;e=0;for(f=c.length;e<f;++e)d=c[e],d={feature:a,geometry:b,depth:[e],index:e,ta:[d,d]},this.i.Da(b.G(),d)};k.uh=function(a,b){var c=b.$(),d,e,f,g;d=0;for(e=c.length-1;d<e;++d)f=c.slice(d,d+2),g={feature:a,geometry:b,index:d,ta:f},this.i.Da(Gb(f),g)};
+k.zm=function(a,b){var c=b.$(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,geometry:b,depth:[g],index:e,ta:l},this.i.Da(Gb(l),m)};k.Dm=function(a,b){var c=b.$(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,geometry:b,depth:[g],index:e,ta:l},this.i.Da(Gb(l),m)};
+k.Bm=function(a,b){var c=b.$(),d,e,f,g,h,l,m,p,n,q;l=0;for(m=c.length;l<m;++l)for(p=c[l],g=0,h=p.length;g<h;++g)for(d=p[g],e=0,f=d.length-1;e<f;++e)n=d.slice(e,e+2),q={feature:a,geometry:b,depth:[g,l],index:e,ta:n},this.i.Da(Gb(n),q)};k.ym=function(a,b){var c,d=b.f;for(c=0;c<d.length;++c)this.U[d[c].Y()].call(this,a,d[c])};function zw(a,b){var c=a.a;c?c.V().qa(b):(c=new J(new C(b)),a.a=c,a.D.la().gb(c))}function Aw(a,b){return a.index-b.index}
+function ow(a){if(!this.Ua(a))return!1;tw(this,a.pixel,a.map);this.j.length=0;this.C=!1;var b=this.a;if(b){var c=[],b=b.V().$(),d=Gb([b]),d=ml(this.i,d),e={};d.sort(Aw);for(var f=0,g=d.length;f<g;++f){var h=d[f],l=h.ta,m=x(h.feature),p=h.depth;p&&(m+="-"+p.join("-"));e[m]||(e[m]=Array(2));if(vb(l[0],b)&&!e[m][0])this.j.push([h,0]),e[m][0]=h;else if(vb(l[1],b)&&!e[m][1]){if("LineString"!==h.geometry.Y()&&"MultiLineString"!==h.geometry.Y()||!e[m][0]||0!==e[m][0].index)this.j.push([h,1]),e[m][1]=h}else x(l)in
+this.Fa&&!e[m][0]&&!e[m][1]&&c.push([h,b])}c.length&&uw(this,a);for(a=c.length-1;0<=a;--a)this.pl.apply(this,c[a])}return!!this.a}
+function pw(a){this.L=!1;uw(this,a);a=a.coordinate;for(var b=0,c=this.j.length;b<c;++b){for(var d=this.j[b],e=d[0],f=e.depth,g=e.geometry,h=g.$(),l=e.ta,d=d[1];a.length<g.pa();)a.push(l[d][a.length]);switch(g.Y()){case "Point":h=a;l[0]=l[1]=a;break;case "MultiPoint":h[e.index]=a;l[0]=l[1]=a;break;case "LineString":h[e.index+d]=a;l[d]=a;break;case "MultiLineString":h[f[0]][e.index+d]=a;l[d]=a;break;case "Polygon":h[f[0]][e.index+d]=a;l[d]=a;break;case "MultiPolygon":h[f[1]][f[0]][e.index+d]=a,l[d]=
+a}e=g;this.o=!0;e.qa(h);this.o=!1}zw(this,a)}function rw(a){for(var b,c=this.j.length-1;0<=c;--c)b=this.j[c][0],kl(this.i,Gb(b.ta),b);this.C&&(this.b(new vw(Bw,this.u,a)),this.C=!1);return!1}function qw(a){if(!(a instanceof cf))return!0;this.P=a;var b;Md(a.map.aa())[1]||"pointermove"!=a.type||this.A||(this.sa=a.pixel,tw(this,a.pixel,a.map));this.a&&this.Ja(a)&&(b="singleclick"==a.type&&this.L?!0:this.ki());"singleclick"==a.type&&(this.L=!1);return Cg.call(this,a)&&!b}
+function tw(a,b,c){function d(a,b){return zb(e,a.ta)-zb(e,b.ta)}var e=c.Sa(b),f=Jb(Sb(e),c.aa().Oa()*a.fa),f=ml(a.i,f);if(0<f.length){f.sort(d);var g=f[0].ta,h=sb(e,g),l=c.Ga(h);if(Math.sqrt(yb(b,l))<=a.fa){b=c.Ga(g[0]);c=c.Ga(g[1]);b=yb(l,b);l=yb(l,c);a.na=Math.sqrt(Math.min(b,l))<=a.fa;a.na&&(h=b>l?g[1]:g[0]);zw(a,h);h={};h[x(g)]=!0;c=1;for(b=f.length;c<b;++c)if(l=f[c].ta,vb(g[0],l[0])&&vb(g[1],l[1])||vb(g[0],l[1])&&vb(g[1],l[0]))h[x(l)]=!0;else break;a.Fa=h;return}}a.a&&(a.D.la().rb(a.a),a.a=null)}
+k.pl=function(a,b){for(var c=a.ta,d=a.feature,e=a.geometry,f=a.depth,g=a.index,h;b.length<e.pa();)b.push(0);switch(e.Y()){case "MultiLineString":h=e.$();h[f[0]].splice(g+1,0,b);break;case "Polygon":h=e.$();h[f[0]].splice(g+1,0,b);break;case "MultiPolygon":h=e.$();h[f[1]][f[0]].splice(g+1,0,b);break;case "LineString":h=e.$();h.splice(g+1,0,b);break;default:return}this.o=!0;e.qa(h);this.o=!1;h=this.i;h.remove(a);Cw(this,e,g,f,1);var l={ta:[c[0],b],feature:d,geometry:e,depth:f,index:g};h.Da(Gb(l.ta),
+l);this.j.push([l,1]);c={ta:[b,c[1]],feature:d,geometry:e,depth:f,index:g+1};h.Da(Gb(c.ta),c);this.j.push([c,0]);this.L=!0};
+k.ki=function(){if(this.P&&"pointerdrag"!=this.P.type){var a=this.P;uw(this,a);var b=this.j,c={},d,e,f,g,h,l,m,p,n;for(h=b.length-1;0<=h;--h)g=b[h],p=g[0],n=x(p.feature),p.depth&&(n+="-"+p.depth.join("-")),n in c||(c[n]={}),0===g[1]?(c[n].right=p,c[n].index=p.index):1==g[1]&&(c[n].left=p,c[n].index=p.index+1);for(n in c){m=c[n].right;h=c[n].left;g=c[n].index;l=g-1;p=void 0!==h?h:m;0>l&&(l=0);b=p.geometry;e=f=b.$();d=!1;switch(b.Y()){case "MultiLineString":2<f[p.depth[0]].length&&(f[p.depth[0]].splice(g,
+1),d=!0);break;case "LineString":2<f.length&&(f.splice(g,1),d=!0);break;case "MultiPolygon":e=e[p.depth[1]];case "Polygon":e=e[p.depth[0]],4<e.length&&(g==e.length-1&&(g=0),e.splice(g,1),d=!0,0===g&&(e.pop(),e.push(e[0]),l=e.length-1))}d&&(d=b,this.o=!0,d.qa(f),this.o=!1,f=[],void 0!==h&&(this.i.remove(h),f.push(h.ta[0])),void 0!==m&&(this.i.remove(m),f.push(m.ta[1])),void 0!==h&&void 0!==m&&(h={depth:p.depth,feature:p.feature,geometry:p.geometry,index:l,ta:f},this.i.Da(Gb(h.ta),h)),Cw(this,b,g,p.depth,
+-1),this.a&&(this.D.la().rb(this.a),this.a=null))}this.b(new vw(Bw,this.u,a));this.C=!1;return!0}return!1};function Cw(a,b,c,d,e){pl(a.i,b.G(),function(a){a.geometry===b&&(void 0===d||void 0===a.depth||db(a.depth,d))&&a.index>c&&(a.index+=e)})}function sw(){var a=Ci();return function(){return a.Point}}function vw(a,b,c){Ia.call(this,a);this.features=b;this.mapBrowserEvent=c}v(vw,Ia);var ww="modifystart",Bw="modifyend";function Dw(a){lg.call(this,{handleEvent:Ew});a=a?a:{};this.C=a.condition?a.condition:vg;this.A=a.addCondition?a.addCondition:nc;this.D=a.removeCondition?a.removeCondition:nc;this.L=a.toggleCondition?a.toggleCondition:xg;this.o=a.multi?a.multi:!1;this.l=a.filter?a.filter:mc;this.j=a.hitTolerance?a.hitTolerance:0;this.i=new G({source:new U({useSpatialIndex:!1,features:a.features,wrapX:a.wrapX}),style:a.style?a.style:Fw(),updateWhileAnimating:!0,updateWhileInteracting:!0});if(a.layers)if("function"===
+typeof a.layers)a=a.layers;else{var b=a.layers;a=function(a){return Za(b,a)}}else a=mc;this.u=a;this.a={};a=this.i.la().c;B(a,ue,this.Em,this);B(a,ve,this.Im,this)}v(Dw,lg);k=Dw.prototype;k.Fm=function(){return this.i.la().c};k.Gm=function(){return this.j};k.Hm=function(a){a=x(a);return this.a[a]};
+function Ew(a){if(!this.C(a))return!0;var b=this.A(a),c=this.D(a),d=this.L(a),e=!b&&!c&&!d,f=a.map,g=this.i.la().c,h=[],l=[];if(e){ua(this.a);f.ae(a.pixel,function(a,b){if(this.l(a,b)){l.push(a);var c=x(a);this.a[c]=b;return!this.o}}.bind(this),{layerFilter:this.u,hitTolerance:this.j});for(e=g.Ub()-1;0<=e;--e){var f=g.item(e),m=l.indexOf(f);-1<m?l.splice(m,1):(g.remove(f),h.push(f))}0!==l.length&&g.Bf(l)}else{f.ae(a.pixel,function(a,e){if(this.l(a,e)){if(!b&&!d||Za(g.a,a))(c||d)&&Za(g.a,a)&&(h.push(a),
+f=x(a),delete this.a[f]);else{l.push(a);var f=x(a);this.a[f]=e}return!this.o}}.bind(this),{layerFilter:this.u,hitTolerance:this.j});for(e=h.length-1;0<=e;--e)g.remove(h[e]);g.Bf(l)}(0<l.length||0<h.length)&&this.b(new Gw(Hw,l,h,a));return ug(a)}k.Jm=function(a){this.j=a};k.setMap=function(a){var b=this.v,c=this.i.la().c;b&&c.forEach(b.Ji,b);lg.prototype.setMap.call(this,a);this.i.setMap(a);a&&c.forEach(a.Di,a)};
+function Fw(){var a=Ci();bb(a.Polygon,a.LineString);bb(a.GeometryCollection,a.LineString);return function(b){return b.V()?a[b.V().Y()]:null}}k.Em=function(a){var b=this.v;b&&b.Di(a.element)};k.Im=function(a){var b=this.v;b&&b.Ji(a.element)};function Gw(a,b,c,d){Ia.call(this,a);this.selected=b;this.deselected=c;this.mapBrowserEvent=d}v(Gw,Ia);var Hw="select";function Iw(a){Bg.call(this,{handleEvent:Jw,handleDownEvent:mc,handleUpEvent:Kw});a=a?a:{};this.o=a.source?a.source:null;this.sa=void 0!==a.vertex?a.vertex:!0;this.P=void 0!==a.edge?a.edge:!0;this.j=a.features?a.features:null;this.na=[];this.C={};this.D={};this.U={};this.u={};this.L=null;this.i=void 0!==a.pixelTolerance?a.pixelTolerance:10;this.Fa=Lw.bind(this);this.a=new jl;this.fa={Point:this.Pm,LineString:this.xh,LinearRing:this.xh,Polygon:this.Qm,MultiPoint:this.Nm,MultiLineString:this.Mm,MultiPolygon:this.Om,
+GeometryCollection:this.Lm}}v(Iw,Bg);k=Iw.prototype;k.gb=function(a,b){var c=void 0!==b?b:!0,d=x(a),e=a.V();if(e){var f=this.fa[e.Y()];f&&(this.U[d]=e.G(Hb()),f.call(this,a,e),c&&(this.D[d]=B(e,"change",this.Pk.bind(this,a),this)))}c&&(this.C[d]=B(a,Ua(a.a),this.Km,this))};k.Qj=function(a){this.gb(a)};k.Rj=function(a){this.rb(a)};k.vh=function(a){var b;a instanceof Bv?b=a.feature:a instanceof te&&(b=a.element);this.gb(b)};
+k.wh=function(a){var b;a instanceof Bv?b=a.feature:a instanceof te&&(b=a.element);this.rb(b)};k.Km=function(a){a=a.target;this.rb(a,!0);this.gb(a,!0)};k.Pk=function(a){if(this.A){var b=x(a);b in this.u||(this.u[b]=a)}else this.Ki(a)};k.rb=function(a,b){var c=void 0!==b?b:!0,d=x(a),e=this.U[d];if(e){var f=this.a,g=[];pl(f,e,function(b){a===b.feature&&g.push(b)});for(e=g.length-1;0<=e;--e)f.remove(g[e]);c&&(Qa(this.D[d]),delete this.D[d])}c&&(Qa(this.C[d]),delete this.C[d])};
+k.setMap=function(a){var b=this.v,c=this.na,d;this.j?d=this.j:this.o&&(d=this.o.we());b&&(c.forEach(Qa),c.length=0,d.forEach(this.Rj,this));Bg.prototype.setMap.call(this,a);a&&(this.j?c.push(B(this.j,ue,this.vh,this),B(this.j,ve,this.wh,this)):this.o&&c.push(B(this.o,Cv,this.vh,this),B(this.o,Dv,this.wh,this)),d.forEach(this.Qj,this))};k.Qc=nc;k.Ki=function(a){this.rb(a,!1);this.gb(a,!1)};k.Lm=function(a,b){var c,d=b.f;for(c=0;c<d.length;++c)this.fa[d[c].Y()].call(this,a,d[c])};
+k.xh=function(a,b){var c=b.$(),d,e,f,g;d=0;for(e=c.length-1;d<e;++d)f=c.slice(d,d+2),g={feature:a,ta:f},this.a.Da(Gb(f),g)};k.Mm=function(a,b){var c=b.$(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,ta:l},this.a.Da(Gb(l),m)};k.Nm=function(a,b){var c=b.$(),d,e,f;e=0;for(f=c.length;e<f;++e)d=c[e],d={feature:a,ta:[d,d]},this.a.Da(b.G(),d)};
+k.Om=function(a,b){var c=b.$(),d,e,f,g,h,l,m,p,n,q;l=0;for(m=c.length;l<m;++l)for(p=c[l],g=0,h=p.length;g<h;++g)for(d=p[g],e=0,f=d.length-1;e<f;++e)n=d.slice(e,e+2),q={feature:a,ta:n},this.a.Da(Gb(n),q)};k.Pm=function(a,b){var c=b.$(),c={feature:a,ta:[c,c]};this.a.Da(b.G(),c)};k.Qm=function(a,b){var c=b.$(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,ta:l},this.a.Da(Gb(l),m)};
+function Jw(a){var b,c,d=a.pixel,e=a.coordinate;b=a.map;var f=b.Sa([d[0]-this.i,d[1]+this.i]);c=b.Sa([d[0]+this.i,d[1]-this.i]);var f=Gb([f,c]),g=ml(this.a,f),h,f=!1,l=null;c=null;if(0<g.length){this.L=e;g.sort(this.Fa);g=g[0].ta;if(this.sa&&!this.P){if(e=b.Ga(g[0]),h=b.Ga(g[1]),e=yb(d,e),d=yb(d,h),h=Math.sqrt(Math.min(e,d)),h=h<=this.i)f=!0,l=e>d?g[1]:g[0],c=b.Ga(l)}else this.P&&(l=sb(e,g),c=b.Ga(l),Math.sqrt(yb(d,c))<=this.i&&(f=!0,this.sa&&(e=b.Ga(g[0]),h=b.Ga(g[1]),e=yb(c,e),d=yb(c,h),h=Math.sqrt(Math.min(e,
+d)),h=h<=this.i)))&&(l=e>d?g[1]:g[0],c=b.Ga(l));f&&(c=[Math.round(c[0]),Math.round(c[1])])}b=l;f&&(a.coordinate=b.slice(0,2),a.pixel=c);return Cg.call(this,a)}function Kw(){var a=va(this.u);a.length&&(a.forEach(this.Ki,this),this.u={});return!1}function Lw(a,b){return zb(this.L,a.ta)-zb(this.L,b.ta)};function Mw(a){Bg.call(this,{handleDownEvent:Nw,handleDragEvent:Ow,handleMoveEvent:Pw,handleUpEvent:Qw});a=a?a:{};this.o=void 0;this.a=null;this.j=void 0!==a.features?a.features:null;var b;if(a.layers)if("function"===typeof a.layers)b=a.layers;else{var c=a.layers;b=function(a){return Za(c,a)}}else b=mc;this.C=b;this.u=a.hitTolerance?a.hitTolerance:0;this.i=null}v(Mw,Bg);
+function Nw(a){this.i=Rw(this,a.pixel,a.map);if(!this.a&&this.i){this.a=a.coordinate;Pw.call(this,a);var b=this.j||new qe([this.i]);this.b(new Sw(Tw,b,a.coordinate));return!0}return!1}function Qw(a){if(this.a){this.a=null;Pw.call(this,a);var b=this.j||new qe([this.i]);this.b(new Sw(Uw,b,a.coordinate));return!0}return!1}
+function Ow(a){if(this.a){a=a.coordinate;var b=a[0]-this.a[0],c=a[1]-this.a[1],d=this.j||new qe([this.i]);d.forEach(function(a){var d=a.V();d.translate(b,c);a.Pa(d)});this.a=a;this.b(new Sw(Vw,d,a))}}function Pw(a){var b=a.map.Cc();Rw(this,a.pixel,a.map)?(this.o=b.style.cursor,b.style.cursor=this.a?"-webkit-grabbing":"-webkit-grab",b.style.cursor=this.a?"grabbing":"grab"):(b.style.cursor=void 0!==this.o?this.o:"",this.o=void 0)}
+function Rw(a,b,c){return c.ae(b,function(a){if(!this.j||Za(this.j.a,a))return a}.bind(a),{layerFilter:a.C,hitTolerance:a.u})}Mw.prototype.D=function(){return this.u};Mw.prototype.L=function(a){this.u=a};function Sw(a,b,c){Ia.call(this,a);this.features=b;this.coordinate=c}v(Sw,Ia);var Tw="translatestart",Vw="translating",Uw="translateend";function V(a){a=a?a:{};var b=ta({},a);delete b.gradient;delete b.radius;delete b.blur;delete b.shadow;delete b.weight;G.call(this,b);this.f=null;this.U=void 0!==a.shadow?a.shadow:250;this.P=void 0;this.c=null;B(this,Ua(Ww),this.Qk,this);this.ui(a.gradient?a.gradient:Xw);this.ni(void 0!==a.blur?a.blur:15);this.Ah(void 0!==a.radius?a.radius:8);B(this,Ua(Yw),this.xf,this);B(this,Ua(Zw),this.xf,this);this.xf();var c=a.weight?a.weight:"weight",d;"string"===typeof c?d=function(a){return a.get(c)}:d=c;this.l(function(a){a=
+d(a);a=void 0!==a?ia(a,0,1):1;var b=255*a|0,c=this.c[b];c||(c=[new xi({image:new xq({opacity:a,src:this.P})})],this.c[b]=c);return c}.bind(this));this.set("renderOrder",null);B(this,"render",this.gl,this)}v(V,G);var Xw=["#00f","#0ff","#0f0","#ff0","#f00"];k=V.prototype;k.Ig=function(){return this.get(Yw)};k.Pg=function(){return this.get(Ww)};k.zh=function(){return this.get(Zw)};
+k.Qk=function(){for(var a=this.Pg(),b=De(1,256),c=b.createLinearGradient(0,0,1,256),d=1/(a.length-1),e=0,f=a.length;e<f;++e)c.addColorStop(e*d,a[e]);b.fillStyle=c;b.fillRect(0,0,1,256);this.f=b.getImageData(0,0,1,256).data};k.xf=function(){var a=this.zh(),b=this.Ig(),c=a+b+1,d=2*c,d=De(d,d);d.shadowOffsetX=d.shadowOffsetY=this.U;d.shadowBlur=b;d.shadowColor="#000";d.beginPath();b=c-this.U;d.arc(b,b,a,0,2*Math.PI,!0);d.fill();this.P=d.canvas.toDataURL();this.c=Array(256);this.s()};
+k.gl=function(a){a=a.context;var b=a.canvas,b=a.getImageData(0,0,b.width,b.height),c=b.data,d,e,f;d=0;for(e=c.length;d<e;d+=4)if(f=4*c[d+3])c[d]=this.f[f],c[d+1]=this.f[f+1],c[d+2]=this.f[f+2];a.putImageData(b,0,0)};k.ni=function(a){this.set(Yw,a)};k.ui=function(a){this.set(Ww,a)};k.Ah=function(a){this.set(Zw,a)};var Yw="blur",Ww="gradient",Zw="radius";function $w(a,b,c,d){function e(){delete window[g];f.parentNode.removeChild(f)}var f=document.createElement("script"),g="olc_"+x(b);f.async=!0;f.src=a+(-1==a.indexOf("?")?"?":"&")+(d||"callback")+"="+g;var h=setTimeout(function(){e();c&&c()},1E4);window[g]=function(a){clearTimeout(h);e();b(a)};document.getElementsByTagName("head")[0].appendChild(f)};function ax(a,b,c,d,e,f,g,h,l,m,p){ag.call(this,e,0);this.C=void 0!==p?p:!1;this.A=g;this.u=h;this.H=null;this.f=b;this.l=d;this.o=f?f:e;this.g=[];this.kd=null;this.i=0;f=d.Na(this.o);h=this.l.G();e=this.f.G();f=h?ic(f,h):f;if(0===bc(f))this.state=4;else if((h=a.G())&&(e?e=ic(e,h):e=h),d=d.Ha(this.o[0]),d=am(a,c,gc(f),d),!isFinite(d)||0>=d)this.state=4;else if(this.v=new dm(a,c,f,e,d*(void 0!==m?m:.5)),0===this.v.f.length)this.state=4;else if(this.i=b.Ec(d),c=fm(this.v),e&&(a.a?(c[1]=ia(c[1],e[1],
+e[3]),c[3]=ia(c[3],e[1],e[3])):c=ic(c,e)),bc(c)){a=fe(b,c,this.i);for(b=a.ea;b<=a.ca;b++)for(c=a.ga;c<=a.ja;c++)(m=l(this.i,b,c,g))&&this.g.push(m);0===this.g.length&&(this.state=4)}else this.state=4}v(ax,ag);ax.prototype.oa=function(){1==this.state&&(this.kd.forEach(ya),this.kd=null);ag.prototype.oa.call(this)};ax.prototype.ub=function(){return this.H};
+ax.prototype.Ld=function(){var a=[];this.g.forEach(function(b){b&&b.W()==cg&&a.push({extent:this.f.Na(b.Ca),image:b.ub()})},this);this.g.length=0;if(0===a.length)this.state=3;else{var b=this.o[0],c=this.l.Za(b),d="number"===typeof c?c:c[0],c="number"===typeof c?c:c[1],b=this.l.Ha(b),e=this.f.Ha(this.i),f=this.l.Na(this.o);this.H=cm(d,c,this.A,e,this.f.G(),b,f,this.v,a,this.u,this.C);this.state=cg}this.s()};
+ax.prototype.load=function(){if(0==this.state){this.state=1;this.s();var a=0;this.kd=[];this.g.forEach(function(b){var c=b.W();if(0==c||1==c){a++;var d;d=B(b,"change",function(){var c=b.W();if(c==cg||3==c||4==c)ya(d),a--,0===a&&(this.kd.forEach(ya),this.kd=null,this.Ld())},this);this.kd.push(d)}},this);this.g.forEach(function(a){0==a.W()&&a.load()});0===a&&setTimeout(this.Ld.bind(this),0)}};function bx(a,b){var c=/\{z\}/g,d=/\{x\}/g,e=/\{y\}/g,f=/\{-y\}/g;return function(g){if(g)return a.replace(c,g[0].toString()).replace(d,g[1].toString()).replace(e,function(){return(-g[2]-1).toString()}).replace(f,function(){var a=b.a?b.a[g[0]]:null;ha(a,55);return(a.ja-a.ga+1+g[2]).toString()})}}function cx(a,b){for(var c=a.length,d=Array(c),e=0;e<c;++e)d[e]=bx(a[e],b);return dx(d)}function dx(a){return 1===a.length?a[0]:function(b,c,d){if(b)return a[oa((b[1]<<b[0])+b[2],a.length)](b,c,d)}}
+function ex(){}function fx(a){var b=[],c=/\{([a-z])-([a-z])\}/.exec(a);if(c){var d=c[2].charCodeAt(0),e;for(e=c[1].charCodeAt(0);e<=d;++e)b.push(a.replace(c[0],String.fromCharCode(e)));return b}if(c=c=/\{(\d+)-(\d+)\}/.exec(a)){d=parseInt(c[2],10);for(e=parseInt(c[1],10);e<=d;e++)b.push(a.replace(c[0],e.toString()));return b}b.push(a);return b};function gx(a){Fm.call(this);this.c=void 0!==a?a:2048}v(gx,Fm);function hx(a){return a.f>a.c}gx.prototype.Wc=function(a){for(var b,c;hx(this);){b=this.a.Rc;c=b.Ca[0].toString();var d;if(d=c in a)b=b.Ca,d=Ud(a[c],b[1],b[2]);if(d)break;else Ha(this.pop())}};function ix(a){hm.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,state:a.state,wrapX:a.wrapX});this.sa=void 0!==a.opaque?a.opaque:!1;this.xa=void 0!==a.tilePixelRatio?a.tilePixelRatio:1;this.tileGrid=void 0!==a.tileGrid?a.tileGrid:null;this.a=new gx(a.cacheSize);this.l=[0,0];this.jc=""}v(ix,hm);k=ix.prototype;k.Ih=function(){return hx(this.a)};k.Wc=function(a,b){var c=this.Cd(a);c&&c.Wc(b)};
+function Vi(a,b,c,d,e){b=a.Cd(b);if(!b)return!1;for(var f=!0,g,h,l=d.ea;l<=d.ca;++l)for(var m=d.ga;m<=d.ja;++m)g=a.Lb(c,l,m),h=!1,b.b.hasOwnProperty(g)&&(g=b.get(g),(h=g.W()===cg)&&(h=!1!==e(g))),h||(f=!1);return f}k.qf=function(){return 0};function jx(a,b){a.jc!==b&&(a.jc=b,a.s())}k.Lb=function(a,b,c){return a+"/"+b+"/"+c};k.tf=function(){return this.sa};k.Va=function(){return this.tileGrid};k.Db=function(a){return this.tileGrid?this.tileGrid:le(a)};
+k.Cd=function(a){var b=this.f;return b&&!Mc(b,a)?null:this.a};k.jb=function(){return this.xa};k.Dd=function(a,b,c){c=this.Db(c);b=this.jb(b);a=Zd(c.Za(a),this.l);return 1==b?a:Yd(a,b,this.l)};function kx(a,b,c){var d=void 0!==c?c:a.f;c=a.Db(d);if(a.D&&d.g){var e=b;b=e[0];a=ke(c,e);d=me(d);Mb(d,a)?b=e:(e=dc(d),a[0]+=e*Math.ceil((d[0]-a[0])/e),b=c.wf(a,b))}e=b[0];d=b[1];a=b[2];if(c.minZoom>e||e>c.maxZoom)c=!1;else{var f=c.G();c=(c=f?fe(c,f,e):c.a?c.a[e]:null)?Ud(c,d,a):!0}return c?b:null}
+k.wa=function(){this.a.clear();this.s()};k.ig=ea;function lx(a,b){Ia.call(this,a);this.tile=b}v(lx,Ia);function mx(a){ix.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,extent:a.extent,logo:a.logo,opaque:a.opaque,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tilePixelRatio:a.tilePixelRatio,wrapX:a.wrapX});this.tileLoadFunction=a.tileLoadFunction;this.tileUrlFunction=this.zc?this.zc.bind(this):ex;this.urls=null;a.urls?this.Ya(a.urls):a.url&&this.cb(a.url);a.tileUrlFunction&&this.Xa(a.tileUrlFunction)}v(mx,ix);k=mx.prototype;k.ib=function(){return this.tileLoadFunction};
+k.kb=function(){return this.tileUrlFunction};k.lb=function(){return this.urls};k.Jh=function(a){a=a.target;switch(a.W()){case 1:this.b(new lx("tileloadstart",a));break;case cg:this.b(new lx("tileloadend",a));break;case 3:this.b(new lx("tileloaderror",a))}};k.sb=function(a){this.a.clear();this.tileLoadFunction=a;this.s()};k.Xa=function(a,b){this.tileUrlFunction=a;"undefined"!==typeof b?jx(this,b):this.s()};
+k.cb=function(a){var b=this.urls=fx(a);this.Xa(this.zc?this.zc.bind(this):cx(b,this.tileGrid),a)};k.Ya=function(a){this.urls=a;var b=a.join("\n");this.Xa(this.zc?this.zc.bind(this):cx(a,this.tileGrid),b)};k.ig=function(a,b,c){a=this.Lb(a,b,c);this.a.b.hasOwnProperty(a)&&this.a.get(a)};function X(a){mx.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,extent:a.extent,logo:a.logo,opaque:a.opaque,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction?a.tileLoadFunction:nx,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:a.tileUrlFunction,url:a.url,urls:a.urls,wrapX:a.wrapX});this.crossOrigin=void 0!==a.crossOrigin?a.crossOrigin:null;this.tileClass=void 0!==a.tileClass?a.tileClass:lv;this.i={};this.v={};this.na=a.reprojectionErrorThreshold;
+this.C=!1}v(X,mx);k=X.prototype;k.Ih=function(){if(hx(this.a))return!0;for(var a in this.i)if(hx(this.i[a]))return!0;return!1};k.Wc=function(a,b){var c=this.Cd(a);this.a.Wc(this.a==c?b:{});for(var d in this.i){var e=this.i[d];e.Wc(e==c?b:{})}};k.qf=function(a){return this.f&&a&&!Mc(this.f,a)?0:this.rf()};k.rf=function(){return 0};k.tf=function(a){return this.f&&a&&!Mc(this.f,a)?!1:mx.prototype.tf.call(this,a)};
+k.Db=function(a){var b=this.f;return!this.tileGrid||b&&!Mc(b,a)?(b=x(a).toString(),b in this.v||(this.v[b]=le(a)),this.v[b]):this.tileGrid};k.Cd=function(a){var b=this.f;if(!b||Mc(b,a))return this.a;a=x(a).toString();a in this.i||(this.i[a]=new gx);return this.i[a]};function ox(a,b,c,d,e,f,g){b=[b,c,d];e=(c=kx(a,b,f))?a.tileUrlFunction(c,e,f):void 0;e=new a.tileClass(b,void 0!==e?0:4,void 0!==e?e:"",a.crossOrigin,a.tileLoadFunction);e.key=g;B(e,"change",a.Jh,a);return e}
+k.Dc=function(a,b,c,d,e){if(this.f&&e&&!Mc(this.f,e)){var f=this.Cd(e);c=[a,b,c];var g;a=this.Lb.apply(this,c);f.b.hasOwnProperty(a)&&(g=f.get(a));b=this.jc;if(g&&g.key==b)return g;var h=this.f,l=this.Db(h),m=this.Db(e),p=kx(this,c,e);d=new ax(h,l,e,m,c,p,this.jb(d),this.rf(),function(a,b,c,d){return px(this,a,b,c,d,h)}.bind(this),this.na,this.C);d.key=b;g?(d.a=g,f.replace(a,d)):f.set(a,d);return d}return px(this,a,b,c,d,e)};
+function px(a,b,c,d,e,f){var g,h=a.Lb(b,c,d),l=a.jc;if(a.a.b.hasOwnProperty(h)){if(g=a.a.get(h),g.key!=l){var m=g;g=ox(a,b,c,d,e,f,l);0==m.W()?g.a=m.a:g.a=m;if(g.a){b=g.a;c=g;do{if(b.W()==cg){b.a=null;break}else 1==b.W()?c=b:0==b.W()?c.a=b.a:c=b;b=c.a}while(b)}a.a.replace(h,g)}}else g=ox(a,b,c,d,e,f,l),a.a.set(h,g);return g}k.Hb=function(a){if(this.C!=a){this.C=a;for(var b in this.i)this.i[b].clear();this.s()}};k.Ib=function(a,b){var c=zc(a);c&&(c=x(c).toString(),c in this.v||(this.v[c]=b))};
+function nx(a,b){a.ub().src=b};function qx(a){this.A=void 0!==a.hidpi?a.hidpi:!1;X.call(this,{cacheSize:a.cacheSize,crossOrigin:"anonymous",opaque:!0,projection:zc("EPSG:3857"),reprojectionErrorThreshold:a.reprojectionErrorThreshold,state:"loading",tileLoadFunction:a.tileLoadFunction,tilePixelRatio:this.A?2:1,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.P=void 0!==a.culture?a.culture:"en-us";this.u=void 0!==a.maxZoom?a.maxZoom:-1;this.c=a.key;this.o=a.imagerySet;$w("https://dev.virtualearth.net/REST/v1/Imagery/Metadata/"+this.o+"?uriScheme=https&include=ImageryProviders&key="+
+this.c,this.fa.bind(this),void 0,"jsonp")}v(qx,X);var rx=new pe({html:'<a class="ol-attribution-bing-tos" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a>'});qx.prototype.U=function(){return this.c};qx.prototype.Z=function(){return this.o};
+qx.prototype.fa=function(a){if(200!=a.statusCode||"OK"!=a.statusDescription||"ValidCredentials"!=a.authenticationResultCode||1!=a.resourceSets.length||1!=a.resourceSets[0].resources.length)jm(this,"error");else{var b=a.brandLogoUri;-1==b.indexOf("https")&&(b=b.replace("http","https"));var c=a.resourceSets[0].resources[0],d=-1==this.u?c.zoomMax:this.u;a=me(this.f);var e=oe({extent:a,minZoom:c.zoomMin,maxZoom:d,tileSize:(c.imageWidth==c.imageHeight?c.imageWidth:[c.imageWidth,c.imageHeight])/this.jb()});
+this.tileGrid=e;var f=this.P,g=this.A;this.tileUrlFunction=dx(c.imageUrlSubdomains.map(function(a){var b=[0,0,0],d=c.imageUrl.replace("{subdomain}",a).replace("{culture}",f);return function(a){if(a)return $d(a[0],a[1],-a[2]-1,b),a=d,g&&(a+="&dpi=d1&device=mobile"),a.replace("{quadkey}",ae(b))}}));if(c.imageryProviders){var h=yc(zc("EPSG:4326"),this.f);a=c.imageryProviders.map(function(a){var b=a.attribution,c={};a.coverageAreas.forEach(function(a){var b=a.zoomMin,f=Math.min(a.zoomMax,d);a=a.bbox;
+a=lc([a[1],a[0],a[3],a[2]],h);var g,l;for(g=b;g<=f;++g)l=g.toString(),b=fe(e,a,g),l in c?c[l].push(b):c[l]=[b]});return new pe({html:b,tileRanges:c})});a.push(rx);this.ua(a)}this.L=b;jm(this,"ready")}};function sx(a){a=a||{};var b=void 0!==a.projection?a.projection:"EPSG:3857",c=void 0!==a.tileGrid?a.tileGrid:oe({extent:me(b),maxZoom:a.maxZoom,minZoom:a.minZoom,tileSize:a.tileSize});X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,opaque:a.opaque,projection:b,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:c,tileLoadFunction:a.tileLoadFunction,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:a.tileUrlFunction,url:a.url,urls:a.urls,
+wrapX:void 0!==a.wrapX?a.wrapX:!0})}v(sx,X);function tx(a){this.u=a.account;this.A=a.map||"";this.c=a.config||{};this.o={};sx.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,maxZoom:void 0!==a.maxZoom?a.maxZoom:18,minZoom:a.minZoom,projection:a.projection,state:"loading",wrapX:a.wrapX});ux(this)}v(tx,sx);k=tx.prototype;k.$j=function(){return this.c};k.wp=function(a){ta(this.c,a);ux(this)};k.fp=function(a){this.c=a||{};ux(this)};
+function ux(a){var b=JSON.stringify(a.c);if(a.o[b])vx(a,a.o[b]);else{var c="https://"+a.u+".cartodb.com/api/v1/map";a.A&&(c+="/named/"+a.A);var d=new XMLHttpRequest;d.addEventListener("load",a.Sk.bind(a,b));d.addEventListener("error",a.Rk.bind(a));d.open("POST",c);d.setRequestHeader("Content-type","application/json");d.send(JSON.stringify(a.c))}}
+k.Sk=function(a,b){var c=b.target;if(!c.status||200<=c.status&&300>c.status){var d;try{d=JSON.parse(c.responseText)}catch(e){jm(this,"error");return}vx(this,d);this.o[a]=d;jm(this,"ready")}else jm(this,"error")};k.Rk=function(){jm(this,"error")};function vx(a,b){a.cb("https://"+b.cdn_url.https+"/"+a.u+"/api/v1/map/"+b.layergroupid+"/{z}/{x}/{y}.png")};function Y(a){U.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,wrapX:a.wrapX});this.C=void 0;this.fa=void 0!==a.distance?a.distance:20;this.A=[];this.na=a.geometryFunction||function(a){a=a.V();ha(a instanceof C,10);return a};this.u=a.source;this.u.J("change",Y.prototype.Ja,this)}v(Y,U);Y.prototype.Ua=function(){return this.u};Y.prototype.Ed=function(a,b,c){this.u.Ed(a,b,c);b!==this.C&&(this.clear(),this.C=b,wx(this),this.Tc(this.A))};
+Y.prototype.Ob=function(a){this.fa=a;this.Ja()};Y.prototype.Ja=function(){this.clear();wx(this);this.Tc(this.A);this.s()};function wx(a){if(void 0!==a.C){a.A.length=0;for(var b=Hb(),c=a.fa*a.C,d=a.u.we(),e={},f=0,g=d.length;f<g;f++){var h=d[f];x(h).toString()in e||!(h=a.na(h))||(h=h.$(),Sb(h,b),Jb(b,c,b),h=a.u.nf(b),h=h.filter(function(a){a=x(a).toString();return a in e?!1:e[a]=!0}),a.A.push(xx(a,h)))}}}
+function xx(a,b){for(var c=[0,0],d=b.length-1;0<=d;--d){var e=a.na(b[d]);e?rb(c,e.$()):b.splice(d,1)}xb(c,1/b.length);c=new J(new C(c));c.set("features",b);return c};function yx(a,b){var c=[];Object.keys(b).forEach(function(a){null!==b[a]&&void 0!==b[a]&&c.push(a+"="+encodeURIComponent(b[a]))});var d=c.join("&");a=a.replace(/[?&]$/,"");a=-1===a.indexOf("?")?a+"?":a+"&";return a+d};function zx(a){a=a||{};km.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions});this.Z=void 0!==a.crossOrigin?a.crossOrigin:null;this.i=a.url;this.l=void 0!==a.imageLoadFunction?a.imageLoadFunction:qm;this.u=a.params||{};this.c=null;this.v=[0,0];this.P=0;this.C=void 0!==a.ratio?a.ratio:1.5}v(zx,km);k=zx.prototype;k.Xm=function(){return this.u};
+k.Xc=function(a,b,c,d){if(void 0===this.i)return null;b=lm(this,b);var e=this.c;if(e&&this.P==this.g&&e.resolution==b&&e.f==c&&Ob(e.G(),a))return e;e={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};ta(e,this.u);a=a.slice();var f=(a[0]+a[2])/2,g=(a[1]+a[3])/2;if(1!=this.C){var h=this.C*dc(a)/2,l=this.C*ec(a)/2;a[0]=f-h;a[1]=g-l;a[2]=f+h;a[3]=g+l}var h=b/c,l=Math.ceil(dc(a)/h),m=Math.ceil(ec(a)/h);a[0]=f-h*l/2;a[2]=f+h*l/2;a[1]=g-h*m/2;a[3]=g+h*m/2;this.v[0]=l;this.v[1]=m;f=a;g=this.v;d=d.hb.split(":").pop();
+e.SIZE=g[0]+","+g[1];e.BBOX=f.join(",");e.BBOXSR=d;e.IMAGESR=d;e.DPI=90*c;d=this.i;f=d.replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage");f==d&&ha(!1,50);e=yx(f,e);this.c=new ii(a,b,c,this.j,e,this.Z,this.l);this.P=this.g;B(this.c,"change",this.o,this);return this.c};k.Wm=function(){return this.l};k.Ym=function(){return this.i};k.Zm=function(a){this.c=null;this.l=a;this.s()};k.$m=function(a){a!=this.i&&(this.i=a,this.c=null,this.s())};
+k.an=function(a){ta(this.u,a);this.c=null;this.s()};function Ax(a){km.call(this,{projection:a.projection,resolutions:a.resolutions});this.Z=void 0!==a.crossOrigin?a.crossOrigin:null;this.v=void 0!==a.displayDpi?a.displayDpi:96;this.l=a.params||{};this.P=a.url;this.c=void 0!==a.imageLoadFunction?a.imageLoadFunction:qm;this.fa=void 0!==a.hidpi?a.hidpi:!0;this.na=void 0!==a.metersPerUnit?a.metersPerUnit:1;this.u=void 0!==a.ratio?a.ratio:1;this.xa=void 0!==a.useOverlay?a.useOverlay:!1;this.i=null;this.C=0}v(Ax,km);k=Ax.prototype;k.cn=function(){return this.l};
+k.Xc=function(a,b,c){b=lm(this,b);c=this.fa?c:1;var d=this.i;if(d&&this.C==this.g&&d.resolution==b&&d.f==c&&Ob(d.G(),a))return d;1!=this.u&&(a=a.slice(),kc(a,this.u));var e=[dc(a)/b*c,ec(a)/b*c];if(void 0!==this.P){var d=this.P,f=gc(a),g=this.na,h=dc(a),l=ec(a),m=e[0],p=e[1],n=.0254/this.v,e={OPERATION:this.xa?"GETDYNAMICMAPOVERLAYIMAGE":"GETMAPIMAGE",VERSION:"2.0.0",LOCALE:"en",CLIENTAGENT:"ol.source.ImageMapGuide source",CLIP:"1",SETDISPLAYDPI:this.v,SETDISPLAYWIDTH:Math.round(e[0]),SETDISPLAYHEIGHT:Math.round(e[1]),
+SETVIEWSCALE:p*h>m*l?h*g/(m*n):l*g/(p*n),SETVIEWCENTERX:f[0],SETVIEWCENTERY:f[1]};ta(e,this.l);d=yx(d,e);d=new ii(a,b,c,this.j,d,this.Z,this.c);B(d,"change",this.o,this)}else d=null;this.i=d;this.C=this.g;return d};k.bn=function(){return this.c};k.en=function(a){ta(this.l,a);this.s()};k.dn=function(a){this.i=null;this.c=a;this.s()};function Bx(a){var b=a.imageExtent,c=void 0!==a.crossOrigin?a.crossOrigin:null,d=void 0!==a.imageLoadFunction?a.imageLoadFunction:qm;km.call(this,{attributions:a.attributions,logo:a.logo,projection:zc(a.projection)});this.c=new ii(b,void 0,1,this.j,a.url,c,d);this.i=a.imageSize?a.imageSize:null;B(this.c,"change",this.o,this)}v(Bx,km);Bx.prototype.Xc=function(a){return jc(a,this.c.G())?this.c:null};
+Bx.prototype.o=function(a){if(this.c.W()==li){var b=this.c.G(),c=this.c.a(),d,e;this.i?(d=this.i[0],e=this.i[1]):(d=c.width,e=c.height);b=Math.ceil(dc(b)/(ec(b)/e));if(b!=d){var b=De(b,e),f=b.canvas;b.drawImage(c,0,0,d,e,0,0,f.width,f.height);this.c.g=f}}km.prototype.o.call(this,a)};function Cx(a){a=a||{};km.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions});this.na=void 0!==a.crossOrigin?a.crossOrigin:null;this.l=a.url;this.C=void 0!==a.imageLoadFunction?a.imageLoadFunction:qm;this.i=a.params||{};this.u=!0;Dx(this);this.fa=a.serverType;this.xa=void 0!==a.hidpi?a.hidpi:!0;this.c=null;this.P=[0,0];this.Z=0;this.v=void 0!==a.ratio?a.ratio:1.5}v(Cx,km);var Ex=[101,101];k=Cx.prototype;
+k.ln=function(a,b,c,d){if(void 0!==this.l){var e=hc(a,b,0,Ex),f={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.i.LAYERS};ta(f,this.i,d);d=Math.floor((e[3]-a[1])/b);f[this.u?"I":"X"]=Math.floor((a[0]-e[0])/b);f[this.u?"J":"Y"]=d;return Fx(this,e,Ex,1,zc(c),f)}};k.nn=function(){return this.i};
+k.Xc=function(a,b,c,d){if(void 0===this.l)return null;b=lm(this,b);1==c||this.xa&&void 0!==this.fa||(c=1);a=a.slice();var e=(a[0]+a[2])/2,f=(a[1]+a[3])/2,g=b/c,h=dc(a)/g,g=ec(a)/g,l=this.c;if(l&&this.Z==this.g&&l.resolution==b&&l.f==c&&Ob(l.G(),a))return l;if(1!=this.v){var l=this.v*dc(a)/2,m=this.v*ec(a)/2;a[0]=e-l;a[1]=f-m;a[2]=e+l;a[3]=f+m}e={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};ta(e,this.i);this.P[0]=Math.ceil(h*this.v);this.P[1]=Math.ceil(g*this.v);
+d=Fx(this,a,this.P,c,d,e);this.c=new ii(a,b,c,this.j,d,this.na,this.C);this.Z=this.g;B(this.c,"change",this.o,this);return this.c};k.mn=function(){return this.C};
+function Fx(a,b,c,d,e,f){ha(void 0!==a.l,9);f[a.u?"CRS":"SRS"]=e.hb;"STYLES"in a.i||(f.STYLES="");if(1!=d)switch(a.fa){case "geoserver":d=90*d+.5|0;f.FORMAT_OPTIONS="FORMAT_OPTIONS"in f?f.FORMAT_OPTIONS+(";dpi:"+d):"dpi:"+d;break;case "mapserver":f.MAP_RESOLUTION=90*d;break;case "carmentaserver":case "qgis":f.DPI=90*d;break;default:ha(!1,8)}f.WIDTH=c[0];f.HEIGHT=c[1];c=e.b;var g;a.u&&"ne"==c.substr(0,2)?g=[b[1],b[0],b[3],b[2]]:g=b;f.BBOX=g.join(",");return yx(a.l,f)}k.pn=function(){return this.l};
+k.qn=function(a){this.c=null;this.C=a;this.s()};k.rn=function(a){a!=this.l&&(this.l=a,this.c=null,this.s())};k.sn=function(a){ta(this.i,a);Dx(this);this.c=null;this.s()};function Dx(a){a.u=0<=qb(a.i.VERSION||"1.3.0")};function Gx(a){a=a||{};var b;void 0!==a.attributions?b=a.attributions:b=[Hx];sx.call(this,{attributions:b,cacheSize:a.cacheSize,crossOrigin:void 0!==a.crossOrigin?a.crossOrigin:"anonymous",opaque:void 0!==a.opaque?a.opaque:!0,maxZoom:void 0!==a.maxZoom?a.maxZoom:19,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileLoadFunction:a.tileLoadFunction,url:void 0!==a.url?a.url:"https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",wrapX:a.wrapX})}v(Gx,sx);var Hx=new pe({html:'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors.'});(function(){var a={},b={ma:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ma=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Sp=c()}})(function(){return function d(a,b,g){function h(m,n){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!n&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ma:{}};a[m][0].call(q.ma,function(b){var d=
+a[m][1][b];return h(d?d:b)},q,q.ma,d,a,b,g)}return b[m].ma}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b,f){a=a("./processor");f.ij=a},{"./processor":2}],2:[function(a,b){function f(a){var b=!0;try{new ImageData(10,10)}catch(d){b=!1}return function(d){var e=d.buffers,f=d.meta,g=d.width,h=d.height,l=e.length,m=e[0].byteLength;if(d.imageOps){m=Array(l);for(d=0;d<l;++d){var O=m,Ja=d,ca;ca=new Uint8ClampedArray(e[d]);var Ma=g,D=h;ca=b?new ImageData(ca,
+Ma,D):{data:ca,width:Ma,height:D};O[Ja]=ca}g=a(m,f).data}else{g=new Uint8ClampedArray(m);h=Array(l);O=Array(l);for(d=0;d<l;++d)h[d]=new Uint8ClampedArray(e[d]),O[d]=[0,0,0,0];for(e=0;e<m;e+=4){for(d=0;d<l;++d)Ja=h[d],O[d][0]=Ja[e],O[d][1]=Ja[e+1],O[d][2]=Ja[e+2],O[d][3]=Ja[e+3];d=a(O,f);g[e]=d[0];g[e+1]=d[1];g[e+2]=d[2];g[e+3]=d[3]}}return g.buffer}}function g(a,b){var d=Object.keys(a.lib||{}).map(function(b){return"var "+b+" = "+a.lib[b].toString()+";"}).concat(["var __minion__ = ("+f.toString()+
+")(",a.operation.toString(),");",'self.addEventListener("message", function(event) {'," var buffer = __minion__(event.data);"," self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);","});"]),d=URL.createObjectURL(new Blob(d,{type:"text/javascript"})),d=new Worker(d);d.addEventListener("message",b);return d}function h(a,b){var d=f(a.operation);return{postMessage:function(a){setTimeout(function(){b({data:{buffer:d(a),meta:a.meta}})},0)}}}function l(a){this.Ze=!!a.nl;var b;0===a.threads?
+b=0:this.Ze?b=1:b=a.threads||1;var d=[];if(b)for(var e=0;e<b;++e)d[e]=g(a,this.sg.bind(this,e));else d[0]=h(a,this.sg.bind(this,0));this.Wd=d;this.qd=[];this.wj=a.yo||Infinity;this.Ud=0;this.Sc={};this.$e=null}var m=a("./util").Hl;l.prototype.wo=function(a,b,d){this.uj({Fc:a,hh:b,Vc:d});this.pg()};l.prototype.uj=function(a){for(this.qd.push(a);this.qd.length>this.wj;)this.qd.shift().Vc(null,null)};l.prototype.pg=function(){if(0===this.Ud&&0<this.qd.length){var a=this.$e=this.qd.shift(),b=a.Fc[0].width,
+d=a.Fc[0].height,e=a.Fc.map(function(a){return a.data.buffer}),f=this.Wd.length;this.Ud=f;if(1===f)this.Wd[0].postMessage({buffers:e,meta:a.hh,imageOps:this.Ze,width:b,height:d},e);else for(var g=4*Math.ceil(a.Fc[0].data.length/4/f),h=0;h<f;++h){for(var l=h*g,m=[],O=0,Ja=e.length;O<Ja;++O)m.push(e[h].slice(l,l+g));this.Wd[h].postMessage({buffers:m,meta:a.hh,imageOps:this.Ze,width:b,height:d},m)}}};l.prototype.sg=function(a,b){this.Qp||(this.Sc[a]=b.data,--this.Ud,0===this.Ud&&this.xj())};l.prototype.xj=
+function(){var a=this.$e,b=this.Wd.length,d,e;if(1===b)d=new Uint8ClampedArray(this.Sc[0].buffer),e=this.Sc[0].meta;else{var f=a.Fc[0].data.length;d=new Uint8ClampedArray(f);e=Array(f);for(var f=4*Math.ceil(f/4/b),g=0;g<b;++g){var h=g*f;d.set(new Uint8ClampedArray(this.Sc[g].buffer),h);e[g]=this.Sc[g].meta}}this.$e=null;this.Sc={};a.Vc(null,m(d,a.Fc[0].width,a.Fc[0].height),e);this.pg()};b.ma=l},{"./util":3}],3:[function(a,b,f){var g=!0;try{new ImageData(10,10)}catch(l){g=!1}var h=document.createElement("canvas").getContext("2d");
+f.Hl=function(a,b,d){if(g)return new ImageData(a,b,d);b=h.createImageData(b,d);b.data.set(a);return b}},{}]},{},[1])(1)});il=b.ma})();function Ix(a){this.C=null;this.xa=void 0!==a.operationType?a.operationType:Jx;this.Ja=void 0!==a.threads?a.threads:1;this.c=Kx(a.sources);for(var b=0,c=this.c.length;b<c;++b)B(this.c[b],"change",this.s,this);this.i=De();this.fa=new ig(function(){return 1},this.s.bind(this));for(var b=Lx(this.c),c={},d=0,e=b.length;d<e;++d)c[x(b[d].layer)]=b[d];this.l=this.v=null;this.Z={animate:!1,attributions:{},coordinateToPixelTransform:Ph(),extent:null,focus:null,index:0,layerStates:c,layerStatesArray:b,logos:{},
+pixelRatio:1,pixelToCoordinateTransform:Ph(),postRenderFunctions:[],size:[0,0],skippedFeatureUids:{},tileQueue:this.fa,time:Date.now(),usedTiles:{},viewState:{rotation:0},viewHints:[],wantedTiles:{}};km.call(this,{});void 0!==a.operation&&this.u(a.operation,a.lib)}v(Ix,km);Ix.prototype.u=function(a,b){this.C=new il.ij({operation:a,nl:this.xa===Mx,yo:1,lib:b,threads:this.Ja});this.s()};function Nx(a,b,c){var d=a.v;return!d||a.g!==d.bp||c!==d.resolution||!Vb(b,d.extent)}
+Ix.prototype.U=function(a,b,c,d){c=!0;for(var e,f=0,g=this.c.length;f<g;++f)if(e=this.c[f].a.la(),"ready"!==e.W()){c=!1;break}if(!c)return null;a=a.slice();if(!Nx(this,a,b))return this.l;c=this.i.canvas;e=Math.round(dc(a)/b);f=Math.round(ec(a)/b);if(e!==c.width||f!==c.height)c.width=e,c.height=f;e=ta({},this.Z);e.viewState=ta({},e.viewState);var f=gc(a),g=Math.round(dc(a)/b),h=Math.round(ec(a)/b);e.extent=a;e.focus=gc(a);e.size[0]=g;e.size[1]=h;g=e.viewState;g.center=f;g.projection=d;g.resolution=
+b;this.l=d=new Xl(a,b,1,this.j,c,this.P.bind(this,e));this.v={extent:a,resolution:b,bp:this.g};return d};
+Ix.prototype.P=function(a,b){for(var c=this.c.length,d=Array(c),e=0;e<c;++e){var f;f=this.c[e];var g=a,h=a.layerStatesArray[e];if(f.o(g,h)){var l=g.size[0],m=g.size[1];if(Ox){var p=Ox.canvas;p.width!==l||p.height!==m?Ox=De(l,m):Ox.clearRect(0,0,l,m)}else Ox=De(l,m);f.D(g,h,Ox);f=Ox.getImageData(0,0,l,m)}else f=null;if(f)d[e]=f;else{d=null;break}}d&&(c={},this.b(new Px(Qx,a,c)),this.C.wo(d,c,this.na.bind(this,a,b)));jg(a.tileQueue,16,16)};
+Ix.prototype.na=function(a,b,c,d,e){c?b(c):d&&(this.b(new Px(Rx,a,e)),Nx(this,a.extent,a.viewState.resolution/a.pixelRatio)||this.i.putImageData(d,0,0),b(null))};var Ox=null;function Lx(a){return a.map(function(a){return wh(a.a)})}function Kx(a){for(var b=a.length,c=Array(b),d=0;d<b;++d){var e=d,f=a[d],g=null;f instanceof ix?(f=new F({source:f}),g=new ij(f)):f instanceof km&&(f=new ei({source:f}),g=new hj(f));c[e]=g}return c}
+function Px(a,b,c){Ia.call(this,a);this.extent=b.extent;this.resolution=b.viewState.resolution/b.pixelRatio;this.data=c}v(Px,Ia);var Qx="beforeoperations",Rx="afteroperations",Jx="pixel",Mx="image";function Sx(a){var b=a.layer.indexOf("-"),b=Tx[-1==b?a.layer:a.layer.slice(0,b)],c=Ux[a.layer];sx.call(this,{attributions:Vx,cacheSize:a.cacheSize,crossOrigin:"anonymous",maxZoom:void 0!=a.maxZoom?a.maxZoom:b.maxZoom,minZoom:void 0!=a.minZoom?a.minZoom:b.minZoom,opaque:c.opaque,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileLoadFunction:a.tileLoadFunction,url:void 0!==a.url?a.url:"https://stamen-tiles-{a-d}.a.ssl.fastly.net/"+a.layer+"/{z}/{x}/{y}."+c.Bb})}v(Sx,sx);
+var Vx=[new pe({html:'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.'}),Hx],Ux={terrain:{Bb:"jpg",opaque:!0},"terrain-background":{Bb:"jpg",opaque:!0},"terrain-labels":{Bb:"png",opaque:!1},"terrain-lines":{Bb:"png",opaque:!1},"toner-background":{Bb:"png",opaque:!0},toner:{Bb:"png",opaque:!0},"toner-hybrid":{Bb:"png",opaque:!1},"toner-labels":{Bb:"png",opaque:!1},"toner-lines":{Bb:"png",opaque:!1},"toner-lite":{Bb:"png",
+opaque:!0},watercolor:{Bb:"jpg",opaque:!0}},Tx={terrain:{minZoom:4,maxZoom:18},toner:{minZoom:0,maxZoom:20},watercolor:{minZoom:1,maxZoom:16}};function Wx(a){a=a||{};X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction,url:a.url,urls:a.urls,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.c=a.params||{};this.o=Hb();jx(this,Xx(this))}v(Wx,X);function Xx(a){var b=0,c=[],d;for(d in a.c)c[b++]=d+"-"+a.c[d];return c.join("/")}Wx.prototype.u=function(){return this.c};
+Wx.prototype.jb=function(a){return a};
+Wx.prototype.zc=function(a,b,c){var d=this.tileGrid;d||(d=this.Db(c));if(!(d.b.length<=a[0])){var e=d.Na(a,this.o),f=Zd(d.Za(a[0]),this.l);1!=b&&(f=Yd(f,b,this.l));d={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};ta(d,this.c);var g=this.urls;g?(c=c.hb.split(":").pop(),d.SIZE=f[0]+","+f[1],d.BBOX=e.join(","),d.BBOXSR=c,d.IMAGESR=c,d.DPI=Math.round(d.DPI?d.DPI*b:90*b),a=(1==g.length?g[0]:g[oa((a[1]<<a[0])+a[2],g.length)]).replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage"),
+a=yx(a,d)):a=void 0;return a}};Wx.prototype.A=function(a){ta(this.c,a);jx(this,Xx(this))};function Yx(a){ix.call(this,{opaque:!1,projection:a.projection,tileGrid:a.tileGrid,wrapX:void 0!==a.wrapX?a.wrapX:!0})}v(Yx,ix);Yx.prototype.Dc=function(a,b,c){var d=this.Lb(a,b,c);if(this.a.b.hasOwnProperty(d))return this.a.get(d);var e=Zd(this.tileGrid.Za(a));a=[a,b,c];b=(b=kx(this,a))?kx(this,b).toString():"";e=new Zx(a,e,b);this.a.set(d,e);return e};function Zx(a,b,c){ag.call(this,a,cg);this.i=b;this.f=c;this.g=null}v(Zx,ag);
+Zx.prototype.ub=function(){if(this.g)return this.g;var a=this.i,b=De(a[0],a[1]);b.strokeStyle="black";b.strokeRect(.5,.5,a[0]+.5,a[1]+.5);b.fillStyle="black";b.textAlign="center";b.textBaseline="middle";b.font="24px sans-serif";b.fillText(this.f,a[0]/2,a[1]/2);return this.g=b.canvas};function $x(a){this.c=null;X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,projection:zc("EPSG:3857"),reprojectionErrorThreshold:a.reprojectionErrorThreshold,state:"loading",tileLoadFunction:a.tileLoadFunction,wrapX:void 0!==a.wrapX?a.wrapX:!0});if(a.jsonp)$w(a.url,this.Gh.bind(this),this.ue.bind(this));else{var b=new XMLHttpRequest;b.addEventListener("load",this.vn.bind(this));b.addEventListener("error",this.tn.bind(this));b.open("GET",a.url);b.send()}}
+v($x,X);k=$x.prototype;k.vn=function(a){a=a.target;if(!a.status||200<=a.status&&300>a.status){var b;try{b=JSON.parse(a.responseText)}catch(c){this.ue();return}this.Gh(b)}else this.ue()};k.tn=function(){this.ue()};k.Fk=function(){return this.c};
+k.Gh=function(a){var b=zc("EPSG:4326"),c=this.f,d;if(void 0!==a.bounds){var e=yc(b,c);d=lc(a.bounds,e)}var f=a.minzoom||0,e=a.maxzoom||22;this.tileGrid=c=oe({extent:me(c),maxZoom:e,minZoom:f});this.tileUrlFunction=cx(a.tiles,c);if(void 0!==a.attribution&&!this.j){b=void 0!==d?d:b.G();d={};for(var g;f<=e;++f)g=f.toString(),d[g]=[fe(c,b,f)];this.ua([new pe({html:a.attribution,tileRanges:d})])}this.c=a;jm(this,"ready")};k.ue=function(){jm(this,"error")};function ay(a){ix.call(this,{projection:zc("EPSG:3857"),state:"loading"});this.v=void 0!==a.preemptive?a.preemptive:!0;this.o=ex;this.i=void 0;this.c=a.jsonp||!1;if(a.url)if(this.c)$w(a.url,this.Kf.bind(this),this.ve.bind(this));else{var b=new XMLHttpRequest;b.addEventListener("load",this.zn.bind(this));b.addEventListener("error",this.yn.bind(this));b.open("GET",a.url);b.send()}else a.tileJSON?this.Kf(a.tileJSON):ha(!1,51)}v(ay,ix);k=ay.prototype;
+k.zn=function(a){a=a.target;if(!a.status||200<=a.status&&300>a.status){var b;try{b=JSON.parse(a.responseText)}catch(c){this.ve();return}this.Kf(b)}else this.ve()};k.yn=function(){this.ve()};k.Ck=function(){return this.i};k.Pj=function(a,b,c,d,e){this.tileGrid?(b=this.tileGrid.fe(a,b),by(this.Dc(b[0],b[1],b[2],1,this.f),a,c,d,e)):!0===e?setTimeout(function(){c.call(d,null)},0):c.call(d,null)};k.ve=function(){jm(this,"error")};
+k.Kf=function(a){var b=zc("EPSG:4326"),c=this.f,d;if(void 0!==a.bounds){var e=yc(b,c);d=lc(a.bounds,e)}var f=a.minzoom||0,e=a.maxzoom||22;this.tileGrid=c=oe({extent:me(c),maxZoom:e,minZoom:f});this.i=a.template;var g=a.grids;if(g){this.o=cx(g,c);if(void 0!==a.attribution){b=void 0!==d?d:b.G();for(d={};f<=e;++f)g=f.toString(),d[g]=[fe(c,b,f)];this.ua([new pe({html:a.attribution,tileRanges:d})])}jm(this,"ready")}else jm(this,"error")};
+k.Dc=function(a,b,c,d,e){var f=this.Lb(a,b,c);if(this.a.b.hasOwnProperty(f))return this.a.get(f);a=[a,b,c];b=kx(this,a,e);d=this.o(b,d,e);d=new cy(a,void 0!==d?0:4,void 0!==d?d:"",this.tileGrid.Na(a),this.v,this.c);this.a.set(f,d);return d};k.ig=function(a,b,c){a=this.Lb(a,b,c);this.a.b.hasOwnProperty(a)&&this.a.get(a)};function cy(a,b,c,d,e,f){ag.call(this,a,b);this.o=c;this.g=d;this.H=e;this.f=this.l=this.i=null;this.v=f}v(cy,ag);k=cy.prototype;k.ub=function(){return null};
+k.getData=function(a){if(!this.i||!this.l)return null;var b=this.i[Math.floor((1-(a[1]-this.g[1])/(this.g[3]-this.g[1]))*this.i.length)];if("string"!==typeof b)return null;b=b.charCodeAt(Math.floor((a[0]-this.g[0])/(this.g[2]-this.g[0])*b.length));93<=b&&b--;35<=b&&b--;b-=32;a=null;b in this.l&&(b=this.l[b],this.f&&b in this.f?a=this.f[b]:a=b);return a};
+function by(a,b,c,d,e){0==a.state&&!0===e?(Da(a,"change",function(){c.call(d,this.getData(b))},a),dy(a)):!0===e?setTimeout(function(){c.call(d,this.getData(b))}.bind(a),0):c.call(d,a.getData(b))}k.bb=function(){return this.o};k.ge=function(){this.state=3;this.s()};k.Hh=function(a){this.i=a.grid;this.l=a.keys;this.f=a.data;this.state=4;this.s()};
+function dy(a){if(0==a.state)if(a.state=1,a.v)$w(a.o,a.Hh.bind(a),a.ge.bind(a));else{var b=new XMLHttpRequest;b.addEventListener("load",a.xn.bind(a));b.addEventListener("error",a.wn.bind(a));b.open("GET",a.o);b.send()}}k.xn=function(a){a=a.target;if(!a.status||200<=a.status&&300>a.status){var b;try{b=JSON.parse(a.responseText)}catch(c){this.ge();return}this.Hh(b)}else this.ge()};k.wn=function(){this.ge()};k.load=function(){this.H&&dy(this)};function ey(a){a=a||{};var b=a.params||{};X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,opaque:!("TRANSPARENT"in b?b.TRANSPARENT:1),projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction,url:a.url,urls:a.urls,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.u=void 0!==a.gutter?a.gutter:0;this.c=b;this.o=!0;this.A=a.serverType;this.U=void 0!==a.hidpi?a.hidpi:!0;this.P="";
+fy(this);this.Z=Hb();gy(this);jx(this,hy(this))}v(ey,X);k=ey.prototype;
+k.An=function(a,b,c,d){c=zc(c);var e=this.tileGrid;e||(e=this.Db(c));b=e.fe(a,b);if(!(e.b.length<=b[0])){var f=e.Ha(b[0]),g=e.Na(b,this.Z),e=Zd(e.Za(b[0]),this.l),h=this.u;0!==h&&(e=Xd(e,h,this.l),g=Jb(g,f*h,g));h={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.c.LAYERS};ta(h,this.c,d);d=Math.floor((g[3]-a[1])/f);h[this.o?"I":"X"]=Math.floor((a[0]-g[0])/f);h[this.o?"J":"Y"]=d;return iy(this,b,e,g,1,c,h)}};k.rf=function(){return this.u};
+k.Lb=function(a,b,c){return this.P+X.prototype.Lb.call(this,a,b,c)};k.Bn=function(){return this.c};
+function iy(a,b,c,d,e,f,g){var h=a.urls;if(h){g.WIDTH=c[0];g.HEIGHT=c[1];g[a.o?"CRS":"SRS"]=f.hb;"STYLES"in a.c||(g.STYLES="");if(1!=e)switch(a.A){case "geoserver":c=90*e+.5|0;g.FORMAT_OPTIONS="FORMAT_OPTIONS"in g?g.FORMAT_OPTIONS+(";dpi:"+c):"dpi:"+c;break;case "mapserver":g.MAP_RESOLUTION=90*e;break;case "carmentaserver":case "qgis":g.DPI=90*e;break;default:ha(!1,52)}f=f.b;a.o&&"ne"==f.substr(0,2)&&(a=d[0],d[0]=d[1],d[1]=a,a=d[2],d[2]=d[3],d[3]=a);g.BBOX=d.join(",");return yx(1==h.length?h[0]:h[oa((b[1]<<
+b[0])+b[2],h.length)],g)}}k.jb=function(a){return this.U&&void 0!==this.A?a:1};function fy(a){var b=0,c=[];if(a.urls){var d,e;d=0;for(e=a.urls.length;d<e;++d)c[b++]=a.urls[d]}a.P=c.join("#")}function hy(a){var b=0,c=[],d;for(d in a.c)c[b++]=d+"-"+a.c[d];return c.join("/")}
+k.zc=function(a,b,c){var d=this.tileGrid;d||(d=this.Db(c));if(!(d.b.length<=a[0])){1==b||this.U&&void 0!==this.A||(b=1);var e=d.Ha(a[0]),f=d.Na(a,this.Z),d=Zd(d.Za(a[0]),this.l),g=this.u;0!==g&&(d=Xd(d,g,this.l),f=Jb(f,e*g,f));1!=b&&(d=Yd(d,b,this.l));e={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};ta(e,this.c);return iy(this,a,d,f,b,c,e)}};k.Ya=function(a){X.prototype.Ya.call(this,a);fy(this)};k.Cn=function(a){ta(this.c,a);fy(this);gy(this);jx(this,hy(this))};
+function gy(a){a.o=0<=qb(a.c.VERSION||"1.3.0")};function jy(a,b,c,d,e){ag.call(this,a,b);this.f=De();this.l=d;this.i=null;this.g={xd:!1,bg:null,li:-1,cg:-1,jd:null};this.H=e;this.o=c}v(jy,ag);k=jy.prototype;k.ub=function(){return-1==this.g.cg?null:this.f.canvas};k.Yl=function(){return this.l};k.bb=function(){return this.o};k.load=function(){0==this.state&&(this.state=1,this.s(),this.H(this,this.o),this.v(null,NaN,null))};k.ho=function(a,b){this.Ff(b);this.ri(a)};k.fo=function(){this.state=3;this.s()};k.ri=function(a){this.i=a;this.state=cg;this.s()};
+k.Ff=function(a){this.j=a};k.wi=function(a){this.v=a};function ky(a,b){a.wi(ao(b,a.l,a.ho.bind(a),a.fo.bind(a)))};function ly(a){mx.call(this,{attributions:a.attributions,cacheSize:void 0!==a.cacheSize?a.cacheSize:128,extent:a.extent,logo:a.logo,opaque:!1,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction?a.tileLoadFunction:ky,tileUrlFunction:a.tileUrlFunction,tilePixelRatio:a.tilePixelRatio,url:a.url,urls:a.urls,wrapX:void 0===a.wrapX?!0:a.wrapX});this.c=a.format?a.format:null;this.i=void 0==a.overlaps?!0:a.overlaps;this.tileClass=a.tileClass?a.tileClass:jy}v(ly,mx);
+ly.prototype.Dc=function(a,b,c,d,e){var f=this.Lb(a,b,c);if(this.a.b.hasOwnProperty(f))return this.a.get(f);a=[a,b,c];d=(b=kx(this,a,e))?this.tileUrlFunction(b,d,e):void 0;d=new this.tileClass(a,void 0!==d?0:4,void 0!==d?d:"",this.c,this.tileLoadFunction);B(d,"change",this.Jh,this);this.a.set(f,d);return d};ly.prototype.jb=function(a){return void 0==a?mx.prototype.jb.call(this,a):a};ly.prototype.Dd=function(a,b){var c=Zd(this.tileGrid.Za(a));return[Math.round(c[0]*b),Math.round(c[1]*b)]};function my(a){this.l=a.matrixIds;be.call(this,{extent:a.extent,origin:a.origin,origins:a.origins,resolutions:a.resolutions,tileSize:a.tileSize,tileSizes:a.tileSizes,sizes:a.sizes})}v(my,be);my.prototype.o=function(){return this.l};
+function ny(a,b,c){var d=[],e=[],f=[],g=[],h=[],l=void 0!==c?c:[];c=zc(a.SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"));var m=c.ic(),p="ne"==c.b.substr(0,2);a.TileMatrix.sort(function(a,b){return b.ScaleDenominator-a.ScaleDenominator});a.TileMatrix.forEach(function(a){var b;0<l.length?b=cb(l,function(b){return a.Identifier==b.TileMatrix}):b=!0;if(b){e.push(a.Identifier);b=2.8E-4*a.ScaleDenominator/m;var c=a.TileWidth,u=a.TileHeight;p?f.push([a.TopLeftCorner[1],a.TopLeftCorner[0]]):
+f.push(a.TopLeftCorner);d.push(b);g.push(c==u?c:[c,u]);h.push([a.MatrixWidth,-a.MatrixHeight])}});return new my({extent:b,origins:f,resolutions:d,matrixIds:e,tileSizes:g,sizes:h})};function Z(a){function b(a){a=d==oy?yx(a,f):a.replace(/\{(\w+?)\}/g,function(a,b){return b.toLowerCase()in f?f[b.toLowerCase()]:a});return function(b){if(b){var c={TileMatrix:e.l[b[0]],TileCol:b[1],TileRow:-b[2]-1};ta(c,g);b=a;return b=d==oy?yx(b,c):b.replace(/\{(\w+?)\}/g,function(a,b){return c[b]})}}}this.Z=void 0!==a.version?a.version:"1.0.0";this.u=void 0!==a.format?a.format:"image/jpeg";this.c=void 0!==a.dimensions?a.dimensions:{};this.A=a.layer;this.o=a.matrixSet;this.P=a.style;var c=a.urls;
+void 0===c&&void 0!==a.url&&(c=fx(a.url));var d=this.U=void 0!==a.requestEncoding?a.requestEncoding:oy,e=a.tileGrid,f={layer:this.A,style:this.P,tilematrixset:this.o};d==oy&&ta(f,{Service:"WMTS",Request:"GetTile",Version:this.Z,Format:this.u});var g=this.c,h=c&&0<c.length?dx(c.map(b)):ex;X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileClass:a.tileClass,tileGrid:e,
+tileLoadFunction:a.tileLoadFunction,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:h,urls:c,wrapX:void 0!==a.wrapX?a.wrapX:!1});jx(this,py(this))}v(Z,X);k=Z.prototype;k.bk=function(){return this.c};k.Dn=function(){return this.u};k.En=function(){return this.A};k.pk=function(){return this.o};k.Ak=function(){return this.U};k.Fn=function(){return this.P};k.Hk=function(){return this.Z};function py(a){var b=0,c=[],d;for(d in a.c)c[b++]=d+"-"+a.c[d];return c.join("/")}
+k.xp=function(a){ta(this.c,a);jx(this,py(this))};var oy="KVP";function qy(a){a=a||{};var b=a.size,c=b[0],d=b[1],e=[],f=256;switch(void 0!==a.tierSizeCalculation?a.tierSizeCalculation:ry){case ry:for(;c>f||d>f;)e.push([Math.ceil(c/f),Math.ceil(d/f)]),f+=f;break;case sy:for(;c>f||d>f;)e.push([Math.ceil(c/f),Math.ceil(d/f)]),c>>=1,d>>=1;break;default:ha(!1,53)}e.push([1,1]);e.reverse();for(var f=[1],g=[0],d=1,c=e.length;d<c;d++)f.push(1<<d),g.push(e[d-1][0]*e[d-1][1]+g[d-1]);f.reverse();var b=[0,-b[1],b[0],0],b=new be({extent:b,origin:ac(b),resolutions:f}),h=a.url;
+X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileClass:ty,tileGrid:b,tileUrlFunction:function(a){if(a){var b=a[0],c=a[1];a=-a[2]-1;return h+"TileGroup"+((c+a*e[b][0]+g[b])/256|0)+"/"+b+"-"+c+"-"+a+".jpg"}}})}v(qy,X);function ty(a,b,c,d,e){lv.call(this,a,b,c,d,e);this.f=null}v(ty,lv);
+ty.prototype.ub=function(){if(this.f)return this.f;var a=lv.prototype.ub.call(this);if(this.state==cg){if(256==a.width&&256==a.height)return this.f=a;var b=De(256,256);b.drawImage(a,0,0);return this.f=b.canvas}return a};var ry="default",sy="truncated";function uy(a,b){this.b=b;this.a=[{x:0,y:0,width:a,height:a}];this.f={};this.g=De(a,a);this.c=this.g.canvas}uy.prototype.get=function(a){return this.f[a]||null};
+uy.prototype.add=function(a,b,c,d,e){var f,g,h;g=0;for(h=this.a.length;g<h;++g)if(f=this.a[g],f.width>=b+this.b&&f.height>=c+this.b)return h={offsetX:f.x+this.b,offsetY:f.y+this.b,image:this.c},this.f[a]=h,d.call(e,this.g,f.x+this.b,f.y+this.b),a=g,b+=this.b,d=c+this.b,f.width-b>f.height-d?(c={x:f.x+b,y:f.y,width:f.width-b,height:f.height},b={x:f.x,y:f.y+d,width:b,height:f.height-d},vy(this,a,c,b)):(c={x:f.x+b,y:f.y,width:f.width-b,height:d},b={x:f.x,y:f.y+d,width:f.width,height:f.height-d},vy(this,
+a,c,b)),h;return null};function vy(a,b,c,d){b=[b,1];0<c.width&&0<c.height&&b.push(c);0<d.width&&0<d.height&&b.push(d);a.a.splice.apply(a.a,b)};function wy(a){a=a||{};this.a=void 0!==a.initialSize?a.initialSize:256;this.g=void 0!==a.maxSize?a.maxSize:void 0!==ba?ba:2048;this.b=void 0!==a.space?a.space:1;this.c=[new uy(this.a,this.b)];this.f=this.a;this.i=[new uy(this.f,this.b)]}wy.prototype.add=function(a,b,c,d,e,f){if(b+this.b>this.g||c+this.b>this.g)return null;d=xy(this,!1,a,b,c,d,f);if(!d)return null;a=xy(this,!0,a,b,c,void 0!==e?e:ea,f);return{offsetX:d.offsetX,offsetY:d.offsetY,image:d.image,yf:a.image}};
+function xy(a,b,c,d,e,f,g){var h=b?a.i:a.c,l,m,p;m=0;for(p=h.length;m<p;++m){l=h[m];if(l=l.add(c,d,e,f,g))return l;l||m!==p-1||(b?(l=Math.min(2*a.f,a.g),a.f=l):(l=Math.min(2*a.a,a.g),a.a=l),l=new uy(l,a.b),h.push(l),++p)}return null};t("ol.animation.bounce",function(a){var b=a.resolution,c=a.start?a.start:Date.now(),d=void 0!==a.duration?a.duration:1E3,e=a.easing?a.easing:Fb;return function(a,g){if(g.time<c)return g.animate=!0,g.viewHints[Kd]+=1,!0;if(g.time<c+d){var h=e((g.time-c)/d),l=b-g.viewState.resolution;g.animate=!0;g.viewState.resolution+=h*l;g.viewHints[Kd]+=1;return!0}return!1}});
+t("ol.animation.pan",function(a){var b=a.source,c=a.start?a.start:Date.now(),d=b[0],e=b[1],f=void 0!==a.duration?a.duration:1E3,g=a.easing?a.easing:Db;return function(a,b){if(b.time<c)return b.animate=!0,b.viewHints[Kd]+=1,!0;if(b.time<c+f){var m=1-g((b.time-c)/f),p=d-b.viewState.center[0],n=e-b.viewState.center[1];b.animate=!0;b.viewState.center[0]+=m*p;b.viewState.center[1]+=m*n;b.viewHints[Kd]+=1;return!0}return!1}});
+t("ol.animation.rotate",function(a){var b=a.rotation?a.rotation:0,c=a.start?a.start:Date.now(),d=void 0!==a.duration?a.duration:1E3,e=a.easing?a.easing:Db,f=a.anchor?a.anchor:null;return function(a,h){if(h.time<c)return h.animate=!0,h.viewHints[Kd]+=1,!0;if(h.time<c+d){var l=1-e((h.time-c)/d),l=(b-h.viewState.rotation)*l;h.animate=!0;h.viewState.rotation+=l;if(f){var m=h.viewState.center;m[0]-=f[0];m[1]-=f[1];wb(m,l);rb(m,f)}h.viewHints[Kd]+=1;return!0}return!1}});
+t("ol.animation.zoom",function(a){var b=a.resolution,c=a.start?a.start:Date.now(),d=void 0!==a.duration?a.duration:1E3,e=a.easing?a.easing:Db;return function(a,g){if(g.time<c)return g.animate=!0,g.viewHints[Kd]+=1,!0;if(g.time<c+d){var h=1-e((g.time-c)/d),l=b-g.viewState.resolution;g.animate=!0;g.viewState.resolution+=h*l;g.viewHints[Kd]+=1;return!0}return!1}});ga.prototype.code=ga.prototype.code;t("ol.Attribution",pe);pe.prototype.getHTML=pe.prototype.g;t("ol.Collection",qe);qe.prototype.clear=qe.prototype.clear;
+qe.prototype.extend=qe.prototype.Bf;qe.prototype.forEach=qe.prototype.forEach;qe.prototype.getArray=qe.prototype.Il;qe.prototype.item=qe.prototype.item;qe.prototype.getLength=qe.prototype.Ub;qe.prototype.insertAt=qe.prototype.ke;qe.prototype.pop=qe.prototype.pop;qe.prototype.push=qe.prototype.push;qe.prototype.remove=qe.prototype.remove;qe.prototype.removeAt=qe.prototype.Zf;qe.prototype.setAt=qe.prototype.ep;te.prototype.element=te.prototype.element;t("ol.color.asArray",ye);
+t("ol.color.asString",Ae);t("ol.colorlike.asColorLike",Ce);t("ol.coordinate.add",rb);t("ol.coordinate.createStringXY",function(a){return function(b){return Ab(b,a)}});t("ol.coordinate.format",ub);t("ol.coordinate.rotate",wb);t("ol.coordinate.toStringHDMS",function(a,b){return a?tb(a[1],"NS",b)+" "+tb(a[0],"EW",b):""});t("ol.coordinate.toStringXY",Ab);t("ol.DeviceOrientation",Cn);Cn.prototype.getAlpha=Cn.prototype.Vj;Cn.prototype.getBeta=Cn.prototype.Yj;Cn.prototype.getGamma=Cn.prototype.ek;
+Cn.prototype.getHeading=Cn.prototype.Jl;Cn.prototype.getTracking=Cn.prototype.kh;Cn.prototype.setTracking=Cn.prototype.Cf;t("ol.easing.easeIn",Bb);t("ol.easing.easeOut",Cb);t("ol.easing.inAndOut",Db);t("ol.easing.linear",Eb);t("ol.easing.upAndDown",Fb);t("ol.Feature",J);J.prototype.clone=J.prototype.clone;J.prototype.getGeometry=J.prototype.V;J.prototype.getId=J.prototype.Ll;J.prototype.getGeometryName=J.prototype.gk;J.prototype.getStyle=J.prototype.Ml;J.prototype.getStyleFunction=J.prototype.Gc;
+J.prototype.setGeometry=J.prototype.Pa;J.prototype.setStyle=J.prototype.Df;J.prototype.setId=J.prototype.cc;J.prototype.setGeometryName=J.prototype.Nc;t("ol.featureloader.xhr",bo);t("ol.Geolocation",Ru);Ru.prototype.getAccuracy=Ru.prototype.Tj;Ru.prototype.getAccuracyGeometry=Ru.prototype.Uj;Ru.prototype.getAltitude=Ru.prototype.Wj;Ru.prototype.getAltitudeAccuracy=Ru.prototype.Xj;Ru.prototype.getHeading=Ru.prototype.Ol;Ru.prototype.getPosition=Ru.prototype.Pl;Ru.prototype.getProjection=Ru.prototype.lh;
+Ru.prototype.getSpeed=Ru.prototype.Bk;Ru.prototype.getTracking=Ru.prototype.mh;Ru.prototype.getTrackingOptions=Ru.prototype.Wg;Ru.prototype.setProjection=Ru.prototype.nh;Ru.prototype.setTracking=Ru.prototype.ne;Ru.prototype.setTrackingOptions=Ru.prototype.Ci;t("ol.Graticule",gv);gv.prototype.getMap=gv.prototype.Sl;gv.prototype.getMeridians=gv.prototype.qk;gv.prototype.getParallels=gv.prototype.xk;gv.prototype.setMap=gv.prototype.setMap;t("ol.has.DEVICE_PIXEL_RATIO",mf);t("ol.has.CANVAS",of);
+t("ol.has.DEVICE_ORIENTATION",pf);t("ol.has.GEOLOCATION",qf);t("ol.has.TOUCH",rf);t("ol.has.WEBGL",ff);ii.prototype.getImage=ii.prototype.a;ii.prototype.load=ii.prototype.load;lv.prototype.getImage=lv.prototype.ub;lv.prototype.load=lv.prototype.load;t("ol.inherits",v);t("ol.Kinetic",kg);t("ol.loadingstrategy.all",wv);t("ol.loadingstrategy.bbox",function(a){return[a]});
+t("ol.loadingstrategy.tile",function(a){return function(b,c){var d=a.Ec(c),e=fe(a,b,d),f=[],d=[d,0,0];for(d[1]=e.ea;d[1]<=e.ca;++d[1])for(d[2]=e.ga;d[2]<=e.ja;++d[2])f.push(a.Na(d));return f}});t("ol.Map",I);I.prototype.addControl=I.prototype.Bj;I.prototype.addInteraction=I.prototype.Cj;I.prototype.addLayer=I.prototype.ug;I.prototype.addOverlay=I.prototype.vg;I.prototype.beforeRender=I.prototype.Ij;I.prototype.forEachFeatureAtPixel=I.prototype.ae;I.prototype.forEachLayerAtPixel=I.prototype.Wl;
+I.prototype.hasFeatureAtPixel=I.prototype.ml;I.prototype.getEventCoordinate=I.prototype.ck;I.prototype.getEventPixel=I.prototype.ce;I.prototype.getTarget=I.prototype.vf;I.prototype.getTargetElement=I.prototype.Cc;I.prototype.getCoordinateFromPixel=I.prototype.Sa;I.prototype.getControls=I.prototype.ak;I.prototype.getOverlays=I.prototype.vk;I.prototype.getOverlayById=I.prototype.uk;I.prototype.getInteractions=I.prototype.hk;I.prototype.getLayerGroup=I.prototype.Bc;I.prototype.getLayers=I.prototype.oh;
+I.prototype.getPixelFromCoordinate=I.prototype.Ga;I.prototype.getSize=I.prototype.nb;I.prototype.getView=I.prototype.aa;I.prototype.getViewport=I.prototype.Ik;I.prototype.renderSync=I.prototype.$o;I.prototype.render=I.prototype.render;I.prototype.removeControl=I.prototype.To;I.prototype.removeInteraction=I.prototype.Uo;I.prototype.removeLayer=I.prototype.Wo;I.prototype.removeOverlay=I.prototype.Xo;I.prototype.setLayerGroup=I.prototype.vi;I.prototype.setSize=I.prototype.eg;I.prototype.setTarget=I.prototype.ph;
+I.prototype.setView=I.prototype.mp;I.prototype.updateSize=I.prototype.ld;$e.prototype.originalEvent=$e.prototype.originalEvent;$e.prototype.pixel=$e.prototype.pixel;$e.prototype.coordinate=$e.prototype.coordinate;$e.prototype.dragging=$e.prototype.dragging;Ge.prototype.map=Ge.prototype.map;Ge.prototype.frameState=Ge.prototype.frameState;t("ol.Object",Sa);Sa.prototype.get=Sa.prototype.get;Sa.prototype.getKeys=Sa.prototype.S;Sa.prototype.getProperties=Sa.prototype.R;Sa.prototype.set=Sa.prototype.set;
+Sa.prototype.setProperties=Sa.prototype.I;Sa.prototype.unset=Sa.prototype.T;Wa.prototype.key=Wa.prototype.key;Wa.prototype.oldValue=Wa.prototype.oldValue;t("ol.Observable",Pa);t("ol.Observable.unByKey",Qa);Pa.prototype.changed=Pa.prototype.s;Pa.prototype.dispatchEvent=Pa.prototype.b;Pa.prototype.getRevision=Pa.prototype.M;Pa.prototype.on=Pa.prototype.J;Pa.prototype.once=Pa.prototype.N;Pa.prototype.un=Pa.prototype.K;Pa.prototype.unByKey=Pa.prototype.O;t("ol.Overlay",Pm);Pm.prototype.getElement=Pm.prototype.be;
+Pm.prototype.getId=Pm.prototype.Xl;Pm.prototype.getMap=Pm.prototype.oe;Pm.prototype.getOffset=Pm.prototype.Tg;Pm.prototype.getPosition=Pm.prototype.qh;Pm.prototype.getPositioning=Pm.prototype.Ug;Pm.prototype.setElement=Pm.prototype.pi;Pm.prototype.setMap=Pm.prototype.setMap;Pm.prototype.setOffset=Pm.prototype.xi;Pm.prototype.setPosition=Pm.prototype.Ef;Pm.prototype.setPositioning=Pm.prototype.Ai;
+t("ol.render.toContext",function(a,b){var c=a.canvas,d=b?b:{},e=d.pixelRatio||mf;if(d=d.size)c.width=d[0]*e,c.height=d[1]*e,c.style.width=d[0]+"px",c.style.height=d[1]+"px";c=[0,0,c.width,c.height];d=Wh(Ph(),e,e);return new Ji(a,e,c,d,0)});t("ol.size.toSize",Zd);ag.prototype.getTileCoord=ag.prototype.c;ag.prototype.load=ag.prototype.load;jy.prototype.getFormat=jy.prototype.Yl;jy.prototype.setFeatures=jy.prototype.ri;jy.prototype.setProjection=jy.prototype.Ff;jy.prototype.setLoader=jy.prototype.wi;
+t("ol.View",Fd);Fd.prototype.animate=Fd.prototype.animate;Fd.prototype.constrainCenter=Fd.prototype.Zd;Fd.prototype.constrainResolution=Fd.prototype.constrainResolution;Fd.prototype.constrainRotation=Fd.prototype.constrainRotation;Fd.prototype.getCenter=Fd.prototype.fb;Fd.prototype.calculateExtent=Fd.prototype.Uc;Fd.prototype.getMaxResolution=Fd.prototype.Zl;Fd.prototype.getMinResolution=Fd.prototype.$l;Fd.prototype.getProjection=Fd.prototype.am;Fd.prototype.getResolution=Fd.prototype.Oa;
+Fd.prototype.getResolutions=Fd.prototype.bm;Fd.prototype.getRotation=Fd.prototype.Ra;Fd.prototype.getZoom=Fd.prototype.Kk;Fd.prototype.fit=Fd.prototype.lf;Fd.prototype.centerOn=Fd.prototype.Kj;Fd.prototype.rotate=Fd.prototype.rotate;Fd.prototype.setCenter=Fd.prototype.Mb;Fd.prototype.setResolution=Fd.prototype.Oc;Fd.prototype.setRotation=Fd.prototype.pe;Fd.prototype.setZoom=Fd.prototype.pp;t("ol.xml.getAllTextContent",Ln);t("ol.xml.parse",Pn);xk.prototype.getGL=xk.prototype.bo;
+xk.prototype.useProgram=xk.prototype.Lc;t("ol.tilegrid.createXYZ",oe);t("ol.tilegrid.TileGrid",be);be.prototype.forEachTileCoord=be.prototype.Hg;be.prototype.getMaxZoom=be.prototype.Rg;be.prototype.getMinZoom=be.prototype.Sg;be.prototype.getOrigin=be.prototype.Kc;be.prototype.getResolution=be.prototype.Ha;be.prototype.getResolutions=be.prototype.Wh;be.prototype.getTileCoordExtent=be.prototype.Na;be.prototype.getTileCoordForCoordAndResolution=be.prototype.fe;be.prototype.getTileCoordForCoordAndZ=be.prototype.wf;
+be.prototype.getTileSize=be.prototype.Za;be.prototype.getZForResolution=be.prototype.Ec;t("ol.tilegrid.WMTS",my);my.prototype.getMatrixIds=my.prototype.o;t("ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet",ny);t("ol.style.AtlasManager",wy);t("ol.style.Circle",ui);ui.prototype.clone=ui.prototype.clone;ui.prototype.setRadius=ui.prototype.Qa;t("ol.style.Fill",vi);vi.prototype.clone=vi.prototype.clone;vi.prototype.getColor=vi.prototype.g;vi.prototype.setColor=vi.prototype.f;t("ol.style.Icon",xq);
+xq.prototype.clone=xq.prototype.clone;xq.prototype.getAnchor=xq.prototype.Ac;xq.prototype.getColor=xq.prototype.Gn;xq.prototype.getImage=xq.prototype.Ic;xq.prototype.getOrigin=xq.prototype.Jc;xq.prototype.getSrc=xq.prototype.Hn;xq.prototype.getSize=xq.prototype.ac;xq.prototype.load=xq.prototype.load;t("ol.style.Image",ri);ri.prototype.getOpacity=ri.prototype.ye;ri.prototype.getRotateWithView=ri.prototype.ze;ri.prototype.getRotation=ri.prototype.Ae;ri.prototype.getScale=ri.prototype.Be;
+ri.prototype.getSnapToPixel=ri.prototype.ee;ri.prototype.setOpacity=ri.prototype.dd;ri.prototype.setRotation=ri.prototype.Ce;ri.prototype.setScale=ri.prototype.ed;t("ol.style.RegularShape",si);si.prototype.clone=si.prototype.clone;si.prototype.getAnchor=si.prototype.Ac;si.prototype.getAngle=si.prototype.Nh;si.prototype.getFill=si.prototype.Oh;si.prototype.getImage=si.prototype.Ic;si.prototype.getOrigin=si.prototype.Jc;si.prototype.getPoints=si.prototype.Ph;si.prototype.getRadius=si.prototype.Qh;
+si.prototype.getRadius2=si.prototype.Vg;si.prototype.getSize=si.prototype.ac;si.prototype.getStroke=si.prototype.Rh;t("ol.style.Stroke",wi);wi.prototype.clone=wi.prototype.clone;wi.prototype.getColor=wi.prototype.In;wi.prototype.getLineCap=wi.prototype.kk;wi.prototype.getLineDash=wi.prototype.Jn;wi.prototype.getLineJoin=wi.prototype.lk;wi.prototype.getMiterLimit=wi.prototype.rk;wi.prototype.getWidth=wi.prototype.Kn;wi.prototype.setColor=wi.prototype.Ln;wi.prototype.setLineCap=wi.prototype.ip;
+wi.prototype.setLineDash=wi.prototype.setLineDash;wi.prototype.setLineJoin=wi.prototype.jp;wi.prototype.setMiterLimit=wi.prototype.kp;wi.prototype.setWidth=wi.prototype.np;t("ol.style.Style",xi);xi.prototype.clone=xi.prototype.clone;xi.prototype.getGeometry=xi.prototype.V;xi.prototype.getGeometryFunction=xi.prototype.fk;xi.prototype.getFill=xi.prototype.Mn;xi.prototype.setFill=xi.prototype.Qn;xi.prototype.getImage=xi.prototype.Nn;xi.prototype.setImage=xi.prototype.Rn;xi.prototype.getStroke=xi.prototype.On;
+xi.prototype.setStroke=xi.prototype.Sn;xi.prototype.getText=xi.prototype.Ka;xi.prototype.setText=xi.prototype.Tn;xi.prototype.getZIndex=xi.prototype.Pn;xi.prototype.setGeometry=xi.prototype.Pa;xi.prototype.setZIndex=xi.prototype.Un;t("ol.style.Text",Dq);Dq.prototype.clone=Dq.prototype.clone;Dq.prototype.getFont=Dq.prototype.dk;Dq.prototype.getOffsetX=Dq.prototype.sk;Dq.prototype.getOffsetY=Dq.prototype.tk;Dq.prototype.getFill=Dq.prototype.Vn;Dq.prototype.getRotateWithView=Dq.prototype.Wn;
+Dq.prototype.getRotation=Dq.prototype.Xn;Dq.prototype.getScale=Dq.prototype.Yn;Dq.prototype.getStroke=Dq.prototype.Zn;Dq.prototype.getText=Dq.prototype.Ka;Dq.prototype.getTextAlign=Dq.prototype.Dk;Dq.prototype.getTextBaseline=Dq.prototype.Ek;Dq.prototype.setFont=Dq.prototype.si;Dq.prototype.setOffsetX=Dq.prototype.yi;Dq.prototype.setOffsetY=Dq.prototype.zi;Dq.prototype.setFill=Dq.prototype.Sh;Dq.prototype.setRotation=Dq.prototype.$n;Dq.prototype.setScale=Dq.prototype.Th;Dq.prototype.setStroke=Dq.prototype.Uh;
+Dq.prototype.setText=Dq.prototype.Vh;Dq.prototype.setTextAlign=Dq.prototype.Bi;Dq.prototype.setTextBaseline=Dq.prototype.lp;t("ol.Sphere",oc);oc.prototype.geodesicArea=oc.prototype.a;oc.prototype.haversineDistance=oc.prototype.b;t("ol.source.BingMaps",qx);t("ol.source.BingMaps.TOS_ATTRIBUTION",rx);qx.prototype.getApiKey=qx.prototype.U;qx.prototype.getImagerySet=qx.prototype.Z;t("ol.source.CartoDB",tx);tx.prototype.getConfig=tx.prototype.$j;tx.prototype.updateConfig=tx.prototype.wp;
+tx.prototype.setConfig=tx.prototype.fp;t("ol.source.Cluster",Y);Y.prototype.getSource=Y.prototype.Ua;Y.prototype.setDistance=Y.prototype.Ob;t("ol.source.Image",km);mm.prototype.image=mm.prototype.image;t("ol.source.ImageArcGISRest",zx);zx.prototype.getParams=zx.prototype.Xm;zx.prototype.getImageLoadFunction=zx.prototype.Wm;zx.prototype.getUrl=zx.prototype.Ym;zx.prototype.setImageLoadFunction=zx.prototype.Zm;zx.prototype.setUrl=zx.prototype.$m;zx.prototype.updateParams=zx.prototype.an;
+t("ol.source.ImageCanvas",rm);t("ol.source.ImageMapGuide",Ax);Ax.prototype.getParams=Ax.prototype.cn;Ax.prototype.getImageLoadFunction=Ax.prototype.bn;Ax.prototype.updateParams=Ax.prototype.en;Ax.prototype.setImageLoadFunction=Ax.prototype.dn;t("ol.source.ImageStatic",Bx);t("ol.source.ImageVector",sm);sm.prototype.getSource=sm.prototype.fn;sm.prototype.getStyle=sm.prototype.gn;sm.prototype.getStyleFunction=sm.prototype.hn;sm.prototype.setStyle=sm.prototype.Fh;t("ol.source.ImageWMS",Cx);
+Cx.prototype.getGetFeatureInfoUrl=Cx.prototype.ln;Cx.prototype.getParams=Cx.prototype.nn;Cx.prototype.getImageLoadFunction=Cx.prototype.mn;Cx.prototype.getUrl=Cx.prototype.pn;Cx.prototype.setImageLoadFunction=Cx.prototype.qn;Cx.prototype.setUrl=Cx.prototype.rn;Cx.prototype.updateParams=Cx.prototype.sn;t("ol.source.OSM",Gx);t("ol.source.OSM.ATTRIBUTION",Hx);t("ol.source.Raster",Ix);Ix.prototype.setOperation=Ix.prototype.u;Px.prototype.extent=Px.prototype.extent;Px.prototype.resolution=Px.prototype.resolution;
+Px.prototype.data=Px.prototype.data;t("ol.source.Source",hm);hm.prototype.getAttributions=hm.prototype.za;hm.prototype.getLogo=hm.prototype.ya;hm.prototype.getProjection=hm.prototype.Aa;hm.prototype.getState=hm.prototype.W;hm.prototype.refresh=hm.prototype.wa;hm.prototype.setAttributions=hm.prototype.ua;t("ol.source.Stamen",Sx);t("ol.source.Tile",ix);ix.prototype.getTileGrid=ix.prototype.Va;lx.prototype.tile=lx.prototype.tile;t("ol.source.TileArcGISRest",Wx);Wx.prototype.getParams=Wx.prototype.u;
+Wx.prototype.updateParams=Wx.prototype.A;t("ol.source.TileDebug",Yx);t("ol.source.TileImage",X);X.prototype.setRenderReprojectionEdges=X.prototype.Hb;X.prototype.setTileGridForProjection=X.prototype.Ib;t("ol.source.TileJSON",$x);$x.prototype.getTileJSON=$x.prototype.Fk;t("ol.source.TileUTFGrid",ay);ay.prototype.getTemplate=ay.prototype.Ck;ay.prototype.forDataAtCoordinateAndResolution=ay.prototype.Pj;t("ol.source.TileWMS",ey);ey.prototype.getGetFeatureInfoUrl=ey.prototype.An;
+ey.prototype.getParams=ey.prototype.Bn;ey.prototype.updateParams=ey.prototype.Cn;mx.prototype.getTileLoadFunction=mx.prototype.ib;mx.prototype.getTileUrlFunction=mx.prototype.kb;mx.prototype.getUrls=mx.prototype.lb;mx.prototype.setTileLoadFunction=mx.prototype.sb;mx.prototype.setTileUrlFunction=mx.prototype.Xa;mx.prototype.setUrl=mx.prototype.cb;mx.prototype.setUrls=mx.prototype.Ya;t("ol.source.Vector",U);U.prototype.addFeature=U.prototype.gb;U.prototype.addFeatures=U.prototype.Tc;
+U.prototype.clear=U.prototype.clear;U.prototype.forEachFeature=U.prototype.Fg;U.prototype.forEachFeatureInExtent=U.prototype.Qb;U.prototype.forEachFeatureIntersectingExtent=U.prototype.Gg;U.prototype.getFeaturesCollection=U.prototype.Og;U.prototype.getFeatures=U.prototype.we;U.prototype.getFeaturesAtCoordinate=U.prototype.Ng;U.prototype.getFeaturesInExtent=U.prototype.nf;U.prototype.getClosestFeatureToCoordinate=U.prototype.Jg;U.prototype.getExtent=U.prototype.G;U.prototype.getFeatureById=U.prototype.Mg;
+U.prototype.getFormat=U.prototype.Kh;U.prototype.getUrl=U.prototype.Lh;U.prototype.removeFeature=U.prototype.rb;Bv.prototype.feature=Bv.prototype.feature;t("ol.source.VectorTile",ly);t("ol.source.WMTS",Z);Z.prototype.getDimensions=Z.prototype.bk;Z.prototype.getFormat=Z.prototype.Dn;Z.prototype.getLayer=Z.prototype.En;Z.prototype.getMatrixSet=Z.prototype.pk;Z.prototype.getRequestEncoding=Z.prototype.Ak;Z.prototype.getStyle=Z.prototype.Fn;Z.prototype.getVersion=Z.prototype.Hk;
+Z.prototype.updateDimensions=Z.prototype.xp;
+t("ol.source.WMTS.optionsFromCapabilities",function(a,b){var c=cb(a.Contents.Layer,function(a){return a.Identifier==b.layer}),d=a.Contents.TileMatrixSet,e,f,g;e=1<c.TileMatrixSetLink.length?"projection"in b?gb(c.TileMatrixSetLink,function(a){var c=cb(d,function(b){return b.Identifier==a.TileMatrixSet}).SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"),e=zc(c),f=zc(b.projection);return e&&f?Mc(e,f):c==b.projection}):gb(c.TileMatrixSetLink,function(a){return a.TileMatrixSet==b.matrixSet}):
+0;0>e&&(e=0);f=c.TileMatrixSetLink[e].TileMatrixSet;g=c.TileMatrixSetLink[e].TileMatrixSetLimits;var h=c.Format[0];"format"in b&&(h=b.format);e=gb(c.Style,function(a){return"style"in b?a.Title==b.style:a.isDefault});0>e&&(e=0);e=c.Style[e].Identifier;var l={};"Dimension"in c&&c.Dimension.forEach(function(a){var b=a.Identifier,c=a.Default;void 0===c&&(c=a.Value[0]);l[b]=c});var m=cb(a.Contents.TileMatrixSet,function(a){return a.Identifier==f}),p;p="projection"in b?zc(b.projection):zc(m.SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,
+"$1:$3"));var n=c.WGS84BoundingBox,q,r;void 0!==n&&(r=zc("EPSG:4326").G(),r=n[0]==r[0]&&n[2]==r[2],q=Qc(n,"EPSG:4326",p),(n=p.G())&&(Ob(n,q)||(q=void 0)));g=ny(m,q,g);var u=[],m=b.requestEncoding,m=void 0!==m?m:"";if("OperationsMetadata"in a&&"GetTile"in a.OperationsMetadata){q=a.OperationsMetadata.GetTile.DCP.HTTP.Get;for(var n=0,w=q.length;n<w;++n){var y=cb(q[n].Constraint,function(a){return"GetEncoding"==a.name}).AllowedValues.Value;""===m&&(m=y[0]);if(m===oy)Za(y,oy)&&u.push(q[n].href);else break}}0===
+u.length&&(m="REST",c.ResourceURL.forEach(function(a){"tile"===a.resourceType&&(h=a.format,u.push(a.template))}));return{urls:u,layer:b.layer,matrixSet:f,format:h,projection:p,requestEncoding:m,tileGrid:g,style:e,dimensions:l,wrapX:r}});t("ol.source.XYZ",sx);t("ol.source.Zoomify",qy);Jh.prototype.vectorContext=Jh.prototype.vectorContext;Jh.prototype.frameState=Jh.prototype.frameState;Jh.prototype.context=Jh.prototype.context;Jh.prototype.glContext=Jh.prototype.glContext;Is.prototype.get=Is.prototype.get;
+Is.prototype.getExtent=Is.prototype.G;Is.prototype.getGeometry=Is.prototype.V;Is.prototype.getProperties=Is.prototype.Tm;Is.prototype.getType=Is.prototype.Y;t("ol.render.VectorContext",Ii);Ol.prototype.setStyle=Ol.prototype.Gd;Ol.prototype.drawGeometry=Ol.prototype.tc;Ol.prototype.drawFeature=Ol.prototype.jf;Ji.prototype.drawCircle=Ji.prototype.hc;Ji.prototype.setStyle=Ji.prototype.Gd;Ji.prototype.drawGeometry=Ji.prototype.tc;Ji.prototype.drawFeature=Ji.prototype.jf;t("ol.proj.common.add",Ih);
+t("ol.proj.METERS_PER_UNIT",qc);t("ol.proj.setProj4",function(a){rc=a});t("ol.proj.getPointResolution",xc);t("ol.proj.addEquivalentProjections",Ac);t("ol.proj.addProjection",Ic);t("ol.proj.addCoordinateTransforms",Kc);t("ol.proj.fromLonLat",function(a,b){return Pc(a,"EPSG:4326",void 0!==b?b:"EPSG:3857")});t("ol.proj.toLonLat",function(a,b){return Pc(a,void 0!==b?b:"EPSG:3857","EPSG:4326")});t("ol.proj.get",zc);t("ol.proj.equivalent",Mc);t("ol.proj.getTransform",Nc);t("ol.proj.transform",Pc);
+t("ol.proj.transformExtent",Qc);t("ol.proj.Projection",sc);sc.prototype.getCode=sc.prototype.Zj;sc.prototype.getExtent=sc.prototype.G;sc.prototype.getUnits=sc.prototype.Eb;sc.prototype.getMetersPerUnit=sc.prototype.ic;sc.prototype.getWorldExtent=sc.prototype.Jk;sc.prototype.isGlobal=sc.prototype.rl;sc.prototype.setGlobal=sc.prototype.hp;sc.prototype.setExtent=sc.prototype.Sm;sc.prototype.setWorldExtent=sc.prototype.op;sc.prototype.setGetPointResolution=sc.prototype.gp;
+t("ol.proj.Units.METERS_PER_UNIT",qc);t("ol.layer.Base",qh);qh.prototype.getExtent=qh.prototype.G;qh.prototype.getMaxResolution=qh.prototype.Wb;qh.prototype.getMinResolution=qh.prototype.Xb;qh.prototype.getOpacity=qh.prototype.Yb;qh.prototype.getVisible=qh.prototype.Fb;qh.prototype.getZIndex=qh.prototype.Zb;qh.prototype.setExtent=qh.prototype.kc;qh.prototype.setMaxResolution=qh.prototype.pc;qh.prototype.setMinResolution=qh.prototype.qc;qh.prototype.setOpacity=qh.prototype.lc;
+qh.prototype.setVisible=qh.prototype.mc;qh.prototype.setZIndex=qh.prototype.nc;t("ol.layer.Group",yh);yh.prototype.getLayers=yh.prototype.cd;yh.prototype.setLayers=yh.prototype.yh;t("ol.layer.Heatmap",V);V.prototype.getBlur=V.prototype.Ig;V.prototype.getGradient=V.prototype.Pg;V.prototype.getRadius=V.prototype.zh;V.prototype.setBlur=V.prototype.ni;V.prototype.setGradient=V.prototype.ui;V.prototype.setRadius=V.prototype.Ah;t("ol.layer.Image",ei);ei.prototype.getSource=ei.prototype.la;
+t("ol.layer.Layer",Kh);Kh.prototype.getSource=Kh.prototype.la;Kh.prototype.setMap=Kh.prototype.setMap;Kh.prototype.setSource=Kh.prototype.Pc;t("ol.layer.Tile",F);F.prototype.getPreload=F.prototype.f;F.prototype.getSource=F.prototype.la;F.prototype.setPreload=F.prototype.l;F.prototype.getUseInterimTilesOnError=F.prototype.c;F.prototype.setUseInterimTilesOnError=F.prototype.C;t("ol.layer.Vector",G);G.prototype.getSource=G.prototype.la;G.prototype.getStyle=G.prototype.D;
+G.prototype.getStyleFunction=G.prototype.L;G.prototype.setStyle=G.prototype.l;t("ol.layer.VectorTile",H);H.prototype.getPreload=H.prototype.f;H.prototype.getUseInterimTilesOnError=H.prototype.c;H.prototype.setPreload=H.prototype.P;H.prototype.setUseInterimTilesOnError=H.prototype.U;t("ol.interaction.DoubleClickZoom",qg);t("ol.interaction.DoubleClickZoom.handleEvent",rg);t("ol.interaction.DragAndDrop",nv);t("ol.interaction.DragAndDrop.handleEvent",mc);qv.prototype.features=qv.prototype.features;
+qv.prototype.file=qv.prototype.file;qv.prototype.projection=qv.prototype.projection;t("ol.interaction.DragBox",Pg);Pg.prototype.getGeometry=Pg.prototype.V;Ug.prototype.coordinate=Ug.prototype.coordinate;Ug.prototype.mapBrowserEvent=Ug.prototype.mapBrowserEvent;t("ol.interaction.DragPan",Eg);t("ol.interaction.DragRotate",Ig);t("ol.interaction.DragRotateAndZoom",sv);t("ol.interaction.DragZoom",Yg);t("ol.interaction.Draw",Iv);t("ol.interaction.Draw.handleEvent",Kv);Iv.prototype.removeLastPoint=Iv.prototype.Vo;
+Iv.prototype.finishDrawing=Iv.prototype.zd;Iv.prototype.extend=Iv.prototype.vm;t("ol.interaction.Draw.createRegularPolygon",function(a,b){return function(c,d){var e=c[0],f=c[1],g=Math.sqrt(yb(e,f)),h=d?d:Dd(new bv(e),a);Ed(h,e,g,b?b:Math.atan((f[1]-e[1])/(f[0]-e[0])));return h}});t("ol.interaction.Draw.createBox",function(){return function(a,b){var c=Gb(a),d=b||new E(null);d.qa([[Yb(c),Zb(c),$b(c),ac(c),Yb(c)]]);return d}});Xv.prototype.feature=Xv.prototype.feature;t("ol.interaction.Extent",aw);
+aw.prototype.getExtent=aw.prototype.G;aw.prototype.setExtent=aw.prototype.i;lw.prototype.extent_=lw.prototype.b;t("ol.interaction.defaults",ph);t("ol.interaction.Interaction",lg);lg.prototype.getActive=lg.prototype.f;lg.prototype.getMap=lg.prototype.c;lg.prototype.setActive=lg.prototype.Ea;t("ol.interaction.KeyboardPan",Zg);t("ol.interaction.KeyboardPan.handleEvent",$g);t("ol.interaction.KeyboardZoom",ah);t("ol.interaction.KeyboardZoom.handleEvent",bh);t("ol.interaction.Modify",nw);
+t("ol.interaction.Modify.handleEvent",qw);nw.prototype.removePoint=nw.prototype.ki;vw.prototype.features=vw.prototype.features;vw.prototype.mapBrowserEvent=vw.prototype.mapBrowserEvent;t("ol.interaction.MouseWheelZoom",ch);t("ol.interaction.MouseWheelZoom.handleEvent",dh);ch.prototype.setMouseAnchor=ch.prototype.P;t("ol.interaction.PinchRotate",gh);t("ol.interaction.PinchZoom",kh);t("ol.interaction.Pointer",Bg);t("ol.interaction.Pointer.handleEvent",Cg);t("ol.interaction.Select",Dw);
+Dw.prototype.getFeatures=Dw.prototype.Fm;Dw.prototype.getHitTolerance=Dw.prototype.Gm;Dw.prototype.getLayer=Dw.prototype.Hm;t("ol.interaction.Select.handleEvent",Ew);Dw.prototype.setHitTolerance=Dw.prototype.Jm;Dw.prototype.setMap=Dw.prototype.setMap;Gw.prototype.selected=Gw.prototype.selected;Gw.prototype.deselected=Gw.prototype.deselected;Gw.prototype.mapBrowserEvent=Gw.prototype.mapBrowserEvent;t("ol.interaction.Snap",Iw);Iw.prototype.addFeature=Iw.prototype.gb;Iw.prototype.removeFeature=Iw.prototype.rb;
+t("ol.interaction.Translate",Mw);Mw.prototype.getHitTolerance=Mw.prototype.D;Mw.prototype.setHitTolerance=Mw.prototype.L;Sw.prototype.features=Sw.prototype.features;Sw.prototype.coordinate=Sw.prototype.coordinate;t("ol.geom.Circle",bv);bv.prototype.clone=bv.prototype.clone;bv.prototype.getCenter=bv.prototype.Fd;bv.prototype.getRadius=bv.prototype.qe;bv.prototype.getType=bv.prototype.Y;bv.prototype.intersectsExtent=bv.prototype.Ta;bv.prototype.setCenter=bv.prototype.nm;
+bv.prototype.setCenterAndRadius=bv.prototype.dg;bv.prototype.setRadius=bv.prototype.om;bv.prototype.transform=bv.prototype.ob;t("ol.geom.Geometry",Rc);Rc.prototype.getClosestPoint=Rc.prototype.Cb;Rc.prototype.intersectsCoordinate=Rc.prototype.mb;Rc.prototype.getExtent=Rc.prototype.G;Rc.prototype.rotate=Rc.prototype.rotate;Rc.prototype.scale=Rc.prototype.scale;Rc.prototype.simplify=Rc.prototype.Jb;Rc.prototype.transform=Rc.prototype.ob;t("ol.geom.GeometryCollection",Vo);Vo.prototype.clone=Vo.prototype.clone;
+Vo.prototype.getGeometries=Vo.prototype.pf;Vo.prototype.getType=Vo.prototype.Y;Vo.prototype.intersectsExtent=Vo.prototype.Ta;Vo.prototype.setGeometries=Vo.prototype.ti;Vo.prototype.applyTransform=Vo.prototype.sc;Vo.prototype.translate=Vo.prototype.translate;t("ol.geom.LinearRing",md);md.prototype.clone=md.prototype.clone;md.prototype.getArea=md.prototype.rm;md.prototype.getCoordinates=md.prototype.$;md.prototype.getType=md.prototype.Y;md.prototype.setCoordinates=md.prototype.qa;
+t("ol.geom.LineString",P);P.prototype.appendCoordinate=P.prototype.Dj;P.prototype.clone=P.prototype.clone;P.prototype.forEachSegment=P.prototype.Sj;P.prototype.getCoordinateAtM=P.prototype.pm;P.prototype.getCoordinates=P.prototype.$;P.prototype.getCoordinateAt=P.prototype.Kg;P.prototype.getLength=P.prototype.qm;P.prototype.getType=P.prototype.Y;P.prototype.intersectsExtent=P.prototype.Ta;P.prototype.setCoordinates=P.prototype.qa;t("ol.geom.MultiLineString",Q);Q.prototype.appendLineString=Q.prototype.Ej;
+Q.prototype.clone=Q.prototype.clone;Q.prototype.getCoordinateAtM=Q.prototype.sm;Q.prototype.getCoordinates=Q.prototype.$;Q.prototype.getLineString=Q.prototype.mk;Q.prototype.getLineStrings=Q.prototype.Yc;Q.prototype.getType=Q.prototype.Y;Q.prototype.intersectsExtent=Q.prototype.Ta;Q.prototype.setCoordinates=Q.prototype.qa;t("ol.geom.MultiPoint",R);R.prototype.appendPoint=R.prototype.Gj;R.prototype.clone=R.prototype.clone;R.prototype.getCoordinates=R.prototype.$;R.prototype.getPoint=R.prototype.yk;
+R.prototype.getPoints=R.prototype.re;R.prototype.getType=R.prototype.Y;R.prototype.intersectsExtent=R.prototype.Ta;R.prototype.setCoordinates=R.prototype.qa;t("ol.geom.MultiPolygon",S);S.prototype.appendPolygon=S.prototype.Hj;S.prototype.clone=S.prototype.clone;S.prototype.getArea=S.prototype.tm;S.prototype.getCoordinates=S.prototype.$;S.prototype.getInteriorPoints=S.prototype.jk;S.prototype.getPolygon=S.prototype.zk;S.prototype.getPolygons=S.prototype.Ad;S.prototype.getType=S.prototype.Y;
+S.prototype.intersectsExtent=S.prototype.Ta;S.prototype.setCoordinates=S.prototype.qa;t("ol.geom.Point",C);C.prototype.clone=C.prototype.clone;C.prototype.getCoordinates=C.prototype.$;C.prototype.getType=C.prototype.Y;C.prototype.intersectsExtent=C.prototype.Ta;C.prototype.setCoordinates=C.prototype.qa;t("ol.geom.Polygon",E);E.prototype.appendLinearRing=E.prototype.Fj;E.prototype.clone=E.prototype.clone;E.prototype.getArea=E.prototype.um;E.prototype.getCoordinates=E.prototype.$;
+E.prototype.getInteriorPoint=E.prototype.ik;E.prototype.getLinearRingCount=E.prototype.nk;E.prototype.getLinearRing=E.prototype.Qg;E.prototype.getLinearRings=E.prototype.Zc;E.prototype.getType=E.prototype.Y;E.prototype.intersectsExtent=E.prototype.Ta;E.prototype.setCoordinates=E.prototype.qa;t("ol.geom.Polygon.circular",Bd);t("ol.geom.Polygon.fromExtent",Cd);t("ol.geom.Polygon.fromCircle",Dd);t("ol.geom.SimpleGeometry",Uc);Uc.prototype.getFirstCoordinate=Uc.prototype.Rb;
+Uc.prototype.getLastCoordinate=Uc.prototype.Sb;Uc.prototype.getLayout=Uc.prototype.Tb;Uc.prototype.applyTransform=Uc.prototype.sc;Uc.prototype.translate=Uc.prototype.translate;t("ol.format.EsriJSON",po);po.prototype.readFeature=po.prototype.bc;po.prototype.readFeatures=po.prototype.La;po.prototype.readGeometry=po.prototype.hd;po.prototype.readProjection=po.prototype.Wa;po.prototype.writeGeometry=po.prototype.od;po.prototype.writeGeometryObject=po.prototype.Te;po.prototype.writeFeature=po.prototype.Od;
+po.prototype.writeFeatureObject=po.prototype.md;po.prototype.writeFeatures=po.prototype.ec;po.prototype.writeFeaturesObject=po.prototype.Se;t("ol.format.Feature",co);t("ol.format.GeoJSON",Zo);Zo.prototype.readFeature=Zo.prototype.bc;Zo.prototype.readFeatures=Zo.prototype.La;Zo.prototype.readGeometry=Zo.prototype.hd;Zo.prototype.readProjection=Zo.prototype.Wa;Zo.prototype.writeFeature=Zo.prototype.Od;Zo.prototype.writeFeatureObject=Zo.prototype.md;Zo.prototype.writeFeatures=Zo.prototype.ec;
+Zo.prototype.writeFeaturesObject=Zo.prototype.Se;Zo.prototype.writeGeometry=Zo.prototype.od;Zo.prototype.writeGeometryObject=Zo.prototype.Te;t("ol.format.GML",tp);tp.prototype.writeFeatures=tp.prototype.ec;tp.prototype.writeFeaturesNode=tp.prototype.a;t("ol.format.GML2",Cp);t("ol.format.GML3",tp);tp.prototype.writeGeometryNode=tp.prototype.H;tp.prototype.writeFeatures=tp.prototype.ec;tp.prototype.writeFeaturesNode=tp.prototype.a;gp.prototype.readFeatures=gp.prototype.La;t("ol.format.GPX",Dp);
+Dp.prototype.readFeature=Dp.prototype.bc;Dp.prototype.readFeatures=Dp.prototype.La;Dp.prototype.readProjection=Dp.prototype.Wa;Dp.prototype.writeFeatures=Dp.prototype.ec;Dp.prototype.writeFeaturesNode=Dp.prototype.a;t("ol.format.IGC",nq);nq.prototype.readFeature=nq.prototype.bc;nq.prototype.readFeatures=nq.prototype.La;nq.prototype.readProjection=nq.prototype.Wa;t("ol.format.KML",Eq);Eq.prototype.readFeature=Eq.prototype.bc;Eq.prototype.readFeatures=Eq.prototype.La;Eq.prototype.readName=Eq.prototype.Jo;
+Eq.prototype.readNetworkLinks=Eq.prototype.Ko;Eq.prototype.readRegion=Eq.prototype.No;Eq.prototype.readRegionFromNode=Eq.prototype.Ie;Eq.prototype.readProjection=Eq.prototype.Wa;Eq.prototype.writeFeatures=Eq.prototype.ec;Eq.prototype.writeFeaturesNode=Eq.prototype.a;t("ol.format.MVT",Js);Js.prototype.readFeatures=Js.prototype.La;Js.prototype.readProjection=Js.prototype.Wa;Js.prototype.setLayers=Js.prototype.c;t("ol.format.OSMXML",Ls);Ls.prototype.readFeatures=Ls.prototype.La;
+Ls.prototype.readProjection=Ls.prototype.Wa;t("ol.format.Polyline",jt);t("ol.format.Polyline.encodeDeltas",kt);t("ol.format.Polyline.decodeDeltas",mt);t("ol.format.Polyline.encodeFloats",lt);t("ol.format.Polyline.decodeFloats",nt);jt.prototype.readFeature=jt.prototype.bc;jt.prototype.readFeatures=jt.prototype.La;jt.prototype.readGeometry=jt.prototype.hd;jt.prototype.readProjection=jt.prototype.Wa;jt.prototype.writeGeometry=jt.prototype.od;t("ol.format.TopoJSON",ot);ot.prototype.readFeatures=ot.prototype.La;
+ot.prototype.readProjection=ot.prototype.Wa;t("ol.format.WFS",ut);ut.prototype.readFeatures=ut.prototype.La;ut.prototype.readTransactionResponse=ut.prototype.o;ut.prototype.readFeatureCollectionMetadata=ut.prototype.l;ut.prototype.writeGetFeature=ut.prototype.v;ut.prototype.writeTransaction=ut.prototype.A;ut.prototype.readProjection=ut.prototype.Wa;t("ol.format.WKT",Mt);Mt.prototype.readFeature=Mt.prototype.bc;Mt.prototype.readFeatures=Mt.prototype.La;Mt.prototype.readGeometry=Mt.prototype.hd;
+Mt.prototype.writeFeature=Mt.prototype.Od;Mt.prototype.writeFeatures=Mt.prototype.ec;Mt.prototype.writeGeometry=Mt.prototype.od;t("ol.format.WMSCapabilities",eu);eu.prototype.read=eu.prototype.read;t("ol.format.WMSGetFeatureInfo",Bu);Bu.prototype.readFeatures=Bu.prototype.La;t("ol.format.WMTSCapabilities",Cu);Cu.prototype.read=Cu.prototype.read;t("ol.format.filter.And",Bo);t("ol.format.filter.Bbox",Co);t("ol.format.filter.Comparison",Do);t("ol.format.filter.ComparisonBinary",Eo);
+t("ol.format.filter.EqualTo",Fo);t("ol.format.filter.Filter",wo);t("ol.format.filter.GreaterThan",Go);t("ol.format.filter.GreaterThanOrEqualTo",Ho);t("ol.format.filter.and",To);t("ol.format.filter.or",function(a,b){return new Ro(a,b)});t("ol.format.filter.not",function(a){return new Po(a)});t("ol.format.filter.bbox",Uo);t("ol.format.filter.intersects",function(a,b,c){return new Jo(a,b,c)});t("ol.format.filter.within",function(a,b,c){return new So(a,b,c)});
+t("ol.format.filter.equalTo",function(a,b,c){return new Fo(a,b,c)});t("ol.format.filter.notEqualTo",function(a,b,c){return new Qo(a,b,c)});t("ol.format.filter.lessThan",function(a,b){return new No(a,b)});t("ol.format.filter.lessThanOrEqualTo",function(a,b){return new Oo(a,b)});t("ol.format.filter.greaterThan",function(a,b){return new Go(a,b)});t("ol.format.filter.greaterThanOrEqualTo",function(a,b){return new Ho(a,b)});t("ol.format.filter.isNull",function(a){return new Mo(a)});
+t("ol.format.filter.between",function(a,b,c){return new Ko(a,b,c)});t("ol.format.filter.like",function(a,b,c,d,e,f){return new Lo(a,b,c,d,e,f)});t("ol.format.filter.Intersects",Jo);t("ol.format.filter.IsBetween",Ko);t("ol.format.filter.IsLike",Lo);t("ol.format.filter.IsNull",Mo);t("ol.format.filter.LessThan",No);t("ol.format.filter.LessThanOrEqualTo",Oo);t("ol.format.filter.Not",Po);t("ol.format.filter.NotEqualTo",Qo);t("ol.format.filter.Or",Ro);t("ol.format.filter.Spatial",Io);
+t("ol.format.filter.Within",So);t("ol.extent.boundingExtent",Gb);t("ol.extent.buffer",Jb);t("ol.extent.containsCoordinate",Mb);t("ol.extent.containsExtent",Ob);t("ol.extent.containsXY",Nb);t("ol.extent.createEmpty",Hb);t("ol.extent.equals",Vb);t("ol.extent.extend",Wb);t("ol.extent.getBottomLeft",Yb);t("ol.extent.getBottomRight",Zb);t("ol.extent.getCenter",gc);t("ol.extent.getHeight",ec);t("ol.extent.getIntersection",ic);t("ol.extent.getSize",function(a){return[a[2]-a[0],a[3]-a[1]]});
+t("ol.extent.getTopLeft",ac);t("ol.extent.getTopRight",$b);t("ol.extent.getWidth",dc);t("ol.extent.intersects",jc);t("ol.extent.isEmpty",cc);t("ol.extent.applyTransform",lc);t("ol.events.condition.altKeyOnly",function(a){a=a.originalEvent;return a.altKey&&!(a.metaKey||a.ctrlKey)&&!a.shiftKey});t("ol.events.condition.altShiftKeysOnly",sg);t("ol.events.condition.always",mc);t("ol.events.condition.click",function(a){return"click"==a.type});t("ol.events.condition.never",nc);
+t("ol.events.condition.pointerMove",ug);t("ol.events.condition.singleClick",vg);t("ol.events.condition.doubleClick",function(a){return"dblclick"==a.type});t("ol.events.condition.noModifierKeys",wg);t("ol.events.condition.platformModifierKeyOnly",function(a){a=a.originalEvent;return!a.altKey&&(lf?a.metaKey:a.ctrlKey)&&!a.shiftKey});t("ol.events.condition.shiftKeyOnly",xg);t("ol.events.condition.targetNotEditable",yg);t("ol.events.condition.mouseOnly",zg);t("ol.events.condition.primaryAction",Ag);
+Ia.prototype.type=Ia.prototype.type;Ia.prototype.target=Ia.prototype.target;Ia.prototype.preventDefault=Ia.prototype.preventDefault;Ia.prototype.stopPropagation=Ia.prototype.stopPropagation;t("ol.control.Attribution",Je);t("ol.control.Attribution.render",Ke);Je.prototype.getCollapsible=Je.prototype.dm;Je.prototype.setCollapsible=Je.prototype.gm;Je.prototype.setCollapsed=Je.prototype.fm;Je.prototype.getCollapsed=Je.prototype.cm;t("ol.control.Control",Ie);Ie.prototype.getMap=Ie.prototype.i;
+Ie.prototype.setMap=Ie.prototype.setMap;Ie.prototype.setTarget=Ie.prototype.c;t("ol.control.FullScreen",Me);t("ol.control.defaults",Ue);t("ol.control.MousePosition",Ve);t("ol.control.MousePosition.render",We);Ve.prototype.getCoordinateFormat=Ve.prototype.Lg;Ve.prototype.getProjection=Ve.prototype.rh;Ve.prototype.setCoordinateFormat=Ve.prototype.oi;Ve.prototype.setProjection=Ve.prototype.sh;t("ol.control.OverviewMap",gn);t("ol.control.OverviewMap.render",hn);gn.prototype.getCollapsible=gn.prototype.jm;
+gn.prototype.setCollapsible=gn.prototype.mm;gn.prototype.setCollapsed=gn.prototype.lm;gn.prototype.getCollapsed=gn.prototype.im;gn.prototype.getOverviewMap=gn.prototype.wk;t("ol.control.Rotate",Re);t("ol.control.Rotate.render",Se);t("ol.control.ScaleLine",mn);mn.prototype.getUnits=mn.prototype.Eb;t("ol.control.ScaleLine.render",nn);mn.prototype.setUnits=mn.prototype.D;t("ol.control.Zoom",Te);t("ol.control.ZoomSlider",wn);t("ol.control.ZoomSlider.render",yn);t("ol.control.ZoomToExtent",Bn);
+Sa.prototype.changed=Sa.prototype.s;Sa.prototype.dispatchEvent=Sa.prototype.b;Sa.prototype.getRevision=Sa.prototype.M;Sa.prototype.on=Sa.prototype.J;Sa.prototype.once=Sa.prototype.N;Sa.prototype.un=Sa.prototype.K;Sa.prototype.unByKey=Sa.prototype.O;qe.prototype.get=qe.prototype.get;qe.prototype.getKeys=qe.prototype.S;qe.prototype.getProperties=qe.prototype.R;qe.prototype.set=qe.prototype.set;qe.prototype.setProperties=qe.prototype.I;qe.prototype.unset=qe.prototype.T;qe.prototype.changed=qe.prototype.s;
+qe.prototype.dispatchEvent=qe.prototype.b;qe.prototype.getRevision=qe.prototype.M;qe.prototype.on=qe.prototype.J;qe.prototype.once=qe.prototype.N;qe.prototype.un=qe.prototype.K;qe.prototype.unByKey=qe.prototype.O;te.prototype.type=te.prototype.type;te.prototype.target=te.prototype.target;te.prototype.preventDefault=te.prototype.preventDefault;te.prototype.stopPropagation=te.prototype.stopPropagation;Cn.prototype.get=Cn.prototype.get;Cn.prototype.getKeys=Cn.prototype.S;Cn.prototype.getProperties=Cn.prototype.R;
+Cn.prototype.set=Cn.prototype.set;Cn.prototype.setProperties=Cn.prototype.I;Cn.prototype.unset=Cn.prototype.T;Cn.prototype.changed=Cn.prototype.s;Cn.prototype.dispatchEvent=Cn.prototype.b;Cn.prototype.getRevision=Cn.prototype.M;Cn.prototype.on=Cn.prototype.J;Cn.prototype.once=Cn.prototype.N;Cn.prototype.un=Cn.prototype.K;Cn.prototype.unByKey=Cn.prototype.O;J.prototype.get=J.prototype.get;J.prototype.getKeys=J.prototype.S;J.prototype.getProperties=J.prototype.R;J.prototype.set=J.prototype.set;
+J.prototype.setProperties=J.prototype.I;J.prototype.unset=J.prototype.T;J.prototype.changed=J.prototype.s;J.prototype.dispatchEvent=J.prototype.b;J.prototype.getRevision=J.prototype.M;J.prototype.on=J.prototype.J;J.prototype.once=J.prototype.N;J.prototype.un=J.prototype.K;J.prototype.unByKey=J.prototype.O;Ru.prototype.get=Ru.prototype.get;Ru.prototype.getKeys=Ru.prototype.S;Ru.prototype.getProperties=Ru.prototype.R;Ru.prototype.set=Ru.prototype.set;Ru.prototype.setProperties=Ru.prototype.I;
+Ru.prototype.unset=Ru.prototype.T;Ru.prototype.changed=Ru.prototype.s;Ru.prototype.dispatchEvent=Ru.prototype.b;Ru.prototype.getRevision=Ru.prototype.M;Ru.prototype.on=Ru.prototype.J;Ru.prototype.once=Ru.prototype.N;Ru.prototype.un=Ru.prototype.K;Ru.prototype.unByKey=Ru.prototype.O;lv.prototype.getTileCoord=lv.prototype.c;I.prototype.get=I.prototype.get;I.prototype.getKeys=I.prototype.S;I.prototype.getProperties=I.prototype.R;I.prototype.set=I.prototype.set;I.prototype.setProperties=I.prototype.I;
+I.prototype.unset=I.prototype.T;I.prototype.changed=I.prototype.s;I.prototype.dispatchEvent=I.prototype.b;I.prototype.getRevision=I.prototype.M;I.prototype.on=I.prototype.J;I.prototype.once=I.prototype.N;I.prototype.un=I.prototype.K;I.prototype.unByKey=I.prototype.O;Ge.prototype.type=Ge.prototype.type;Ge.prototype.target=Ge.prototype.target;Ge.prototype.preventDefault=Ge.prototype.preventDefault;Ge.prototype.stopPropagation=Ge.prototype.stopPropagation;$e.prototype.map=$e.prototype.map;
+$e.prototype.frameState=$e.prototype.frameState;$e.prototype.type=$e.prototype.type;$e.prototype.target=$e.prototype.target;$e.prototype.preventDefault=$e.prototype.preventDefault;$e.prototype.stopPropagation=$e.prototype.stopPropagation;cf.prototype.originalEvent=cf.prototype.originalEvent;cf.prototype.pixel=cf.prototype.pixel;cf.prototype.coordinate=cf.prototype.coordinate;cf.prototype.dragging=cf.prototype.dragging;cf.prototype.preventDefault=cf.prototype.preventDefault;
+cf.prototype.stopPropagation=cf.prototype.stopPropagation;cf.prototype.map=cf.prototype.map;cf.prototype.frameState=cf.prototype.frameState;cf.prototype.type=cf.prototype.type;cf.prototype.target=cf.prototype.target;Wa.prototype.type=Wa.prototype.type;Wa.prototype.target=Wa.prototype.target;Wa.prototype.preventDefault=Wa.prototype.preventDefault;Wa.prototype.stopPropagation=Wa.prototype.stopPropagation;Pm.prototype.get=Pm.prototype.get;Pm.prototype.getKeys=Pm.prototype.S;
+Pm.prototype.getProperties=Pm.prototype.R;Pm.prototype.set=Pm.prototype.set;Pm.prototype.setProperties=Pm.prototype.I;Pm.prototype.unset=Pm.prototype.T;Pm.prototype.changed=Pm.prototype.s;Pm.prototype.dispatchEvent=Pm.prototype.b;Pm.prototype.getRevision=Pm.prototype.M;Pm.prototype.on=Pm.prototype.J;Pm.prototype.once=Pm.prototype.N;Pm.prototype.un=Pm.prototype.K;Pm.prototype.unByKey=Pm.prototype.O;jy.prototype.getTileCoord=jy.prototype.c;Fd.prototype.get=Fd.prototype.get;Fd.prototype.getKeys=Fd.prototype.S;
+Fd.prototype.getProperties=Fd.prototype.R;Fd.prototype.set=Fd.prototype.set;Fd.prototype.setProperties=Fd.prototype.I;Fd.prototype.unset=Fd.prototype.T;Fd.prototype.changed=Fd.prototype.s;Fd.prototype.dispatchEvent=Fd.prototype.b;Fd.prototype.getRevision=Fd.prototype.M;Fd.prototype.on=Fd.prototype.J;Fd.prototype.once=Fd.prototype.N;Fd.prototype.un=Fd.prototype.K;Fd.prototype.unByKey=Fd.prototype.O;my.prototype.forEachTileCoord=my.prototype.Hg;my.prototype.getMaxZoom=my.prototype.Rg;
+my.prototype.getMinZoom=my.prototype.Sg;my.prototype.getOrigin=my.prototype.Kc;my.prototype.getResolution=my.prototype.Ha;my.prototype.getResolutions=my.prototype.Wh;my.prototype.getTileCoordExtent=my.prototype.Na;my.prototype.getTileCoordForCoordAndResolution=my.prototype.fe;my.prototype.getTileCoordForCoordAndZ=my.prototype.wf;my.prototype.getTileSize=my.prototype.Za;my.prototype.getZForResolution=my.prototype.Ec;si.prototype.getOpacity=si.prototype.ye;si.prototype.getRotateWithView=si.prototype.ze;
+si.prototype.getRotation=si.prototype.Ae;si.prototype.getScale=si.prototype.Be;si.prototype.getSnapToPixel=si.prototype.ee;si.prototype.setOpacity=si.prototype.dd;si.prototype.setRotation=si.prototype.Ce;si.prototype.setScale=si.prototype.ed;ui.prototype.getAngle=ui.prototype.Nh;ui.prototype.getFill=ui.prototype.Oh;ui.prototype.getPoints=ui.prototype.Ph;ui.prototype.getRadius=ui.prototype.Qh;ui.prototype.getRadius2=ui.prototype.Vg;ui.prototype.getStroke=ui.prototype.Rh;ui.prototype.getOpacity=ui.prototype.ye;
+ui.prototype.getRotateWithView=ui.prototype.ze;ui.prototype.getRotation=ui.prototype.Ae;ui.prototype.getScale=ui.prototype.Be;ui.prototype.getSnapToPixel=ui.prototype.ee;ui.prototype.setOpacity=ui.prototype.dd;ui.prototype.setRotation=ui.prototype.Ce;ui.prototype.setScale=ui.prototype.ed;xq.prototype.getOpacity=xq.prototype.ye;xq.prototype.getRotateWithView=xq.prototype.ze;xq.prototype.getRotation=xq.prototype.Ae;xq.prototype.getScale=xq.prototype.Be;xq.prototype.getSnapToPixel=xq.prototype.ee;
+xq.prototype.setOpacity=xq.prototype.dd;xq.prototype.setRotation=xq.prototype.Ce;xq.prototype.setScale=xq.prototype.ed;hm.prototype.get=hm.prototype.get;hm.prototype.getKeys=hm.prototype.S;hm.prototype.getProperties=hm.prototype.R;hm.prototype.set=hm.prototype.set;hm.prototype.setProperties=hm.prototype.I;hm.prototype.unset=hm.prototype.T;hm.prototype.changed=hm.prototype.s;hm.prototype.dispatchEvent=hm.prototype.b;hm.prototype.getRevision=hm.prototype.M;hm.prototype.on=hm.prototype.J;
+hm.prototype.once=hm.prototype.N;hm.prototype.un=hm.prototype.K;hm.prototype.unByKey=hm.prototype.O;ix.prototype.getAttributions=ix.prototype.za;ix.prototype.getLogo=ix.prototype.ya;ix.prototype.getProjection=ix.prototype.Aa;ix.prototype.getState=ix.prototype.W;ix.prototype.refresh=ix.prototype.wa;ix.prototype.setAttributions=ix.prototype.ua;ix.prototype.get=ix.prototype.get;ix.prototype.getKeys=ix.prototype.S;ix.prototype.getProperties=ix.prototype.R;ix.prototype.set=ix.prototype.set;
+ix.prototype.setProperties=ix.prototype.I;ix.prototype.unset=ix.prototype.T;ix.prototype.changed=ix.prototype.s;ix.prototype.dispatchEvent=ix.prototype.b;ix.prototype.getRevision=ix.prototype.M;ix.prototype.on=ix.prototype.J;ix.prototype.once=ix.prototype.N;ix.prototype.un=ix.prototype.K;ix.prototype.unByKey=ix.prototype.O;mx.prototype.getTileGrid=mx.prototype.Va;mx.prototype.refresh=mx.prototype.wa;mx.prototype.getAttributions=mx.prototype.za;mx.prototype.getLogo=mx.prototype.ya;
+mx.prototype.getProjection=mx.prototype.Aa;mx.prototype.getState=mx.prototype.W;mx.prototype.setAttributions=mx.prototype.ua;mx.prototype.get=mx.prototype.get;mx.prototype.getKeys=mx.prototype.S;mx.prototype.getProperties=mx.prototype.R;mx.prototype.set=mx.prototype.set;mx.prototype.setProperties=mx.prototype.I;mx.prototype.unset=mx.prototype.T;mx.prototype.changed=mx.prototype.s;mx.prototype.dispatchEvent=mx.prototype.b;mx.prototype.getRevision=mx.prototype.M;mx.prototype.on=mx.prototype.J;
+mx.prototype.once=mx.prototype.N;mx.prototype.un=mx.prototype.K;mx.prototype.unByKey=mx.prototype.O;X.prototype.getTileLoadFunction=X.prototype.ib;X.prototype.getTileUrlFunction=X.prototype.kb;X.prototype.getUrls=X.prototype.lb;X.prototype.setTileLoadFunction=X.prototype.sb;X.prototype.setTileUrlFunction=X.prototype.Xa;X.prototype.setUrl=X.prototype.cb;X.prototype.setUrls=X.prototype.Ya;X.prototype.getTileGrid=X.prototype.Va;X.prototype.refresh=X.prototype.wa;X.prototype.getAttributions=X.prototype.za;
+X.prototype.getLogo=X.prototype.ya;X.prototype.getProjection=X.prototype.Aa;X.prototype.getState=X.prototype.W;X.prototype.setAttributions=X.prototype.ua;X.prototype.get=X.prototype.get;X.prototype.getKeys=X.prototype.S;X.prototype.getProperties=X.prototype.R;X.prototype.set=X.prototype.set;X.prototype.setProperties=X.prototype.I;X.prototype.unset=X.prototype.T;X.prototype.changed=X.prototype.s;X.prototype.dispatchEvent=X.prototype.b;X.prototype.getRevision=X.prototype.M;X.prototype.on=X.prototype.J;
+X.prototype.once=X.prototype.N;X.prototype.un=X.prototype.K;X.prototype.unByKey=X.prototype.O;qx.prototype.setRenderReprojectionEdges=qx.prototype.Hb;qx.prototype.setTileGridForProjection=qx.prototype.Ib;qx.prototype.getTileLoadFunction=qx.prototype.ib;qx.prototype.getTileUrlFunction=qx.prototype.kb;qx.prototype.getUrls=qx.prototype.lb;qx.prototype.setTileLoadFunction=qx.prototype.sb;qx.prototype.setTileUrlFunction=qx.prototype.Xa;qx.prototype.setUrl=qx.prototype.cb;qx.prototype.setUrls=qx.prototype.Ya;
+qx.prototype.getTileGrid=qx.prototype.Va;qx.prototype.refresh=qx.prototype.wa;qx.prototype.getAttributions=qx.prototype.za;qx.prototype.getLogo=qx.prototype.ya;qx.prototype.getProjection=qx.prototype.Aa;qx.prototype.getState=qx.prototype.W;qx.prototype.setAttributions=qx.prototype.ua;qx.prototype.get=qx.prototype.get;qx.prototype.getKeys=qx.prototype.S;qx.prototype.getProperties=qx.prototype.R;qx.prototype.set=qx.prototype.set;qx.prototype.setProperties=qx.prototype.I;qx.prototype.unset=qx.prototype.T;
+qx.prototype.changed=qx.prototype.s;qx.prototype.dispatchEvent=qx.prototype.b;qx.prototype.getRevision=qx.prototype.M;qx.prototype.on=qx.prototype.J;qx.prototype.once=qx.prototype.N;qx.prototype.un=qx.prototype.K;qx.prototype.unByKey=qx.prototype.O;sx.prototype.setRenderReprojectionEdges=sx.prototype.Hb;sx.prototype.setTileGridForProjection=sx.prototype.Ib;sx.prototype.getTileLoadFunction=sx.prototype.ib;sx.prototype.getTileUrlFunction=sx.prototype.kb;sx.prototype.getUrls=sx.prototype.lb;
+sx.prototype.setTileLoadFunction=sx.prototype.sb;sx.prototype.setTileUrlFunction=sx.prototype.Xa;sx.prototype.setUrl=sx.prototype.cb;sx.prototype.setUrls=sx.prototype.Ya;sx.prototype.getTileGrid=sx.prototype.Va;sx.prototype.refresh=sx.prototype.wa;sx.prototype.getAttributions=sx.prototype.za;sx.prototype.getLogo=sx.prototype.ya;sx.prototype.getProjection=sx.prototype.Aa;sx.prototype.getState=sx.prototype.W;sx.prototype.setAttributions=sx.prototype.ua;sx.prototype.get=sx.prototype.get;
+sx.prototype.getKeys=sx.prototype.S;sx.prototype.getProperties=sx.prototype.R;sx.prototype.set=sx.prototype.set;sx.prototype.setProperties=sx.prototype.I;sx.prototype.unset=sx.prototype.T;sx.prototype.changed=sx.prototype.s;sx.prototype.dispatchEvent=sx.prototype.b;sx.prototype.getRevision=sx.prototype.M;sx.prototype.on=sx.prototype.J;sx.prototype.once=sx.prototype.N;sx.prototype.un=sx.prototype.K;sx.prototype.unByKey=sx.prototype.O;tx.prototype.setRenderReprojectionEdges=tx.prototype.Hb;
+tx.prototype.setTileGridForProjection=tx.prototype.Ib;tx.prototype.getTileLoadFunction=tx.prototype.ib;tx.prototype.getTileUrlFunction=tx.prototype.kb;tx.prototype.getUrls=tx.prototype.lb;tx.prototype.setTileLoadFunction=tx.prototype.sb;tx.prototype.setTileUrlFunction=tx.prototype.Xa;tx.prototype.setUrl=tx.prototype.cb;tx.prototype.setUrls=tx.prototype.Ya;tx.prototype.getTileGrid=tx.prototype.Va;tx.prototype.refresh=tx.prototype.wa;tx.prototype.getAttributions=tx.prototype.za;
+tx.prototype.getLogo=tx.prototype.ya;tx.prototype.getProjection=tx.prototype.Aa;tx.prototype.getState=tx.prototype.W;tx.prototype.setAttributions=tx.prototype.ua;tx.prototype.get=tx.prototype.get;tx.prototype.getKeys=tx.prototype.S;tx.prototype.getProperties=tx.prototype.R;tx.prototype.set=tx.prototype.set;tx.prototype.setProperties=tx.prototype.I;tx.prototype.unset=tx.prototype.T;tx.prototype.changed=tx.prototype.s;tx.prototype.dispatchEvent=tx.prototype.b;tx.prototype.getRevision=tx.prototype.M;
+tx.prototype.on=tx.prototype.J;tx.prototype.once=tx.prototype.N;tx.prototype.un=tx.prototype.K;tx.prototype.unByKey=tx.prototype.O;U.prototype.getAttributions=U.prototype.za;U.prototype.getLogo=U.prototype.ya;U.prototype.getProjection=U.prototype.Aa;U.prototype.getState=U.prototype.W;U.prototype.refresh=U.prototype.wa;U.prototype.setAttributions=U.prototype.ua;U.prototype.get=U.prototype.get;U.prototype.getKeys=U.prototype.S;U.prototype.getProperties=U.prototype.R;U.prototype.set=U.prototype.set;
+U.prototype.setProperties=U.prototype.I;U.prototype.unset=U.prototype.T;U.prototype.changed=U.prototype.s;U.prototype.dispatchEvent=U.prototype.b;U.prototype.getRevision=U.prototype.M;U.prototype.on=U.prototype.J;U.prototype.once=U.prototype.N;U.prototype.un=U.prototype.K;U.prototype.unByKey=U.prototype.O;Y.prototype.addFeature=Y.prototype.gb;Y.prototype.addFeatures=Y.prototype.Tc;Y.prototype.clear=Y.prototype.clear;Y.prototype.forEachFeature=Y.prototype.Fg;Y.prototype.forEachFeatureInExtent=Y.prototype.Qb;
+Y.prototype.forEachFeatureIntersectingExtent=Y.prototype.Gg;Y.prototype.getFeaturesCollection=Y.prototype.Og;Y.prototype.getFeatures=Y.prototype.we;Y.prototype.getFeaturesAtCoordinate=Y.prototype.Ng;Y.prototype.getFeaturesInExtent=Y.prototype.nf;Y.prototype.getClosestFeatureToCoordinate=Y.prototype.Jg;Y.prototype.getExtent=Y.prototype.G;Y.prototype.getFeatureById=Y.prototype.Mg;Y.prototype.getFormat=Y.prototype.Kh;Y.prototype.getUrl=Y.prototype.Lh;Y.prototype.removeFeature=Y.prototype.rb;
+Y.prototype.getAttributions=Y.prototype.za;Y.prototype.getLogo=Y.prototype.ya;Y.prototype.getProjection=Y.prototype.Aa;Y.prototype.getState=Y.prototype.W;Y.prototype.refresh=Y.prototype.wa;Y.prototype.setAttributions=Y.prototype.ua;Y.prototype.get=Y.prototype.get;Y.prototype.getKeys=Y.prototype.S;Y.prototype.getProperties=Y.prototype.R;Y.prototype.set=Y.prototype.set;Y.prototype.setProperties=Y.prototype.I;Y.prototype.unset=Y.prototype.T;Y.prototype.changed=Y.prototype.s;
+Y.prototype.dispatchEvent=Y.prototype.b;Y.prototype.getRevision=Y.prototype.M;Y.prototype.on=Y.prototype.J;Y.prototype.once=Y.prototype.N;Y.prototype.un=Y.prototype.K;Y.prototype.unByKey=Y.prototype.O;km.prototype.getAttributions=km.prototype.za;km.prototype.getLogo=km.prototype.ya;km.prototype.getProjection=km.prototype.Aa;km.prototype.getState=km.prototype.W;km.prototype.refresh=km.prototype.wa;km.prototype.setAttributions=km.prototype.ua;km.prototype.get=km.prototype.get;km.prototype.getKeys=km.prototype.S;
+km.prototype.getProperties=km.prototype.R;km.prototype.set=km.prototype.set;km.prototype.setProperties=km.prototype.I;km.prototype.unset=km.prototype.T;km.prototype.changed=km.prototype.s;km.prototype.dispatchEvent=km.prototype.b;km.prototype.getRevision=km.prototype.M;km.prototype.on=km.prototype.J;km.prototype.once=km.prototype.N;km.prototype.un=km.prototype.K;km.prototype.unByKey=km.prototype.O;mm.prototype.type=mm.prototype.type;mm.prototype.target=mm.prototype.target;
+mm.prototype.preventDefault=mm.prototype.preventDefault;mm.prototype.stopPropagation=mm.prototype.stopPropagation;zx.prototype.getAttributions=zx.prototype.za;zx.prototype.getLogo=zx.prototype.ya;zx.prototype.getProjection=zx.prototype.Aa;zx.prototype.getState=zx.prototype.W;zx.prototype.refresh=zx.prototype.wa;zx.prototype.setAttributions=zx.prototype.ua;zx.prototype.get=zx.prototype.get;zx.prototype.getKeys=zx.prototype.S;zx.prototype.getProperties=zx.prototype.R;zx.prototype.set=zx.prototype.set;
+zx.prototype.setProperties=zx.prototype.I;zx.prototype.unset=zx.prototype.T;zx.prototype.changed=zx.prototype.s;zx.prototype.dispatchEvent=zx.prototype.b;zx.prototype.getRevision=zx.prototype.M;zx.prototype.on=zx.prototype.J;zx.prototype.once=zx.prototype.N;zx.prototype.un=zx.prototype.K;zx.prototype.unByKey=zx.prototype.O;rm.prototype.getAttributions=rm.prototype.za;rm.prototype.getLogo=rm.prototype.ya;rm.prototype.getProjection=rm.prototype.Aa;rm.prototype.getState=rm.prototype.W;
+rm.prototype.refresh=rm.prototype.wa;rm.prototype.setAttributions=rm.prototype.ua;rm.prototype.get=rm.prototype.get;rm.prototype.getKeys=rm.prototype.S;rm.prototype.getProperties=rm.prototype.R;rm.prototype.set=rm.prototype.set;rm.prototype.setProperties=rm.prototype.I;rm.prototype.unset=rm.prototype.T;rm.prototype.changed=rm.prototype.s;rm.prototype.dispatchEvent=rm.prototype.b;rm.prototype.getRevision=rm.prototype.M;rm.prototype.on=rm.prototype.J;rm.prototype.once=rm.prototype.N;
+rm.prototype.un=rm.prototype.K;rm.prototype.unByKey=rm.prototype.O;Ax.prototype.getAttributions=Ax.prototype.za;Ax.prototype.getLogo=Ax.prototype.ya;Ax.prototype.getProjection=Ax.prototype.Aa;Ax.prototype.getState=Ax.prototype.W;Ax.prototype.refresh=Ax.prototype.wa;Ax.prototype.setAttributions=Ax.prototype.ua;Ax.prototype.get=Ax.prototype.get;Ax.prototype.getKeys=Ax.prototype.S;Ax.prototype.getProperties=Ax.prototype.R;Ax.prototype.set=Ax.prototype.set;Ax.prototype.setProperties=Ax.prototype.I;
+Ax.prototype.unset=Ax.prototype.T;Ax.prototype.changed=Ax.prototype.s;Ax.prototype.dispatchEvent=Ax.prototype.b;Ax.prototype.getRevision=Ax.prototype.M;Ax.prototype.on=Ax.prototype.J;Ax.prototype.once=Ax.prototype.N;Ax.prototype.un=Ax.prototype.K;Ax.prototype.unByKey=Ax.prototype.O;Bx.prototype.getAttributions=Bx.prototype.za;Bx.prototype.getLogo=Bx.prototype.ya;Bx.prototype.getProjection=Bx.prototype.Aa;Bx.prototype.getState=Bx.prototype.W;Bx.prototype.refresh=Bx.prototype.wa;
+Bx.prototype.setAttributions=Bx.prototype.ua;Bx.prototype.get=Bx.prototype.get;Bx.prototype.getKeys=Bx.prototype.S;Bx.prototype.getProperties=Bx.prototype.R;Bx.prototype.set=Bx.prototype.set;Bx.prototype.setProperties=Bx.prototype.I;Bx.prototype.unset=Bx.prototype.T;Bx.prototype.changed=Bx.prototype.s;Bx.prototype.dispatchEvent=Bx.prototype.b;Bx.prototype.getRevision=Bx.prototype.M;Bx.prototype.on=Bx.prototype.J;Bx.prototype.once=Bx.prototype.N;Bx.prototype.un=Bx.prototype.K;
+Bx.prototype.unByKey=Bx.prototype.O;sm.prototype.getAttributions=sm.prototype.za;sm.prototype.getLogo=sm.prototype.ya;sm.prototype.getProjection=sm.prototype.Aa;sm.prototype.getState=sm.prototype.W;sm.prototype.refresh=sm.prototype.wa;sm.prototype.setAttributions=sm.prototype.ua;sm.prototype.get=sm.prototype.get;sm.prototype.getKeys=sm.prototype.S;sm.prototype.getProperties=sm.prototype.R;sm.prototype.set=sm.prototype.set;sm.prototype.setProperties=sm.prototype.I;sm.prototype.unset=sm.prototype.T;
+sm.prototype.changed=sm.prototype.s;sm.prototype.dispatchEvent=sm.prototype.b;sm.prototype.getRevision=sm.prototype.M;sm.prototype.on=sm.prototype.J;sm.prototype.once=sm.prototype.N;sm.prototype.un=sm.prototype.K;sm.prototype.unByKey=sm.prototype.O;Cx.prototype.getAttributions=Cx.prototype.za;Cx.prototype.getLogo=Cx.prototype.ya;Cx.prototype.getProjection=Cx.prototype.Aa;Cx.prototype.getState=Cx.prototype.W;Cx.prototype.refresh=Cx.prototype.wa;Cx.prototype.setAttributions=Cx.prototype.ua;
+Cx.prototype.get=Cx.prototype.get;Cx.prototype.getKeys=Cx.prototype.S;Cx.prototype.getProperties=Cx.prototype.R;Cx.prototype.set=Cx.prototype.set;Cx.prototype.setProperties=Cx.prototype.I;Cx.prototype.unset=Cx.prototype.T;Cx.prototype.changed=Cx.prototype.s;Cx.prototype.dispatchEvent=Cx.prototype.b;Cx.prototype.getRevision=Cx.prototype.M;Cx.prototype.on=Cx.prototype.J;Cx.prototype.once=Cx.prototype.N;Cx.prototype.un=Cx.prototype.K;Cx.prototype.unByKey=Cx.prototype.O;
+Gx.prototype.setRenderReprojectionEdges=Gx.prototype.Hb;Gx.prototype.setTileGridForProjection=Gx.prototype.Ib;Gx.prototype.getTileLoadFunction=Gx.prototype.ib;Gx.prototype.getTileUrlFunction=Gx.prototype.kb;Gx.prototype.getUrls=Gx.prototype.lb;Gx.prototype.setTileLoadFunction=Gx.prototype.sb;Gx.prototype.setTileUrlFunction=Gx.prototype.Xa;Gx.prototype.setUrl=Gx.prototype.cb;Gx.prototype.setUrls=Gx.prototype.Ya;Gx.prototype.getTileGrid=Gx.prototype.Va;Gx.prototype.refresh=Gx.prototype.wa;
+Gx.prototype.getAttributions=Gx.prototype.za;Gx.prototype.getLogo=Gx.prototype.ya;Gx.prototype.getProjection=Gx.prototype.Aa;Gx.prototype.getState=Gx.prototype.W;Gx.prototype.setAttributions=Gx.prototype.ua;Gx.prototype.get=Gx.prototype.get;Gx.prototype.getKeys=Gx.prototype.S;Gx.prototype.getProperties=Gx.prototype.R;Gx.prototype.set=Gx.prototype.set;Gx.prototype.setProperties=Gx.prototype.I;Gx.prototype.unset=Gx.prototype.T;Gx.prototype.changed=Gx.prototype.s;Gx.prototype.dispatchEvent=Gx.prototype.b;
+Gx.prototype.getRevision=Gx.prototype.M;Gx.prototype.on=Gx.prototype.J;Gx.prototype.once=Gx.prototype.N;Gx.prototype.un=Gx.prototype.K;Gx.prototype.unByKey=Gx.prototype.O;Ix.prototype.getAttributions=Ix.prototype.za;Ix.prototype.getLogo=Ix.prototype.ya;Ix.prototype.getProjection=Ix.prototype.Aa;Ix.prototype.getState=Ix.prototype.W;Ix.prototype.refresh=Ix.prototype.wa;Ix.prototype.setAttributions=Ix.prototype.ua;Ix.prototype.get=Ix.prototype.get;Ix.prototype.getKeys=Ix.prototype.S;
+Ix.prototype.getProperties=Ix.prototype.R;Ix.prototype.set=Ix.prototype.set;Ix.prototype.setProperties=Ix.prototype.I;Ix.prototype.unset=Ix.prototype.T;Ix.prototype.changed=Ix.prototype.s;Ix.prototype.dispatchEvent=Ix.prototype.b;Ix.prototype.getRevision=Ix.prototype.M;Ix.prototype.on=Ix.prototype.J;Ix.prototype.once=Ix.prototype.N;Ix.prototype.un=Ix.prototype.K;Ix.prototype.unByKey=Ix.prototype.O;Px.prototype.type=Px.prototype.type;Px.prototype.target=Px.prototype.target;
+Px.prototype.preventDefault=Px.prototype.preventDefault;Px.prototype.stopPropagation=Px.prototype.stopPropagation;Sx.prototype.setRenderReprojectionEdges=Sx.prototype.Hb;Sx.prototype.setTileGridForProjection=Sx.prototype.Ib;Sx.prototype.getTileLoadFunction=Sx.prototype.ib;Sx.prototype.getTileUrlFunction=Sx.prototype.kb;Sx.prototype.getUrls=Sx.prototype.lb;Sx.prototype.setTileLoadFunction=Sx.prototype.sb;Sx.prototype.setTileUrlFunction=Sx.prototype.Xa;Sx.prototype.setUrl=Sx.prototype.cb;
+Sx.prototype.setUrls=Sx.prototype.Ya;Sx.prototype.getTileGrid=Sx.prototype.Va;Sx.prototype.refresh=Sx.prototype.wa;Sx.prototype.getAttributions=Sx.prototype.za;Sx.prototype.getLogo=Sx.prototype.ya;Sx.prototype.getProjection=Sx.prototype.Aa;Sx.prototype.getState=Sx.prototype.W;Sx.prototype.setAttributions=Sx.prototype.ua;Sx.prototype.get=Sx.prototype.get;Sx.prototype.getKeys=Sx.prototype.S;Sx.prototype.getProperties=Sx.prototype.R;Sx.prototype.set=Sx.prototype.set;Sx.prototype.setProperties=Sx.prototype.I;
+Sx.prototype.unset=Sx.prototype.T;Sx.prototype.changed=Sx.prototype.s;Sx.prototype.dispatchEvent=Sx.prototype.b;Sx.prototype.getRevision=Sx.prototype.M;Sx.prototype.on=Sx.prototype.J;Sx.prototype.once=Sx.prototype.N;Sx.prototype.un=Sx.prototype.K;Sx.prototype.unByKey=Sx.prototype.O;lx.prototype.type=lx.prototype.type;lx.prototype.target=lx.prototype.target;lx.prototype.preventDefault=lx.prototype.preventDefault;lx.prototype.stopPropagation=lx.prototype.stopPropagation;
+Wx.prototype.setRenderReprojectionEdges=Wx.prototype.Hb;Wx.prototype.setTileGridForProjection=Wx.prototype.Ib;Wx.prototype.getTileLoadFunction=Wx.prototype.ib;Wx.prototype.getTileUrlFunction=Wx.prototype.kb;Wx.prototype.getUrls=Wx.prototype.lb;Wx.prototype.setTileLoadFunction=Wx.prototype.sb;Wx.prototype.setTileUrlFunction=Wx.prototype.Xa;Wx.prototype.setUrl=Wx.prototype.cb;Wx.prototype.setUrls=Wx.prototype.Ya;Wx.prototype.getTileGrid=Wx.prototype.Va;Wx.prototype.refresh=Wx.prototype.wa;
+Wx.prototype.getAttributions=Wx.prototype.za;Wx.prototype.getLogo=Wx.prototype.ya;Wx.prototype.getProjection=Wx.prototype.Aa;Wx.prototype.getState=Wx.prototype.W;Wx.prototype.setAttributions=Wx.prototype.ua;Wx.prototype.get=Wx.prototype.get;Wx.prototype.getKeys=Wx.prototype.S;Wx.prototype.getProperties=Wx.prototype.R;Wx.prototype.set=Wx.prototype.set;Wx.prototype.setProperties=Wx.prototype.I;Wx.prototype.unset=Wx.prototype.T;Wx.prototype.changed=Wx.prototype.s;Wx.prototype.dispatchEvent=Wx.prototype.b;
+Wx.prototype.getRevision=Wx.prototype.M;Wx.prototype.on=Wx.prototype.J;Wx.prototype.once=Wx.prototype.N;Wx.prototype.un=Wx.prototype.K;Wx.prototype.unByKey=Wx.prototype.O;Yx.prototype.getTileGrid=Yx.prototype.Va;Yx.prototype.refresh=Yx.prototype.wa;Yx.prototype.getAttributions=Yx.prototype.za;Yx.prototype.getLogo=Yx.prototype.ya;Yx.prototype.getProjection=Yx.prototype.Aa;Yx.prototype.getState=Yx.prototype.W;Yx.prototype.setAttributions=Yx.prototype.ua;Yx.prototype.get=Yx.prototype.get;
+Yx.prototype.getKeys=Yx.prototype.S;Yx.prototype.getProperties=Yx.prototype.R;Yx.prototype.set=Yx.prototype.set;Yx.prototype.setProperties=Yx.prototype.I;Yx.prototype.unset=Yx.prototype.T;Yx.prototype.changed=Yx.prototype.s;Yx.prototype.dispatchEvent=Yx.prototype.b;Yx.prototype.getRevision=Yx.prototype.M;Yx.prototype.on=Yx.prototype.J;Yx.prototype.once=Yx.prototype.N;Yx.prototype.un=Yx.prototype.K;Yx.prototype.unByKey=Yx.prototype.O;$x.prototype.setRenderReprojectionEdges=$x.prototype.Hb;
+$x.prototype.setTileGridForProjection=$x.prototype.Ib;$x.prototype.getTileLoadFunction=$x.prototype.ib;$x.prototype.getTileUrlFunction=$x.prototype.kb;$x.prototype.getUrls=$x.prototype.lb;$x.prototype.setTileLoadFunction=$x.prototype.sb;$x.prototype.setTileUrlFunction=$x.prototype.Xa;$x.prototype.setUrl=$x.prototype.cb;$x.prototype.setUrls=$x.prototype.Ya;$x.prototype.getTileGrid=$x.prototype.Va;$x.prototype.refresh=$x.prototype.wa;$x.prototype.getAttributions=$x.prototype.za;
+$x.prototype.getLogo=$x.prototype.ya;$x.prototype.getProjection=$x.prototype.Aa;$x.prototype.getState=$x.prototype.W;$x.prototype.setAttributions=$x.prototype.ua;$x.prototype.get=$x.prototype.get;$x.prototype.getKeys=$x.prototype.S;$x.prototype.getProperties=$x.prototype.R;$x.prototype.set=$x.prototype.set;$x.prototype.setProperties=$x.prototype.I;$x.prototype.unset=$x.prototype.T;$x.prototype.changed=$x.prototype.s;$x.prototype.dispatchEvent=$x.prototype.b;$x.prototype.getRevision=$x.prototype.M;
+$x.prototype.on=$x.prototype.J;$x.prototype.once=$x.prototype.N;$x.prototype.un=$x.prototype.K;$x.prototype.unByKey=$x.prototype.O;ay.prototype.getTileGrid=ay.prototype.Va;ay.prototype.refresh=ay.prototype.wa;ay.prototype.getAttributions=ay.prototype.za;ay.prototype.getLogo=ay.prototype.ya;ay.prototype.getProjection=ay.prototype.Aa;ay.prototype.getState=ay.prototype.W;ay.prototype.setAttributions=ay.prototype.ua;ay.prototype.get=ay.prototype.get;ay.prototype.getKeys=ay.prototype.S;
+ay.prototype.getProperties=ay.prototype.R;ay.prototype.set=ay.prototype.set;ay.prototype.setProperties=ay.prototype.I;ay.prototype.unset=ay.prototype.T;ay.prototype.changed=ay.prototype.s;ay.prototype.dispatchEvent=ay.prototype.b;ay.prototype.getRevision=ay.prototype.M;ay.prototype.on=ay.prototype.J;ay.prototype.once=ay.prototype.N;ay.prototype.un=ay.prototype.K;ay.prototype.unByKey=ay.prototype.O;ey.prototype.setRenderReprojectionEdges=ey.prototype.Hb;ey.prototype.setTileGridForProjection=ey.prototype.Ib;
+ey.prototype.getTileLoadFunction=ey.prototype.ib;ey.prototype.getTileUrlFunction=ey.prototype.kb;ey.prototype.getUrls=ey.prototype.lb;ey.prototype.setTileLoadFunction=ey.prototype.sb;ey.prototype.setTileUrlFunction=ey.prototype.Xa;ey.prototype.setUrl=ey.prototype.cb;ey.prototype.setUrls=ey.prototype.Ya;ey.prototype.getTileGrid=ey.prototype.Va;ey.prototype.refresh=ey.prototype.wa;ey.prototype.getAttributions=ey.prototype.za;ey.prototype.getLogo=ey.prototype.ya;ey.prototype.getProjection=ey.prototype.Aa;
+ey.prototype.getState=ey.prototype.W;ey.prototype.setAttributions=ey.prototype.ua;ey.prototype.get=ey.prototype.get;ey.prototype.getKeys=ey.prototype.S;ey.prototype.getProperties=ey.prototype.R;ey.prototype.set=ey.prototype.set;ey.prototype.setProperties=ey.prototype.I;ey.prototype.unset=ey.prototype.T;ey.prototype.changed=ey.prototype.s;ey.prototype.dispatchEvent=ey.prototype.b;ey.prototype.getRevision=ey.prototype.M;ey.prototype.on=ey.prototype.J;ey.prototype.once=ey.prototype.N;
+ey.prototype.un=ey.prototype.K;ey.prototype.unByKey=ey.prototype.O;Bv.prototype.type=Bv.prototype.type;Bv.prototype.target=Bv.prototype.target;Bv.prototype.preventDefault=Bv.prototype.preventDefault;Bv.prototype.stopPropagation=Bv.prototype.stopPropagation;ly.prototype.getTileLoadFunction=ly.prototype.ib;ly.prototype.getTileUrlFunction=ly.prototype.kb;ly.prototype.getUrls=ly.prototype.lb;ly.prototype.setTileLoadFunction=ly.prototype.sb;ly.prototype.setTileUrlFunction=ly.prototype.Xa;
+ly.prototype.setUrl=ly.prototype.cb;ly.prototype.setUrls=ly.prototype.Ya;ly.prototype.getTileGrid=ly.prototype.Va;ly.prototype.refresh=ly.prototype.wa;ly.prototype.getAttributions=ly.prototype.za;ly.prototype.getLogo=ly.prototype.ya;ly.prototype.getProjection=ly.prototype.Aa;ly.prototype.getState=ly.prototype.W;ly.prototype.setAttributions=ly.prototype.ua;ly.prototype.get=ly.prototype.get;ly.prototype.getKeys=ly.prototype.S;ly.prototype.getProperties=ly.prototype.R;ly.prototype.set=ly.prototype.set;
+ly.prototype.setProperties=ly.prototype.I;ly.prototype.unset=ly.prototype.T;ly.prototype.changed=ly.prototype.s;ly.prototype.dispatchEvent=ly.prototype.b;ly.prototype.getRevision=ly.prototype.M;ly.prototype.on=ly.prototype.J;ly.prototype.once=ly.prototype.N;ly.prototype.un=ly.prototype.K;ly.prototype.unByKey=ly.prototype.O;Z.prototype.setRenderReprojectionEdges=Z.prototype.Hb;Z.prototype.setTileGridForProjection=Z.prototype.Ib;Z.prototype.getTileLoadFunction=Z.prototype.ib;
+Z.prototype.getTileUrlFunction=Z.prototype.kb;Z.prototype.getUrls=Z.prototype.lb;Z.prototype.setTileLoadFunction=Z.prototype.sb;Z.prototype.setTileUrlFunction=Z.prototype.Xa;Z.prototype.setUrl=Z.prototype.cb;Z.prototype.setUrls=Z.prototype.Ya;Z.prototype.getTileGrid=Z.prototype.Va;Z.prototype.refresh=Z.prototype.wa;Z.prototype.getAttributions=Z.prototype.za;Z.prototype.getLogo=Z.prototype.ya;Z.prototype.getProjection=Z.prototype.Aa;Z.prototype.getState=Z.prototype.W;Z.prototype.setAttributions=Z.prototype.ua;
+Z.prototype.get=Z.prototype.get;Z.prototype.getKeys=Z.prototype.S;Z.prototype.getProperties=Z.prototype.R;Z.prototype.set=Z.prototype.set;Z.prototype.setProperties=Z.prototype.I;Z.prototype.unset=Z.prototype.T;Z.prototype.changed=Z.prototype.s;Z.prototype.dispatchEvent=Z.prototype.b;Z.prototype.getRevision=Z.prototype.M;Z.prototype.on=Z.prototype.J;Z.prototype.once=Z.prototype.N;Z.prototype.un=Z.prototype.K;Z.prototype.unByKey=Z.prototype.O;qy.prototype.setRenderReprojectionEdges=qy.prototype.Hb;
+qy.prototype.setTileGridForProjection=qy.prototype.Ib;qy.prototype.getTileLoadFunction=qy.prototype.ib;qy.prototype.getTileUrlFunction=qy.prototype.kb;qy.prototype.getUrls=qy.prototype.lb;qy.prototype.setTileLoadFunction=qy.prototype.sb;qy.prototype.setTileUrlFunction=qy.prototype.Xa;qy.prototype.setUrl=qy.prototype.cb;qy.prototype.setUrls=qy.prototype.Ya;qy.prototype.getTileGrid=qy.prototype.Va;qy.prototype.refresh=qy.prototype.wa;qy.prototype.getAttributions=qy.prototype.za;
+qy.prototype.getLogo=qy.prototype.ya;qy.prototype.getProjection=qy.prototype.Aa;qy.prototype.getState=qy.prototype.W;qy.prototype.setAttributions=qy.prototype.ua;qy.prototype.get=qy.prototype.get;qy.prototype.getKeys=qy.prototype.S;qy.prototype.getProperties=qy.prototype.R;qy.prototype.set=qy.prototype.set;qy.prototype.setProperties=qy.prototype.I;qy.prototype.unset=qy.prototype.T;qy.prototype.changed=qy.prototype.s;qy.prototype.dispatchEvent=qy.prototype.b;qy.prototype.getRevision=qy.prototype.M;
+qy.prototype.on=qy.prototype.J;qy.prototype.once=qy.prototype.N;qy.prototype.un=qy.prototype.K;qy.prototype.unByKey=qy.prototype.O;ax.prototype.getTileCoord=ax.prototype.c;ax.prototype.load=ax.prototype.load;Ui.prototype.changed=Ui.prototype.s;Ui.prototype.dispatchEvent=Ui.prototype.b;Ui.prototype.getRevision=Ui.prototype.M;Ui.prototype.on=Ui.prototype.J;Ui.prototype.once=Ui.prototype.N;Ui.prototype.un=Ui.prototype.K;Ui.prototype.unByKey=Ui.prototype.O;Ul.prototype.changed=Ul.prototype.s;
+Ul.prototype.dispatchEvent=Ul.prototype.b;Ul.prototype.getRevision=Ul.prototype.M;Ul.prototype.on=Ul.prototype.J;Ul.prototype.once=Ul.prototype.N;Ul.prototype.un=Ul.prototype.K;Ul.prototype.unByKey=Ul.prototype.O;um.prototype.changed=um.prototype.s;um.prototype.dispatchEvent=um.prototype.b;um.prototype.getRevision=um.prototype.M;um.prototype.on=um.prototype.J;um.prototype.once=um.prototype.N;um.prototype.un=um.prototype.K;um.prototype.unByKey=um.prototype.O;Cm.prototype.changed=Cm.prototype.s;
+Cm.prototype.dispatchEvent=Cm.prototype.b;Cm.prototype.getRevision=Cm.prototype.M;Cm.prototype.on=Cm.prototype.J;Cm.prototype.once=Cm.prototype.N;Cm.prototype.un=Cm.prototype.K;Cm.prototype.unByKey=Cm.prototype.O;Em.prototype.changed=Em.prototype.s;Em.prototype.dispatchEvent=Em.prototype.b;Em.prototype.getRevision=Em.prototype.M;Em.prototype.on=Em.prototype.J;Em.prototype.once=Em.prototype.N;Em.prototype.un=Em.prototype.K;Em.prototype.unByKey=Em.prototype.O;cj.prototype.changed=cj.prototype.s;
+cj.prototype.dispatchEvent=cj.prototype.b;cj.prototype.getRevision=cj.prototype.M;cj.prototype.on=cj.prototype.J;cj.prototype.once=cj.prototype.N;cj.prototype.un=cj.prototype.K;cj.prototype.unByKey=cj.prototype.O;gj.prototype.changed=gj.prototype.s;gj.prototype.dispatchEvent=gj.prototype.b;gj.prototype.getRevision=gj.prototype.M;gj.prototype.on=gj.prototype.J;gj.prototype.once=gj.prototype.N;gj.prototype.un=gj.prototype.K;gj.prototype.unByKey=gj.prototype.O;hj.prototype.changed=hj.prototype.s;
+hj.prototype.dispatchEvent=hj.prototype.b;hj.prototype.getRevision=hj.prototype.M;hj.prototype.on=hj.prototype.J;hj.prototype.once=hj.prototype.N;hj.prototype.un=hj.prototype.K;hj.prototype.unByKey=hj.prototype.O;ij.prototype.changed=ij.prototype.s;ij.prototype.dispatchEvent=ij.prototype.b;ij.prototype.getRevision=ij.prototype.M;ij.prototype.on=ij.prototype.J;ij.prototype.once=ij.prototype.N;ij.prototype.un=ij.prototype.K;ij.prototype.unByKey=ij.prototype.O;Nj.prototype.changed=Nj.prototype.s;
+Nj.prototype.dispatchEvent=Nj.prototype.b;Nj.prototype.getRevision=Nj.prototype.M;Nj.prototype.on=Nj.prototype.J;Nj.prototype.once=Nj.prototype.N;Nj.prototype.un=Nj.prototype.K;Nj.prototype.unByKey=Nj.prototype.O;Oj.prototype.changed=Oj.prototype.s;Oj.prototype.dispatchEvent=Oj.prototype.b;Oj.prototype.getRevision=Oj.prototype.M;Oj.prototype.on=Oj.prototype.J;Oj.prototype.once=Oj.prototype.N;Oj.prototype.un=Oj.prototype.K;Oj.prototype.unByKey=Oj.prototype.O;Jh.prototype.type=Jh.prototype.type;
+Jh.prototype.target=Jh.prototype.target;Jh.prototype.preventDefault=Jh.prototype.preventDefault;Jh.prototype.stopPropagation=Jh.prototype.stopPropagation;If.prototype.type=If.prototype.type;If.prototype.target=If.prototype.target;If.prototype.preventDefault=If.prototype.preventDefault;If.prototype.stopPropagation=If.prototype.stopPropagation;qh.prototype.get=qh.prototype.get;qh.prototype.getKeys=qh.prototype.S;qh.prototype.getProperties=qh.prototype.R;qh.prototype.set=qh.prototype.set;
+qh.prototype.setProperties=qh.prototype.I;qh.prototype.unset=qh.prototype.T;qh.prototype.changed=qh.prototype.s;qh.prototype.dispatchEvent=qh.prototype.b;qh.prototype.getRevision=qh.prototype.M;qh.prototype.on=qh.prototype.J;qh.prototype.once=qh.prototype.N;qh.prototype.un=qh.prototype.K;qh.prototype.unByKey=qh.prototype.O;yh.prototype.getExtent=yh.prototype.G;yh.prototype.getMaxResolution=yh.prototype.Wb;yh.prototype.getMinResolution=yh.prototype.Xb;yh.prototype.getOpacity=yh.prototype.Yb;
+yh.prototype.getVisible=yh.prototype.Fb;yh.prototype.getZIndex=yh.prototype.Zb;yh.prototype.setExtent=yh.prototype.kc;yh.prototype.setMaxResolution=yh.prototype.pc;yh.prototype.setMinResolution=yh.prototype.qc;yh.prototype.setOpacity=yh.prototype.lc;yh.prototype.setVisible=yh.prototype.mc;yh.prototype.setZIndex=yh.prototype.nc;yh.prototype.get=yh.prototype.get;yh.prototype.getKeys=yh.prototype.S;yh.prototype.getProperties=yh.prototype.R;yh.prototype.set=yh.prototype.set;
+yh.prototype.setProperties=yh.prototype.I;yh.prototype.unset=yh.prototype.T;yh.prototype.changed=yh.prototype.s;yh.prototype.dispatchEvent=yh.prototype.b;yh.prototype.getRevision=yh.prototype.M;yh.prototype.on=yh.prototype.J;yh.prototype.once=yh.prototype.N;yh.prototype.un=yh.prototype.K;yh.prototype.unByKey=yh.prototype.O;Kh.prototype.getExtent=Kh.prototype.G;Kh.prototype.getMaxResolution=Kh.prototype.Wb;Kh.prototype.getMinResolution=Kh.prototype.Xb;Kh.prototype.getOpacity=Kh.prototype.Yb;
+Kh.prototype.getVisible=Kh.prototype.Fb;Kh.prototype.getZIndex=Kh.prototype.Zb;Kh.prototype.setExtent=Kh.prototype.kc;Kh.prototype.setMaxResolution=Kh.prototype.pc;Kh.prototype.setMinResolution=Kh.prototype.qc;Kh.prototype.setOpacity=Kh.prototype.lc;Kh.prototype.setVisible=Kh.prototype.mc;Kh.prototype.setZIndex=Kh.prototype.nc;Kh.prototype.get=Kh.prototype.get;Kh.prototype.getKeys=Kh.prototype.S;Kh.prototype.getProperties=Kh.prototype.R;Kh.prototype.set=Kh.prototype.set;
+Kh.prototype.setProperties=Kh.prototype.I;Kh.prototype.unset=Kh.prototype.T;Kh.prototype.changed=Kh.prototype.s;Kh.prototype.dispatchEvent=Kh.prototype.b;Kh.prototype.getRevision=Kh.prototype.M;Kh.prototype.on=Kh.prototype.J;Kh.prototype.once=Kh.prototype.N;Kh.prototype.un=Kh.prototype.K;Kh.prototype.unByKey=Kh.prototype.O;G.prototype.setMap=G.prototype.setMap;G.prototype.setSource=G.prototype.Pc;G.prototype.getExtent=G.prototype.G;G.prototype.getMaxResolution=G.prototype.Wb;
+G.prototype.getMinResolution=G.prototype.Xb;G.prototype.getOpacity=G.prototype.Yb;G.prototype.getVisible=G.prototype.Fb;G.prototype.getZIndex=G.prototype.Zb;G.prototype.setExtent=G.prototype.kc;G.prototype.setMaxResolution=G.prototype.pc;G.prototype.setMinResolution=G.prototype.qc;G.prototype.setOpacity=G.prototype.lc;G.prototype.setVisible=G.prototype.mc;G.prototype.setZIndex=G.prototype.nc;G.prototype.get=G.prototype.get;G.prototype.getKeys=G.prototype.S;G.prototype.getProperties=G.prototype.R;
+G.prototype.set=G.prototype.set;G.prototype.setProperties=G.prototype.I;G.prototype.unset=G.prototype.T;G.prototype.changed=G.prototype.s;G.prototype.dispatchEvent=G.prototype.b;G.prototype.getRevision=G.prototype.M;G.prototype.on=G.prototype.J;G.prototype.once=G.prototype.N;G.prototype.un=G.prototype.K;G.prototype.unByKey=G.prototype.O;V.prototype.getSource=V.prototype.la;V.prototype.getStyle=V.prototype.D;V.prototype.getStyleFunction=V.prototype.L;V.prototype.setStyle=V.prototype.l;
+V.prototype.setMap=V.prototype.setMap;V.prototype.setSource=V.prototype.Pc;V.prototype.getExtent=V.prototype.G;V.prototype.getMaxResolution=V.prototype.Wb;V.prototype.getMinResolution=V.prototype.Xb;V.prototype.getOpacity=V.prototype.Yb;V.prototype.getVisible=V.prototype.Fb;V.prototype.getZIndex=V.prototype.Zb;V.prototype.setExtent=V.prototype.kc;V.prototype.setMaxResolution=V.prototype.pc;V.prototype.setMinResolution=V.prototype.qc;V.prototype.setOpacity=V.prototype.lc;V.prototype.setVisible=V.prototype.mc;
+V.prototype.setZIndex=V.prototype.nc;V.prototype.get=V.prototype.get;V.prototype.getKeys=V.prototype.S;V.prototype.getProperties=V.prototype.R;V.prototype.set=V.prototype.set;V.prototype.setProperties=V.prototype.I;V.prototype.unset=V.prototype.T;V.prototype.changed=V.prototype.s;V.prototype.dispatchEvent=V.prototype.b;V.prototype.getRevision=V.prototype.M;V.prototype.on=V.prototype.J;V.prototype.once=V.prototype.N;V.prototype.un=V.prototype.K;V.prototype.unByKey=V.prototype.O;
+ei.prototype.setMap=ei.prototype.setMap;ei.prototype.setSource=ei.prototype.Pc;ei.prototype.getExtent=ei.prototype.G;ei.prototype.getMaxResolution=ei.prototype.Wb;ei.prototype.getMinResolution=ei.prototype.Xb;ei.prototype.getOpacity=ei.prototype.Yb;ei.prototype.getVisible=ei.prototype.Fb;ei.prototype.getZIndex=ei.prototype.Zb;ei.prototype.setExtent=ei.prototype.kc;ei.prototype.setMaxResolution=ei.prototype.pc;ei.prototype.setMinResolution=ei.prototype.qc;ei.prototype.setOpacity=ei.prototype.lc;
+ei.prototype.setVisible=ei.prototype.mc;ei.prototype.setZIndex=ei.prototype.nc;ei.prototype.get=ei.prototype.get;ei.prototype.getKeys=ei.prototype.S;ei.prototype.getProperties=ei.prototype.R;ei.prototype.set=ei.prototype.set;ei.prototype.setProperties=ei.prototype.I;ei.prototype.unset=ei.prototype.T;ei.prototype.changed=ei.prototype.s;ei.prototype.dispatchEvent=ei.prototype.b;ei.prototype.getRevision=ei.prototype.M;ei.prototype.on=ei.prototype.J;ei.prototype.once=ei.prototype.N;ei.prototype.un=ei.prototype.K;
+ei.prototype.unByKey=ei.prototype.O;F.prototype.setMap=F.prototype.setMap;F.prototype.setSource=F.prototype.Pc;F.prototype.getExtent=F.prototype.G;F.prototype.getMaxResolution=F.prototype.Wb;F.prototype.getMinResolution=F.prototype.Xb;F.prototype.getOpacity=F.prototype.Yb;F.prototype.getVisible=F.prototype.Fb;F.prototype.getZIndex=F.prototype.Zb;F.prototype.setExtent=F.prototype.kc;F.prototype.setMaxResolution=F.prototype.pc;F.prototype.setMinResolution=F.prototype.qc;F.prototype.setOpacity=F.prototype.lc;
+F.prototype.setVisible=F.prototype.mc;F.prototype.setZIndex=F.prototype.nc;F.prototype.get=F.prototype.get;F.prototype.getKeys=F.prototype.S;F.prototype.getProperties=F.prototype.R;F.prototype.set=F.prototype.set;F.prototype.setProperties=F.prototype.I;F.prototype.unset=F.prototype.T;F.prototype.changed=F.prototype.s;F.prototype.dispatchEvent=F.prototype.b;F.prototype.getRevision=F.prototype.M;F.prototype.on=F.prototype.J;F.prototype.once=F.prototype.N;F.prototype.un=F.prototype.K;
+F.prototype.unByKey=F.prototype.O;H.prototype.getSource=H.prototype.la;H.prototype.getStyle=H.prototype.D;H.prototype.getStyleFunction=H.prototype.L;H.prototype.setStyle=H.prototype.l;H.prototype.setMap=H.prototype.setMap;H.prototype.setSource=H.prototype.Pc;H.prototype.getExtent=H.prototype.G;H.prototype.getMaxResolution=H.prototype.Wb;H.prototype.getMinResolution=H.prototype.Xb;H.prototype.getOpacity=H.prototype.Yb;H.prototype.getVisible=H.prototype.Fb;H.prototype.getZIndex=H.prototype.Zb;
+H.prototype.setExtent=H.prototype.kc;H.prototype.setMaxResolution=H.prototype.pc;H.prototype.setMinResolution=H.prototype.qc;H.prototype.setOpacity=H.prototype.lc;H.prototype.setVisible=H.prototype.mc;H.prototype.setZIndex=H.prototype.nc;H.prototype.get=H.prototype.get;H.prototype.getKeys=H.prototype.S;H.prototype.getProperties=H.prototype.R;H.prototype.set=H.prototype.set;H.prototype.setProperties=H.prototype.I;H.prototype.unset=H.prototype.T;H.prototype.changed=H.prototype.s;
+H.prototype.dispatchEvent=H.prototype.b;H.prototype.getRevision=H.prototype.M;H.prototype.on=H.prototype.J;H.prototype.once=H.prototype.N;H.prototype.un=H.prototype.K;H.prototype.unByKey=H.prototype.O;lg.prototype.get=lg.prototype.get;lg.prototype.getKeys=lg.prototype.S;lg.prototype.getProperties=lg.prototype.R;lg.prototype.set=lg.prototype.set;lg.prototype.setProperties=lg.prototype.I;lg.prototype.unset=lg.prototype.T;lg.prototype.changed=lg.prototype.s;lg.prototype.dispatchEvent=lg.prototype.b;
+lg.prototype.getRevision=lg.prototype.M;lg.prototype.on=lg.prototype.J;lg.prototype.once=lg.prototype.N;lg.prototype.un=lg.prototype.K;lg.prototype.unByKey=lg.prototype.O;qg.prototype.getActive=qg.prototype.f;qg.prototype.getMap=qg.prototype.c;qg.prototype.setActive=qg.prototype.Ea;qg.prototype.get=qg.prototype.get;qg.prototype.getKeys=qg.prototype.S;qg.prototype.getProperties=qg.prototype.R;qg.prototype.set=qg.prototype.set;qg.prototype.setProperties=qg.prototype.I;qg.prototype.unset=qg.prototype.T;
+qg.prototype.changed=qg.prototype.s;qg.prototype.dispatchEvent=qg.prototype.b;qg.prototype.getRevision=qg.prototype.M;qg.prototype.on=qg.prototype.J;qg.prototype.once=qg.prototype.N;qg.prototype.un=qg.prototype.K;qg.prototype.unByKey=qg.prototype.O;nv.prototype.getActive=nv.prototype.f;nv.prototype.getMap=nv.prototype.c;nv.prototype.setActive=nv.prototype.Ea;nv.prototype.get=nv.prototype.get;nv.prototype.getKeys=nv.prototype.S;nv.prototype.getProperties=nv.prototype.R;nv.prototype.set=nv.prototype.set;
+nv.prototype.setProperties=nv.prototype.I;nv.prototype.unset=nv.prototype.T;nv.prototype.changed=nv.prototype.s;nv.prototype.dispatchEvent=nv.prototype.b;nv.prototype.getRevision=nv.prototype.M;nv.prototype.on=nv.prototype.J;nv.prototype.once=nv.prototype.N;nv.prototype.un=nv.prototype.K;nv.prototype.unByKey=nv.prototype.O;qv.prototype.type=qv.prototype.type;qv.prototype.target=qv.prototype.target;qv.prototype.preventDefault=qv.prototype.preventDefault;qv.prototype.stopPropagation=qv.prototype.stopPropagation;
+Bg.prototype.getActive=Bg.prototype.f;Bg.prototype.getMap=Bg.prototype.c;Bg.prototype.setActive=Bg.prototype.Ea;Bg.prototype.get=Bg.prototype.get;Bg.prototype.getKeys=Bg.prototype.S;Bg.prototype.getProperties=Bg.prototype.R;Bg.prototype.set=Bg.prototype.set;Bg.prototype.setProperties=Bg.prototype.I;Bg.prototype.unset=Bg.prototype.T;Bg.prototype.changed=Bg.prototype.s;Bg.prototype.dispatchEvent=Bg.prototype.b;Bg.prototype.getRevision=Bg.prototype.M;Bg.prototype.on=Bg.prototype.J;
+Bg.prototype.once=Bg.prototype.N;Bg.prototype.un=Bg.prototype.K;Bg.prototype.unByKey=Bg.prototype.O;Pg.prototype.getActive=Pg.prototype.f;Pg.prototype.getMap=Pg.prototype.c;Pg.prototype.setActive=Pg.prototype.Ea;Pg.prototype.get=Pg.prototype.get;Pg.prototype.getKeys=Pg.prototype.S;Pg.prototype.getProperties=Pg.prototype.R;Pg.prototype.set=Pg.prototype.set;Pg.prototype.setProperties=Pg.prototype.I;Pg.prototype.unset=Pg.prototype.T;Pg.prototype.changed=Pg.prototype.s;Pg.prototype.dispatchEvent=Pg.prototype.b;
+Pg.prototype.getRevision=Pg.prototype.M;Pg.prototype.on=Pg.prototype.J;Pg.prototype.once=Pg.prototype.N;Pg.prototype.un=Pg.prototype.K;Pg.prototype.unByKey=Pg.prototype.O;Ug.prototype.type=Ug.prototype.type;Ug.prototype.target=Ug.prototype.target;Ug.prototype.preventDefault=Ug.prototype.preventDefault;Ug.prototype.stopPropagation=Ug.prototype.stopPropagation;Eg.prototype.getActive=Eg.prototype.f;Eg.prototype.getMap=Eg.prototype.c;Eg.prototype.setActive=Eg.prototype.Ea;Eg.prototype.get=Eg.prototype.get;
+Eg.prototype.getKeys=Eg.prototype.S;Eg.prototype.getProperties=Eg.prototype.R;Eg.prototype.set=Eg.prototype.set;Eg.prototype.setProperties=Eg.prototype.I;Eg.prototype.unset=Eg.prototype.T;Eg.prototype.changed=Eg.prototype.s;Eg.prototype.dispatchEvent=Eg.prototype.b;Eg.prototype.getRevision=Eg.prototype.M;Eg.prototype.on=Eg.prototype.J;Eg.prototype.once=Eg.prototype.N;Eg.prototype.un=Eg.prototype.K;Eg.prototype.unByKey=Eg.prototype.O;Ig.prototype.getActive=Ig.prototype.f;Ig.prototype.getMap=Ig.prototype.c;
+Ig.prototype.setActive=Ig.prototype.Ea;Ig.prototype.get=Ig.prototype.get;Ig.prototype.getKeys=Ig.prototype.S;Ig.prototype.getProperties=Ig.prototype.R;Ig.prototype.set=Ig.prototype.set;Ig.prototype.setProperties=Ig.prototype.I;Ig.prototype.unset=Ig.prototype.T;Ig.prototype.changed=Ig.prototype.s;Ig.prototype.dispatchEvent=Ig.prototype.b;Ig.prototype.getRevision=Ig.prototype.M;Ig.prototype.on=Ig.prototype.J;Ig.prototype.once=Ig.prototype.N;Ig.prototype.un=Ig.prototype.K;Ig.prototype.unByKey=Ig.prototype.O;
+sv.prototype.getActive=sv.prototype.f;sv.prototype.getMap=sv.prototype.c;sv.prototype.setActive=sv.prototype.Ea;sv.prototype.get=sv.prototype.get;sv.prototype.getKeys=sv.prototype.S;sv.prototype.getProperties=sv.prototype.R;sv.prototype.set=sv.prototype.set;sv.prototype.setProperties=sv.prototype.I;sv.prototype.unset=sv.prototype.T;sv.prototype.changed=sv.prototype.s;sv.prototype.dispatchEvent=sv.prototype.b;sv.prototype.getRevision=sv.prototype.M;sv.prototype.on=sv.prototype.J;
+sv.prototype.once=sv.prototype.N;sv.prototype.un=sv.prototype.K;sv.prototype.unByKey=sv.prototype.O;Yg.prototype.getGeometry=Yg.prototype.V;Yg.prototype.getActive=Yg.prototype.f;Yg.prototype.getMap=Yg.prototype.c;Yg.prototype.setActive=Yg.prototype.Ea;Yg.prototype.get=Yg.prototype.get;Yg.prototype.getKeys=Yg.prototype.S;Yg.prototype.getProperties=Yg.prototype.R;Yg.prototype.set=Yg.prototype.set;Yg.prototype.setProperties=Yg.prototype.I;Yg.prototype.unset=Yg.prototype.T;Yg.prototype.changed=Yg.prototype.s;
+Yg.prototype.dispatchEvent=Yg.prototype.b;Yg.prototype.getRevision=Yg.prototype.M;Yg.prototype.on=Yg.prototype.J;Yg.prototype.once=Yg.prototype.N;Yg.prototype.un=Yg.prototype.K;Yg.prototype.unByKey=Yg.prototype.O;Iv.prototype.getActive=Iv.prototype.f;Iv.prototype.getMap=Iv.prototype.c;Iv.prototype.setActive=Iv.prototype.Ea;Iv.prototype.get=Iv.prototype.get;Iv.prototype.getKeys=Iv.prototype.S;Iv.prototype.getProperties=Iv.prototype.R;Iv.prototype.set=Iv.prototype.set;Iv.prototype.setProperties=Iv.prototype.I;
+Iv.prototype.unset=Iv.prototype.T;Iv.prototype.changed=Iv.prototype.s;Iv.prototype.dispatchEvent=Iv.prototype.b;Iv.prototype.getRevision=Iv.prototype.M;Iv.prototype.on=Iv.prototype.J;Iv.prototype.once=Iv.prototype.N;Iv.prototype.un=Iv.prototype.K;Iv.prototype.unByKey=Iv.prototype.O;Xv.prototype.type=Xv.prototype.type;Xv.prototype.target=Xv.prototype.target;Xv.prototype.preventDefault=Xv.prototype.preventDefault;Xv.prototype.stopPropagation=Xv.prototype.stopPropagation;aw.prototype.getActive=aw.prototype.f;
+aw.prototype.getMap=aw.prototype.c;aw.prototype.setActive=aw.prototype.Ea;aw.prototype.get=aw.prototype.get;aw.prototype.getKeys=aw.prototype.S;aw.prototype.getProperties=aw.prototype.R;aw.prototype.set=aw.prototype.set;aw.prototype.setProperties=aw.prototype.I;aw.prototype.unset=aw.prototype.T;aw.prototype.changed=aw.prototype.s;aw.prototype.dispatchEvent=aw.prototype.b;aw.prototype.getRevision=aw.prototype.M;aw.prototype.on=aw.prototype.J;aw.prototype.once=aw.prototype.N;aw.prototype.un=aw.prototype.K;
+aw.prototype.unByKey=aw.prototype.O;lw.prototype.type=lw.prototype.type;lw.prototype.target=lw.prototype.target;lw.prototype.preventDefault=lw.prototype.preventDefault;lw.prototype.stopPropagation=lw.prototype.stopPropagation;Zg.prototype.getActive=Zg.prototype.f;Zg.prototype.getMap=Zg.prototype.c;Zg.prototype.setActive=Zg.prototype.Ea;Zg.prototype.get=Zg.prototype.get;Zg.prototype.getKeys=Zg.prototype.S;Zg.prototype.getProperties=Zg.prototype.R;Zg.prototype.set=Zg.prototype.set;
+Zg.prototype.setProperties=Zg.prototype.I;Zg.prototype.unset=Zg.prototype.T;Zg.prototype.changed=Zg.prototype.s;Zg.prototype.dispatchEvent=Zg.prototype.b;Zg.prototype.getRevision=Zg.prototype.M;Zg.prototype.on=Zg.prototype.J;Zg.prototype.once=Zg.prototype.N;Zg.prototype.un=Zg.prototype.K;Zg.prototype.unByKey=Zg.prototype.O;ah.prototype.getActive=ah.prototype.f;ah.prototype.getMap=ah.prototype.c;ah.prototype.setActive=ah.prototype.Ea;ah.prototype.get=ah.prototype.get;ah.prototype.getKeys=ah.prototype.S;
+ah.prototype.getProperties=ah.prototype.R;ah.prototype.set=ah.prototype.set;ah.prototype.setProperties=ah.prototype.I;ah.prototype.unset=ah.prototype.T;ah.prototype.changed=ah.prototype.s;ah.prototype.dispatchEvent=ah.prototype.b;ah.prototype.getRevision=ah.prototype.M;ah.prototype.on=ah.prototype.J;ah.prototype.once=ah.prototype.N;ah.prototype.un=ah.prototype.K;ah.prototype.unByKey=ah.prototype.O;nw.prototype.getActive=nw.prototype.f;nw.prototype.getMap=nw.prototype.c;nw.prototype.setActive=nw.prototype.Ea;
+nw.prototype.get=nw.prototype.get;nw.prototype.getKeys=nw.prototype.S;nw.prototype.getProperties=nw.prototype.R;nw.prototype.set=nw.prototype.set;nw.prototype.setProperties=nw.prototype.I;nw.prototype.unset=nw.prototype.T;nw.prototype.changed=nw.prototype.s;nw.prototype.dispatchEvent=nw.prototype.b;nw.prototype.getRevision=nw.prototype.M;nw.prototype.on=nw.prototype.J;nw.prototype.once=nw.prototype.N;nw.prototype.un=nw.prototype.K;nw.prototype.unByKey=nw.prototype.O;vw.prototype.type=vw.prototype.type;
+vw.prototype.target=vw.prototype.target;vw.prototype.preventDefault=vw.prototype.preventDefault;vw.prototype.stopPropagation=vw.prototype.stopPropagation;ch.prototype.getActive=ch.prototype.f;ch.prototype.getMap=ch.prototype.c;ch.prototype.setActive=ch.prototype.Ea;ch.prototype.get=ch.prototype.get;ch.prototype.getKeys=ch.prototype.S;ch.prototype.getProperties=ch.prototype.R;ch.prototype.set=ch.prototype.set;ch.prototype.setProperties=ch.prototype.I;ch.prototype.unset=ch.prototype.T;
+ch.prototype.changed=ch.prototype.s;ch.prototype.dispatchEvent=ch.prototype.b;ch.prototype.getRevision=ch.prototype.M;ch.prototype.on=ch.prototype.J;ch.prototype.once=ch.prototype.N;ch.prototype.un=ch.prototype.K;ch.prototype.unByKey=ch.prototype.O;gh.prototype.getActive=gh.prototype.f;gh.prototype.getMap=gh.prototype.c;gh.prototype.setActive=gh.prototype.Ea;gh.prototype.get=gh.prototype.get;gh.prototype.getKeys=gh.prototype.S;gh.prototype.getProperties=gh.prototype.R;gh.prototype.set=gh.prototype.set;
+gh.prototype.setProperties=gh.prototype.I;gh.prototype.unset=gh.prototype.T;gh.prototype.changed=gh.prototype.s;gh.prototype.dispatchEvent=gh.prototype.b;gh.prototype.getRevision=gh.prototype.M;gh.prototype.on=gh.prototype.J;gh.prototype.once=gh.prototype.N;gh.prototype.un=gh.prototype.K;gh.prototype.unByKey=gh.prototype.O;kh.prototype.getActive=kh.prototype.f;kh.prototype.getMap=kh.prototype.c;kh.prototype.setActive=kh.prototype.Ea;kh.prototype.get=kh.prototype.get;kh.prototype.getKeys=kh.prototype.S;
+kh.prototype.getProperties=kh.prototype.R;kh.prototype.set=kh.prototype.set;kh.prototype.setProperties=kh.prototype.I;kh.prototype.unset=kh.prototype.T;kh.prototype.changed=kh.prototype.s;kh.prototype.dispatchEvent=kh.prototype.b;kh.prototype.getRevision=kh.prototype.M;kh.prototype.on=kh.prototype.J;kh.prototype.once=kh.prototype.N;kh.prototype.un=kh.prototype.K;kh.prototype.unByKey=kh.prototype.O;Dw.prototype.getActive=Dw.prototype.f;Dw.prototype.getMap=Dw.prototype.c;Dw.prototype.setActive=Dw.prototype.Ea;
+Dw.prototype.get=Dw.prototype.get;Dw.prototype.getKeys=Dw.prototype.S;Dw.prototype.getProperties=Dw.prototype.R;Dw.prototype.set=Dw.prototype.set;Dw.prototype.setProperties=Dw.prototype.I;Dw.prototype.unset=Dw.prototype.T;Dw.prototype.changed=Dw.prototype.s;Dw.prototype.dispatchEvent=Dw.prototype.b;Dw.prototype.getRevision=Dw.prototype.M;Dw.prototype.on=Dw.prototype.J;Dw.prototype.once=Dw.prototype.N;Dw.prototype.un=Dw.prototype.K;Dw.prototype.unByKey=Dw.prototype.O;Gw.prototype.type=Gw.prototype.type;
+Gw.prototype.target=Gw.prototype.target;Gw.prototype.preventDefault=Gw.prototype.preventDefault;Gw.prototype.stopPropagation=Gw.prototype.stopPropagation;Iw.prototype.getActive=Iw.prototype.f;Iw.prototype.getMap=Iw.prototype.c;Iw.prototype.setActive=Iw.prototype.Ea;Iw.prototype.get=Iw.prototype.get;Iw.prototype.getKeys=Iw.prototype.S;Iw.prototype.getProperties=Iw.prototype.R;Iw.prototype.set=Iw.prototype.set;Iw.prototype.setProperties=Iw.prototype.I;Iw.prototype.unset=Iw.prototype.T;
+Iw.prototype.changed=Iw.prototype.s;Iw.prototype.dispatchEvent=Iw.prototype.b;Iw.prototype.getRevision=Iw.prototype.M;Iw.prototype.on=Iw.prototype.J;Iw.prototype.once=Iw.prototype.N;Iw.prototype.un=Iw.prototype.K;Iw.prototype.unByKey=Iw.prototype.O;Mw.prototype.getActive=Mw.prototype.f;Mw.prototype.getMap=Mw.prototype.c;Mw.prototype.setActive=Mw.prototype.Ea;Mw.prototype.get=Mw.prototype.get;Mw.prototype.getKeys=Mw.prototype.S;Mw.prototype.getProperties=Mw.prototype.R;Mw.prototype.set=Mw.prototype.set;
+Mw.prototype.setProperties=Mw.prototype.I;Mw.prototype.unset=Mw.prototype.T;Mw.prototype.changed=Mw.prototype.s;Mw.prototype.dispatchEvent=Mw.prototype.b;Mw.prototype.getRevision=Mw.prototype.M;Mw.prototype.on=Mw.prototype.J;Mw.prototype.once=Mw.prototype.N;Mw.prototype.un=Mw.prototype.K;Mw.prototype.unByKey=Mw.prototype.O;Sw.prototype.type=Sw.prototype.type;Sw.prototype.target=Sw.prototype.target;Sw.prototype.preventDefault=Sw.prototype.preventDefault;Sw.prototype.stopPropagation=Sw.prototype.stopPropagation;
+Rc.prototype.get=Rc.prototype.get;Rc.prototype.getKeys=Rc.prototype.S;Rc.prototype.getProperties=Rc.prototype.R;Rc.prototype.set=Rc.prototype.set;Rc.prototype.setProperties=Rc.prototype.I;Rc.prototype.unset=Rc.prototype.T;Rc.prototype.changed=Rc.prototype.s;Rc.prototype.dispatchEvent=Rc.prototype.b;Rc.prototype.getRevision=Rc.prototype.M;Rc.prototype.on=Rc.prototype.J;Rc.prototype.once=Rc.prototype.N;Rc.prototype.un=Rc.prototype.K;Rc.prototype.unByKey=Rc.prototype.O;Uc.prototype.getClosestPoint=Uc.prototype.Cb;
+Uc.prototype.intersectsCoordinate=Uc.prototype.mb;Uc.prototype.getExtent=Uc.prototype.G;Uc.prototype.rotate=Uc.prototype.rotate;Uc.prototype.scale=Uc.prototype.scale;Uc.prototype.simplify=Uc.prototype.Jb;Uc.prototype.transform=Uc.prototype.ob;Uc.prototype.get=Uc.prototype.get;Uc.prototype.getKeys=Uc.prototype.S;Uc.prototype.getProperties=Uc.prototype.R;Uc.prototype.set=Uc.prototype.set;Uc.prototype.setProperties=Uc.prototype.I;Uc.prototype.unset=Uc.prototype.T;Uc.prototype.changed=Uc.prototype.s;
+Uc.prototype.dispatchEvent=Uc.prototype.b;Uc.prototype.getRevision=Uc.prototype.M;Uc.prototype.on=Uc.prototype.J;Uc.prototype.once=Uc.prototype.N;Uc.prototype.un=Uc.prototype.K;Uc.prototype.unByKey=Uc.prototype.O;bv.prototype.getFirstCoordinate=bv.prototype.Rb;bv.prototype.getLastCoordinate=bv.prototype.Sb;bv.prototype.getLayout=bv.prototype.Tb;bv.prototype.rotate=bv.prototype.rotate;bv.prototype.scale=bv.prototype.scale;bv.prototype.getClosestPoint=bv.prototype.Cb;
+bv.prototype.intersectsCoordinate=bv.prototype.mb;bv.prototype.getExtent=bv.prototype.G;bv.prototype.simplify=bv.prototype.Jb;bv.prototype.get=bv.prototype.get;bv.prototype.getKeys=bv.prototype.S;bv.prototype.getProperties=bv.prototype.R;bv.prototype.set=bv.prototype.set;bv.prototype.setProperties=bv.prototype.I;bv.prototype.unset=bv.prototype.T;bv.prototype.changed=bv.prototype.s;bv.prototype.dispatchEvent=bv.prototype.b;bv.prototype.getRevision=bv.prototype.M;bv.prototype.on=bv.prototype.J;
+bv.prototype.once=bv.prototype.N;bv.prototype.un=bv.prototype.K;bv.prototype.unByKey=bv.prototype.O;Vo.prototype.getClosestPoint=Vo.prototype.Cb;Vo.prototype.intersectsCoordinate=Vo.prototype.mb;Vo.prototype.getExtent=Vo.prototype.G;Vo.prototype.rotate=Vo.prototype.rotate;Vo.prototype.scale=Vo.prototype.scale;Vo.prototype.simplify=Vo.prototype.Jb;Vo.prototype.transform=Vo.prototype.ob;Vo.prototype.get=Vo.prototype.get;Vo.prototype.getKeys=Vo.prototype.S;Vo.prototype.getProperties=Vo.prototype.R;
+Vo.prototype.set=Vo.prototype.set;Vo.prototype.setProperties=Vo.prototype.I;Vo.prototype.unset=Vo.prototype.T;Vo.prototype.changed=Vo.prototype.s;Vo.prototype.dispatchEvent=Vo.prototype.b;Vo.prototype.getRevision=Vo.prototype.M;Vo.prototype.on=Vo.prototype.J;Vo.prototype.once=Vo.prototype.N;Vo.prototype.un=Vo.prototype.K;Vo.prototype.unByKey=Vo.prototype.O;md.prototype.getFirstCoordinate=md.prototype.Rb;md.prototype.getLastCoordinate=md.prototype.Sb;md.prototype.getLayout=md.prototype.Tb;
+md.prototype.rotate=md.prototype.rotate;md.prototype.scale=md.prototype.scale;md.prototype.getClosestPoint=md.prototype.Cb;md.prototype.intersectsCoordinate=md.prototype.mb;md.prototype.getExtent=md.prototype.G;md.prototype.simplify=md.prototype.Jb;md.prototype.transform=md.prototype.ob;md.prototype.get=md.prototype.get;md.prototype.getKeys=md.prototype.S;md.prototype.getProperties=md.prototype.R;md.prototype.set=md.prototype.set;md.prototype.setProperties=md.prototype.I;md.prototype.unset=md.prototype.T;
+md.prototype.changed=md.prototype.s;md.prototype.dispatchEvent=md.prototype.b;md.prototype.getRevision=md.prototype.M;md.prototype.on=md.prototype.J;md.prototype.once=md.prototype.N;md.prototype.un=md.prototype.K;md.prototype.unByKey=md.prototype.O;P.prototype.getFirstCoordinate=P.prototype.Rb;P.prototype.getLastCoordinate=P.prototype.Sb;P.prototype.getLayout=P.prototype.Tb;P.prototype.rotate=P.prototype.rotate;P.prototype.scale=P.prototype.scale;P.prototype.getClosestPoint=P.prototype.Cb;
+P.prototype.intersectsCoordinate=P.prototype.mb;P.prototype.getExtent=P.prototype.G;P.prototype.simplify=P.prototype.Jb;P.prototype.transform=P.prototype.ob;P.prototype.get=P.prototype.get;P.prototype.getKeys=P.prototype.S;P.prototype.getProperties=P.prototype.R;P.prototype.set=P.prototype.set;P.prototype.setProperties=P.prototype.I;P.prototype.unset=P.prototype.T;P.prototype.changed=P.prototype.s;P.prototype.dispatchEvent=P.prototype.b;P.prototype.getRevision=P.prototype.M;P.prototype.on=P.prototype.J;
+P.prototype.once=P.prototype.N;P.prototype.un=P.prototype.K;P.prototype.unByKey=P.prototype.O;Q.prototype.getFirstCoordinate=Q.prototype.Rb;Q.prototype.getLastCoordinate=Q.prototype.Sb;Q.prototype.getLayout=Q.prototype.Tb;Q.prototype.rotate=Q.prototype.rotate;Q.prototype.scale=Q.prototype.scale;Q.prototype.getClosestPoint=Q.prototype.Cb;Q.prototype.intersectsCoordinate=Q.prototype.mb;Q.prototype.getExtent=Q.prototype.G;Q.prototype.simplify=Q.prototype.Jb;Q.prototype.transform=Q.prototype.ob;
+Q.prototype.get=Q.prototype.get;Q.prototype.getKeys=Q.prototype.S;Q.prototype.getProperties=Q.prototype.R;Q.prototype.set=Q.prototype.set;Q.prototype.setProperties=Q.prototype.I;Q.prototype.unset=Q.prototype.T;Q.prototype.changed=Q.prototype.s;Q.prototype.dispatchEvent=Q.prototype.b;Q.prototype.getRevision=Q.prototype.M;Q.prototype.on=Q.prototype.J;Q.prototype.once=Q.prototype.N;Q.prototype.un=Q.prototype.K;Q.prototype.unByKey=Q.prototype.O;R.prototype.getFirstCoordinate=R.prototype.Rb;
+R.prototype.getLastCoordinate=R.prototype.Sb;R.prototype.getLayout=R.prototype.Tb;R.prototype.rotate=R.prototype.rotate;R.prototype.scale=R.prototype.scale;R.prototype.getClosestPoint=R.prototype.Cb;R.prototype.intersectsCoordinate=R.prototype.mb;R.prototype.getExtent=R.prototype.G;R.prototype.simplify=R.prototype.Jb;R.prototype.transform=R.prototype.ob;R.prototype.get=R.prototype.get;R.prototype.getKeys=R.prototype.S;R.prototype.getProperties=R.prototype.R;R.prototype.set=R.prototype.set;
+R.prototype.setProperties=R.prototype.I;R.prototype.unset=R.prototype.T;R.prototype.changed=R.prototype.s;R.prototype.dispatchEvent=R.prototype.b;R.prototype.getRevision=R.prototype.M;R.prototype.on=R.prototype.J;R.prototype.once=R.prototype.N;R.prototype.un=R.prototype.K;R.prototype.unByKey=R.prototype.O;S.prototype.getFirstCoordinate=S.prototype.Rb;S.prototype.getLastCoordinate=S.prototype.Sb;S.prototype.getLayout=S.prototype.Tb;S.prototype.rotate=S.prototype.rotate;S.prototype.scale=S.prototype.scale;
+S.prototype.getClosestPoint=S.prototype.Cb;S.prototype.intersectsCoordinate=S.prototype.mb;S.prototype.getExtent=S.prototype.G;S.prototype.simplify=S.prototype.Jb;S.prototype.transform=S.prototype.ob;S.prototype.get=S.prototype.get;S.prototype.getKeys=S.prototype.S;S.prototype.getProperties=S.prototype.R;S.prototype.set=S.prototype.set;S.prototype.setProperties=S.prototype.I;S.prototype.unset=S.prototype.T;S.prototype.changed=S.prototype.s;S.prototype.dispatchEvent=S.prototype.b;
+S.prototype.getRevision=S.prototype.M;S.prototype.on=S.prototype.J;S.prototype.once=S.prototype.N;S.prototype.un=S.prototype.K;S.prototype.unByKey=S.prototype.O;C.prototype.getFirstCoordinate=C.prototype.Rb;C.prototype.getLastCoordinate=C.prototype.Sb;C.prototype.getLayout=C.prototype.Tb;C.prototype.rotate=C.prototype.rotate;C.prototype.scale=C.prototype.scale;C.prototype.getClosestPoint=C.prototype.Cb;C.prototype.intersectsCoordinate=C.prototype.mb;C.prototype.getExtent=C.prototype.G;
+C.prototype.simplify=C.prototype.Jb;C.prototype.transform=C.prototype.ob;C.prototype.get=C.prototype.get;C.prototype.getKeys=C.prototype.S;C.prototype.getProperties=C.prototype.R;C.prototype.set=C.prototype.set;C.prototype.setProperties=C.prototype.I;C.prototype.unset=C.prototype.T;C.prototype.changed=C.prototype.s;C.prototype.dispatchEvent=C.prototype.b;C.prototype.getRevision=C.prototype.M;C.prototype.on=C.prototype.J;C.prototype.once=C.prototype.N;C.prototype.un=C.prototype.K;
+C.prototype.unByKey=C.prototype.O;E.prototype.getFirstCoordinate=E.prototype.Rb;E.prototype.getLastCoordinate=E.prototype.Sb;E.prototype.getLayout=E.prototype.Tb;E.prototype.rotate=E.prototype.rotate;E.prototype.scale=E.prototype.scale;E.prototype.getClosestPoint=E.prototype.Cb;E.prototype.intersectsCoordinate=E.prototype.mb;E.prototype.getExtent=E.prototype.G;E.prototype.simplify=E.prototype.Jb;E.prototype.transform=E.prototype.ob;E.prototype.get=E.prototype.get;E.prototype.getKeys=E.prototype.S;
+E.prototype.getProperties=E.prototype.R;E.prototype.set=E.prototype.set;E.prototype.setProperties=E.prototype.I;E.prototype.unset=E.prototype.T;E.prototype.changed=E.prototype.s;E.prototype.dispatchEvent=E.prototype.b;E.prototype.getRevision=E.prototype.M;E.prototype.on=E.prototype.J;E.prototype.once=E.prototype.N;E.prototype.un=E.prototype.K;E.prototype.unByKey=E.prototype.O;tp.prototype.readFeatures=tp.prototype.La;Cp.prototype.readFeatures=Cp.prototype.La;tp.prototype.readFeatures=tp.prototype.La;
+Ie.prototype.get=Ie.prototype.get;Ie.prototype.getKeys=Ie.prototype.S;Ie.prototype.getProperties=Ie.prototype.R;Ie.prototype.set=Ie.prototype.set;Ie.prototype.setProperties=Ie.prototype.I;Ie.prototype.unset=Ie.prototype.T;Ie.prototype.changed=Ie.prototype.s;Ie.prototype.dispatchEvent=Ie.prototype.b;Ie.prototype.getRevision=Ie.prototype.M;Ie.prototype.on=Ie.prototype.J;Ie.prototype.once=Ie.prototype.N;Ie.prototype.un=Ie.prototype.K;Ie.prototype.unByKey=Ie.prototype.O;Je.prototype.getMap=Je.prototype.i;
+Je.prototype.setMap=Je.prototype.setMap;Je.prototype.setTarget=Je.prototype.c;Je.prototype.get=Je.prototype.get;Je.prototype.getKeys=Je.prototype.S;Je.prototype.getProperties=Je.prototype.R;Je.prototype.set=Je.prototype.set;Je.prototype.setProperties=Je.prototype.I;Je.prototype.unset=Je.prototype.T;Je.prototype.changed=Je.prototype.s;Je.prototype.dispatchEvent=Je.prototype.b;Je.prototype.getRevision=Je.prototype.M;Je.prototype.on=Je.prototype.J;Je.prototype.once=Je.prototype.N;Je.prototype.un=Je.prototype.K;
+Je.prototype.unByKey=Je.prototype.O;Me.prototype.getMap=Me.prototype.i;Me.prototype.setMap=Me.prototype.setMap;Me.prototype.setTarget=Me.prototype.c;Me.prototype.get=Me.prototype.get;Me.prototype.getKeys=Me.prototype.S;Me.prototype.getProperties=Me.prototype.R;Me.prototype.set=Me.prototype.set;Me.prototype.setProperties=Me.prototype.I;Me.prototype.unset=Me.prototype.T;Me.prototype.changed=Me.prototype.s;Me.prototype.dispatchEvent=Me.prototype.b;Me.prototype.getRevision=Me.prototype.M;
+Me.prototype.on=Me.prototype.J;Me.prototype.once=Me.prototype.N;Me.prototype.un=Me.prototype.K;Me.prototype.unByKey=Me.prototype.O;Ve.prototype.getMap=Ve.prototype.i;Ve.prototype.setMap=Ve.prototype.setMap;Ve.prototype.setTarget=Ve.prototype.c;Ve.prototype.get=Ve.prototype.get;Ve.prototype.getKeys=Ve.prototype.S;Ve.prototype.getProperties=Ve.prototype.R;Ve.prototype.set=Ve.prototype.set;Ve.prototype.setProperties=Ve.prototype.I;Ve.prototype.unset=Ve.prototype.T;Ve.prototype.changed=Ve.prototype.s;
+Ve.prototype.dispatchEvent=Ve.prototype.b;Ve.prototype.getRevision=Ve.prototype.M;Ve.prototype.on=Ve.prototype.J;Ve.prototype.once=Ve.prototype.N;Ve.prototype.un=Ve.prototype.K;Ve.prototype.unByKey=Ve.prototype.O;gn.prototype.getMap=gn.prototype.i;gn.prototype.setMap=gn.prototype.setMap;gn.prototype.setTarget=gn.prototype.c;gn.prototype.get=gn.prototype.get;gn.prototype.getKeys=gn.prototype.S;gn.prototype.getProperties=gn.prototype.R;gn.prototype.set=gn.prototype.set;gn.prototype.setProperties=gn.prototype.I;
+gn.prototype.unset=gn.prototype.T;gn.prototype.changed=gn.prototype.s;gn.prototype.dispatchEvent=gn.prototype.b;gn.prototype.getRevision=gn.prototype.M;gn.prototype.on=gn.prototype.J;gn.prototype.once=gn.prototype.N;gn.prototype.un=gn.prototype.K;gn.prototype.unByKey=gn.prototype.O;Re.prototype.getMap=Re.prototype.i;Re.prototype.setMap=Re.prototype.setMap;Re.prototype.setTarget=Re.prototype.c;Re.prototype.get=Re.prototype.get;Re.prototype.getKeys=Re.prototype.S;Re.prototype.getProperties=Re.prototype.R;
+Re.prototype.set=Re.prototype.set;Re.prototype.setProperties=Re.prototype.I;Re.prototype.unset=Re.prototype.T;Re.prototype.changed=Re.prototype.s;Re.prototype.dispatchEvent=Re.prototype.b;Re.prototype.getRevision=Re.prototype.M;Re.prototype.on=Re.prototype.J;Re.prototype.once=Re.prototype.N;Re.prototype.un=Re.prototype.K;Re.prototype.unByKey=Re.prototype.O;mn.prototype.getMap=mn.prototype.i;mn.prototype.setMap=mn.prototype.setMap;mn.prototype.setTarget=mn.prototype.c;mn.prototype.get=mn.prototype.get;
+mn.prototype.getKeys=mn.prototype.S;mn.prototype.getProperties=mn.prototype.R;mn.prototype.set=mn.prototype.set;mn.prototype.setProperties=mn.prototype.I;mn.prototype.unset=mn.prototype.T;mn.prototype.changed=mn.prototype.s;mn.prototype.dispatchEvent=mn.prototype.b;mn.prototype.getRevision=mn.prototype.M;mn.prototype.on=mn.prototype.J;mn.prototype.once=mn.prototype.N;mn.prototype.un=mn.prototype.K;mn.prototype.unByKey=mn.prototype.O;Te.prototype.getMap=Te.prototype.i;Te.prototype.setMap=Te.prototype.setMap;
+Te.prototype.setTarget=Te.prototype.c;Te.prototype.get=Te.prototype.get;Te.prototype.getKeys=Te.prototype.S;Te.prototype.getProperties=Te.prototype.R;Te.prototype.set=Te.prototype.set;Te.prototype.setProperties=Te.prototype.I;Te.prototype.unset=Te.prototype.T;Te.prototype.changed=Te.prototype.s;Te.prototype.dispatchEvent=Te.prototype.b;Te.prototype.getRevision=Te.prototype.M;Te.prototype.on=Te.prototype.J;Te.prototype.once=Te.prototype.N;Te.prototype.un=Te.prototype.K;Te.prototype.unByKey=Te.prototype.O;
+wn.prototype.getMap=wn.prototype.i;wn.prototype.setMap=wn.prototype.setMap;wn.prototype.setTarget=wn.prototype.c;wn.prototype.get=wn.prototype.get;wn.prototype.getKeys=wn.prototype.S;wn.prototype.getProperties=wn.prototype.R;wn.prototype.set=wn.prototype.set;wn.prototype.setProperties=wn.prototype.I;wn.prototype.unset=wn.prototype.T;wn.prototype.changed=wn.prototype.s;wn.prototype.dispatchEvent=wn.prototype.b;wn.prototype.getRevision=wn.prototype.M;wn.prototype.on=wn.prototype.J;
+wn.prototype.once=wn.prototype.N;wn.prototype.un=wn.prototype.K;wn.prototype.unByKey=wn.prototype.O;Bn.prototype.getMap=Bn.prototype.i;Bn.prototype.setMap=Bn.prototype.setMap;Bn.prototype.setTarget=Bn.prototype.c;Bn.prototype.get=Bn.prototype.get;Bn.prototype.getKeys=Bn.prototype.S;Bn.prototype.getProperties=Bn.prototype.R;Bn.prototype.set=Bn.prototype.set;Bn.prototype.setProperties=Bn.prototype.I;Bn.prototype.unset=Bn.prototype.T;Bn.prototype.changed=Bn.prototype.s;Bn.prototype.dispatchEvent=Bn.prototype.b;
+Bn.prototype.getRevision=Bn.prototype.M;Bn.prototype.on=Bn.prototype.J;Bn.prototype.once=Bn.prototype.N;Bn.prototype.un=Bn.prototype.K;Bn.prototype.unByKey=Bn.prototype.O;
+ return OPENLAYERS.ol;
+}));
+
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 835a716e4..eeca98190 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -10,6 +10,7 @@
//= require jquery-ui
//= require modernizr
//= require cocoon
+//= require_directory ./OpenLayers
//= require bootstrap-sass-official
//= require select2-full
//= require select2_locale_fr