// OpenLayers. See https://openlayers.org/ // License: https://raw.githubusercontent.com/openlayers/openlayers/master/LICENSE.md // Version: v4.6.4 ;(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 attempt to load 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. * * Avoid including base.js more than once. This is strictly discouraged and not * supported. goog.require(...) won't work properly in that case. * * @provideGoog */ /** * @define {boolean} Overridden to true by the compiler. */ 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. * * @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; }; /** * 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'; }; /** * 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]); } 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[part] !== Object.prototype[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 && // Anti DOM-clobbering runtime check (b/37736576). /** @type {?} */ (goog.global.CLOSURE_UNCOMPILED_DEFINES).nodeType === undefined && Object.prototype.hasOwnProperty.call( goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; } else if ( goog.global.CLOSURE_DEFINES && // Anti DOM-clobbering runtime check (b/37736576). /** @type {?} */ (goog.global.CLOSURE_DEFINES).nodeType === undefined && 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. It can be easily stripped * by specifying --define goog.DEBUG=false to the Closure Compiler aka * 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 a compiler * 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). * */ 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 compiler. */ 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 new 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 new 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. * @return {void} */ goog.module = function(name) { if (!goog.isString(name) || !name || name.search(goog.VALID_MODULE_RE_) == -1) { throw new Error('Invalid module identifier'); } if (!goog.isInModuleLoader_()) { throw new Error( 'Module ' + name + ' has been loaded incorrectly. Note, ' + 'modules cannot be loaded as normal scripts. They require some kind of ' + 'pre-processing step. You\'re likely trying to load a module via a ' + 'script tag or as a part of a concatenated bundle without rewriting the ' + 'module. For more info see: ' + 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); } if (goog.moduleLoaderState_.moduleName) { throw new 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 new 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 (name in goog.loadedModules_) { return goog.loadedModules_[name]; } else if (!goog.implicitNamespaces_[name]) { var ns = goog.getObjectByName(name); return ns != null ? ns : null; } } 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 new 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 new 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. * * Before using goog.forwardDeclare, please read the documentation at * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to * understand the options and tradeoffs when working with forward declarations. * * @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 i = 0; i < parts.length; i++) { cur = cur[parts[i]]; if (!goog.isDefAndNotNull(cur)) { 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 was 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 compiler. * @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 to 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 if (goog.ENABLE_DEBUG_LOADER) { var path = goog.getPathFromDeps_(name); if (path) { goog.writeScripts_(path); } else { var errorMessage = 'goog.require could not find: ' + name; goog.logToConsole_(errorMessage); throw new Error(errorMessage); } } return null; } }; /** * 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 attempt to load Closure's deps file. By default, when uncompiled, * deps files will attempt to be loaded. * @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. * * @type {!Function} * @throws {Error} when invoked to indicate the method should be overridden. */ goog.abstractMethod = function() { throw new 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) { // instance_ is immediately set to prevent issues with sealed constructors // such as are encountered when a constructor is returned as the export object // of a goog.module in unoptimized code. ctor.instance_ = undefined; 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) && // Anti DOM-clobbering runtime check (b/37736576). goog.isString(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; // If we have a currentScript available, use it exclusively. var currentScript = doc.currentScript; if (currentScript) { var scripts = [currentScript]; } else { 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); /** * Whether IE9 or earlier is waiting on a dependency. This ensures that * deferred modules that have no non-deferred dependencies actually get * loaded, since if we defer them and then never pull in a non-deferred * script, then `goog.loadQueuedModules_` will never be called. Instead, * if not waiting on anything we simply don't defer in the first place. * @private {boolean} */ goog.oldIeWaiting_ = false; /** * 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); } } goog.oldIeWaiting_ = false; }; /** * 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] || {}; var languageLevel = loadFlags['lang'] || 'es3'; if (path && (loadFlags['module'] == 'goog' || goog.needsTranspile_(languageLevel))) { 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 new 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 { goog.oldIeWaiting_ = true; 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">' + goog.protectScriptTag_(opt_sourceText) + '</' + 'script>'); } return true; } else { return false; } }; /** * Rewrites closing script tags in input to avoid ending an enclosing script * tag. * * @param {string} str * @return {string} * @private */ goog.protectScriptTag_ = function(str) { return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1'); }; /** * 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.requiresTranspilation_) { goog.requiresTranspilation_ = goog.createRequiresTranspilation_(); } if (lang in goog.requiresTranspilation_) { return goog.requiresTranspilation_[lang]; } else { throw new Error('Unknown language mode: ' + lang); } }; /** @private {?Object<string, boolean>} */ goog.requiresTranspilation_ = null; /** @private {number} */ goog.lastNonModuleScriptIndex_ = 0; /** * A readystatechange handler for legacy IE * @param {?} 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 new 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 languageLevel = loadFlags['lang'] || 'es3'; var needsTranspile = goog.needsTranspile_(languageLevel); 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 new 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'); } } /** * @package {?boolean} * Visible for testing. */ goog.hasBadLetScoping = null; /** * @return {boolean} * @package Visible for testing. */ goog.useSafari10Workaround = function() { if (goog.hasBadLetScoping == null) { var hasBadLetScoping; try { hasBadLetScoping = !eval( '"use strict";' + 'let x = 1; function f() { return typeof x; };' + 'f() == "number";'); } catch (e) { // Assume that ES6 syntax isn't supported. hasBadLetScoping = false; } goog.hasBadLetScoping = hasBadLetScoping; } return goog.hasBadLetScoping; }; /** * @param {string} moduleDef * @return {string} * @package Visible for testing. */ goog.workaroundSafari10EvalBug = function(moduleDef) { return '(function(){' + moduleDef + '\n' + // Terminate any trailing single line comment. ';' + // Terminate any trailing expression. '})();\n'; }; /** * @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)) { if (goog.useSafari10Workaround()) { moduleDef = goog.workaroundSafari10EvalBug(moduleDef); } exports = goog.loadModuleFromSource_.call(undefined, moduleDef); } else { throw new Error('Invalid module definition'); } var moduleName = goog.moduleLoaderState_.moduleName; if (!goog.isString(moduleName) || !moduleName) { throw new 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 && typeof exports == 'object' && exports != null) { Object.seal(exports); } goog.loadedModules_[moduleName] = exports; } finally { goog.moduleLoaderState_ = previousState; } }; /** * @private @const */ goog.loadModuleFromSource_ = /** @type {function(string):?} */ (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('/'); }; /** * Provides a hook for loading a file when using Closure's goog.require() API * with goog.modules. In particular this hook is provided to support Node.js. * * @type {(function(string):string)|undefined} */ goog.global.CLOSURE_LOAD_FILE_SYNC; /** * 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.oldIeWaiting_) { 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); // Even though the transpiler is optional, if $gwtExport is found, it's // a sign the transpiler was loaded and the $jscomp.transpile *should* // be there. if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] && !goog.global['$gwtExport']['$jscomp']['transpile']) { throw new Error( 'The transpiler did not properly export the "transpile" ' + 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport'])); } // transpile.js only exports a single $jscomp function, transpile. We // grab just that and add it to the existing definition of $jscomp which // contains the polyfills. goog.global['$jscomp'].transpile = goog.global['$gwtExport']['$jscomp']['transpile']; 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 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_); } 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(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 * @private */ 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(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 * @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 new 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_; /** * A hook for modifying the default behavior goog.getCssName. The function * if present, will recieve the standard output of the goog.getCssName as * its input. * * @type {(function(string):string)|undefined} */ goog.global.CLOSURE_CSS_NAME_MAP_FN; /** * 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) { // String() is used for compatibility with compiled soy where the passed // className can be non-string objects. if (String(className).charAt(0) == '.') { throw new Error( 'className passed in goog.getCssName must not start with ".".' + ' You passed: ' + className); } 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; }; } var result = opt_modifier ? className + '-' + rename(opt_modifier) : rename(className); // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further // processing of the class name. if (goog.global.CLOSURE_CSS_NAME_MAP_FN) { return goog.global.CLOSURE_CSS_NAME_MAP_FN(result); } return result; }; /** * 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. * @deprecated goog.base is not strict mode compatible. Prefer the static * "base" method added to the constructor by goog.inherits * or ES6 classes and the "super" keyword. */ goog.base = function(me, opt_methodName, var_args) { var caller = arguments.callee.caller; if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) { throw new 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 new 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 new 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 new 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) * }} */ 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. * @return {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'; /** * Returns a newly created map from language mode string to a boolean * indicating whether transpilation should be done for that mode. * * Guaranteed invariant: * For any two modes, l1 and l2 where l2 is a newer mode than l1, * `map[l1] == true` implies that `map[l2] == true`. * @private * @return {!Object<string, boolean>} */ goog.createRequiresTranspilation_ = function() { var /** !Object<string, boolean> */ requiresTranspilation = {'es3': false}; var transpilationRequiredForAllLaterModes = false; /** * Adds an entry to requiresTranspliation for the given language mode. * * IMPORTANT: Calls must be made in order from oldest to newest language * mode. * @param {string} modeName * @param {function(): boolean} isSupported Returns true if the JS engine * supports the given mode. */ function addNewerLanguageTranspilationCheck(modeName, isSupported) { if (transpilationRequiredForAllLaterModes) { requiresTranspilation[modeName] = true; } else if (isSupported()) { requiresTranspilation[modeName] = false; } else { requiresTranspilation[modeName] = true; transpilationRequiredForAllLaterModes = true; } } /** * Does the given code evaluate without syntax errors and return a truthy * result? */ function /** boolean */ evalCheck(/** string */ code) { try { return !!eval(code); } catch (ignored) { return false; } } var userAgent = goog.global.navigator && goog.global.navigator.userAgent ? goog.global.navigator.userAgent : ''; // Identify ES3-only browsers by their incorrect treatment of commas. addNewerLanguageTranspilationCheck('es5', function() { return evalCheck('[1,].length==1'); }); addNewerLanguageTranspilationCheck('es6', function() { // Edge has a non-deterministic (i.e., not reproducible) bug with ES6: // https://github.com/Microsoft/ChakraCore/issues/1496. var re = /Edge\/(\d+)(\.\d)*/i; var edgeUserAgent = userAgent.match(re); if (edgeUserAgent && Number(edgeUserAgent[1]) < 15) { return false; } // Test 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}'; return evalCheck('(()=>{"use strict";' + es6fullTest + '})()'); }); // TODO(joeltine): Remove es6-impl references for b/31340605. // Consider es6-impl (widely-implemented es6 features) to be supported // whenever es6 is supported. Technically es6-impl is a lower level of // support than es6, but we don't have tests specifically for it. addNewerLanguageTranspilationCheck('es6-impl', function() { return true; }); // ** and **= are the only new features in 'es7' addNewerLanguageTranspilationCheck('es7', function() { return evalCheck('2 ** 2 == 4'); }); // async functions are the only new features in 'es8' addNewerLanguageTranspilationCheck('es8', function() { return evalCheck('async () => 1, true'); }); return requiresTranspilation; }; goog.provide('ol.array'); /** * 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) { while (begin < end) { var tmp = arr[begin]; arr[begin] = arr[end]; arr[end] = tmp; ++begin; --end; } }; /** * @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'); /** * 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} 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/openlayers/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 {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 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 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 {boolean} Include debuggable shader sources. Default is `true`. * This should be set to `false` for production builds (if `ol.ENABLE_WEBGL` * is `true`). */ ol.DEBUG_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.TileState.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) { var path = ol.VERSION ? ol.VERSION.split('-')[0] : 'latest'; /** * @type {string} */ this.message = 'Assertion failed. See https://openlayers.org/en/' + path + '/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.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.math'); 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; 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.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 */ 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.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.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 */ 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) { 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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; }; /** * Get the size of an extent. * @param {ol.Extent} extent Extent. * @return {number} Area. * @api */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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.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.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' }; /** * @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'); goog.require('ol.geom.GeometryType'); /** * @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](https://trs-new.jpl.nasa.gov/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) { return ol.Sphere.getArea_(coordinates, this.radius); }; /** * 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) { return ol.Sphere.getDistance_(c1, c2, this.radius); }; /** * 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)]; }; /** * The mean Earth radius (1/3 * (2a + b)) for the WGS84 ellipsoid. * https://en.wikipedia.org/wiki/Earth_radius#Mean_radius * @type {number} */ ol.Sphere.DEFAULT_RADIUS = 6371008.8; /** * Get the spherical length of a geometry. This length is the sum of the * great circle distances between coordinates. For polygons, the length is * the sum of all rings. For points, the length is zero. For multi-part * geometries, the length is the sum of the length of each part. * @param {ol.geom.Geometry} geometry A geometry. * @param {olx.SphereMetricOptions=} opt_options Options for the length * calculation. By default, geometries are assumed to be in 'EPSG:3857'. * You can change this by providing a `projection` option. * @return {number} The spherical length (in meters). * @api */ ol.Sphere.getLength = function(geometry, opt_options) { var options = opt_options || {}; var radius = options.radius || ol.Sphere.DEFAULT_RADIUS; var projection = options.projection || 'EPSG:3857'; geometry = geometry.clone().transform(projection, 'EPSG:4326'); var type = geometry.getType(); var length = 0; var coordinates, coords, i, ii, j, jj; switch (type) { case ol.geom.GeometryType.POINT: case ol.geom.GeometryType.MULTI_POINT: { break; } case ol.geom.GeometryType.LINE_STRING: case ol.geom.GeometryType.LINEAR_RING: { coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates(); length = ol.Sphere.getLength_(coordinates, radius); break; } case ol.geom.GeometryType.MULTI_LINE_STRING: case ol.geom.GeometryType.POLYGON: { coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates(); for (i = 0, ii = coordinates.length; i < ii; ++i) { length += ol.Sphere.getLength_(coordinates[i], radius); } break; } case ol.geom.GeometryType.MULTI_POLYGON: { coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates(); for (i = 0, ii = coordinates.length; i < ii; ++i) { coords = coordinates[i]; for (j = 0, jj = coords.length; j < jj; ++j) { length += ol.Sphere.getLength_(coords[j], radius); } } break; } case ol.geom.GeometryType.GEOMETRY_COLLECTION: { var geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries(); for (i = 0, ii = geometries.length; i < ii; ++i) { length += ol.Sphere.getLength(geometries[i], opt_options); } break; } default: { throw new Error('Unsupported geometry type: ' + type); } } return length; }; /** * Get the cumulative great circle length of linestring coordinates (geographic). * @param {Array} coordinates Linestring coordinates. * @param {number} radius The sphere radius to use. * @return {number} The length (in meters). */ ol.Sphere.getLength_ = function(coordinates, radius) { var length = 0; for (var i = 0, ii = coordinates.length; i < ii - 1; ++i) { length += ol.Sphere.getDistance_(coordinates[i], coordinates[i + 1], radius); } return length; }; /** * Get the great circle distance between two geographic coordinates. * @param {Array} c1 Starting coordinate. * @param {Array} c2 Ending coordinate. * @param {number} radius The sphere radius to use. * @return {number} The great circle distance between the points (in meters). */ ol.Sphere.getDistance_ = function(c1, c2, radius) { 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 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); }; /** * Get the spherical area of a geometry. This is the area (in meters) assuming * that polygon edges are segments of great circles on a sphere. * @param {ol.geom.Geometry} geometry A geometry. * @param {olx.SphereMetricOptions=} opt_options Options for the area * calculation. By default, geometries are assumed to be in 'EPSG:3857'. * You can change this by providing a `projection` option. * @return {number} The spherical area (in square meters). * @api */ ol.Sphere.getArea = function(geometry, opt_options) { var options = opt_options || {}; var radius = options.radius || ol.Sphere.DEFAULT_RADIUS; var projection = options.projection || 'EPSG:3857'; geometry = geometry.clone().transform(projection, 'EPSG:4326'); var type = geometry.getType(); var area = 0; var coordinates, coords, i, ii, j, jj; switch (type) { case ol.geom.GeometryType.POINT: case ol.geom.GeometryType.MULTI_POINT: case ol.geom.GeometryType.LINE_STRING: case ol.geom.GeometryType.MULTI_LINE_STRING: case ol.geom.GeometryType.LINEAR_RING: { break; } case ol.geom.GeometryType.POLYGON: { coordinates = /** @type {ol.geom.Polygon} */ (geometry).getCoordinates(); area = Math.abs(ol.Sphere.getArea_(coordinates[0], radius)); for (i = 1, ii = coordinates.length; i < ii; ++i) { area -= Math.abs(ol.Sphere.getArea_(coordinates[i], radius)); } break; } case ol.geom.GeometryType.MULTI_POLYGON: { coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates(); for (i = 0, ii = coordinates.length; i < ii; ++i) { coords = coordinates[i]; area += Math.abs(ol.Sphere.getArea_(coords[0], radius)); for (j = 1, jj = coords.length; j < jj; ++j) { area -= Math.abs(ol.Sphere.getArea_(coords[j], radius)); } } break; } case ol.geom.GeometryType.GEOMETRY_COLLECTION: { var geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries(); for (i = 0, ii = geometries.length; i < ii; ++i) { area += ol.Sphere.getArea(geometries[i], opt_options); } break; } default: { throw new Error('Unsupported geometry type: ' + type); } } return area; }; /** * Returns the spherical area for a list of coordinates. * * [Reference](https://trs-new.jpl.nasa.gov/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. * @param {number} radius The sphere radius. * @return {number} Area (in square meters). */ ol.Sphere.getArea_ = function(coordinates, radius) { 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 * radius * radius / 2.0; }; goog.provide('ol.proj.Units'); /** * 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 */ ol.proj.Units.METERS_PER_UNIT = {}; // use the radius of the Normal sphere ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.DEGREES] = 2 * Math.PI * 6370997 / 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 */ ol.proj.Projection = function(options) { /** * @private * @type {string} */ this.code_ = options.code; /** * Units of projected coordinates. When set to `ol.proj.Units.TILE_PIXELS`, a * `this.extent_` and `this.worldExtent_` must be configured properly for each * tile. * @private * @type {ol.proj.Units} */ this.units_ = /** @type {ol.proj.Units} */ (options.units); /** * Validity extent of the projection in projected coordinates. For projections * with `ol.proj.Units.TILE_PIXELS` units, this is the extent of the tile in * tile pixel space. * @private * @type {ol.Extent} */ this.extent_ = options.extent !== undefined ? options.extent : null; /** * Extent of the world in EPSG:4326. For projections with * `ol.proj.Units.TILE_PIXELS` units, this is the extent of the tile in * projected coordinate space. * @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; 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 */ ol.proj.Projection.prototype.getCode = function() { return this.code_; }; /** * Get the validity extent for this projection. * @return {ol.Extent} Extent. * @api */ ol.proj.Projection.prototype.getExtent = function() { return this.extent_; }; /** * Get the units of this projection. * @return {ol.proj.Units} Units. * @api */ 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 */ 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. * @api */ 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 */ 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 */ 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 */ 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 (see {@link ol.proj#getPointResolution} * 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.EPSG3857'); goog.require('ol'); goog.require('ol.math'); 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.Projection_ = 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.Projection_, ol.proj.Projection); /** * Radius of WGS84 sphere * * @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]; /** * Projections equal to EPSG:3857. * * @const * @type {Array.<ol.proj.Projection>} */ ol.proj.EPSG3857.PROJECTIONS = [ new ol.proj.EPSG3857.Projection_('EPSG:3857'), new ol.proj.EPSG3857.Projection_('EPSG:102100'), new ol.proj.EPSG3857.Projection_('EPSG:102113'), new ol.proj.EPSG3857.Projection_('EPSG:900913'), new ol.proj.EPSG3857.Projection_('urn:ogc:def:crs:EPSG:6.18:3:3857'), new ol.proj.EPSG3857.Projection_('urn:ogc:def:crs:EPSG::3857'), new ol.proj.EPSG3857.Projection_('http://www.opengis.net/gml/srs/epsg.xml#3857') ]; /** * 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); } } 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); } } 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.proj.EPSG4326'); goog.require('ol'); goog.require('ol.proj.Projection'); goog.require('ol.proj.Units'); /** * @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.Projection_ = 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.Projection_, ol.proj.Projection); /** * Radius of WGS84 sphere * * @const * @type {number} */ ol.proj.EPSG4326.RADIUS = 6378137; /** * 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.proj.EPSG4326.RADIUS / 180; /** * Projections equal to EPSG:4326. * * @const * @type {Array.<ol.proj.Projection>} */ ol.proj.EPSG4326.PROJECTIONS = [ new ol.proj.EPSG4326.Projection_('CRS:84'), new ol.proj.EPSG4326.Projection_('EPSG:4326', 'neu'), new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:EPSG::4326', 'neu'), new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'), new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:OGC:1.3:CRS84'), new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:OGC:2:84'), new ol.proj.EPSG4326.Projection_('http://www.opengis.net/gml/srs/epsg.xml#4326', 'neu'), new ol.proj.EPSG4326.Projection_('urn:x-ogc:def:crs:EPSG:4326', 'neu') ]; 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.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_; 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.Sphere'); goog.require('ol.extent'); goog.require('ol.math'); goog.require('ol.proj.EPSG3857'); goog.require('ol.proj.EPSG4326'); 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'); /** * Meters per unit lookup table. * @const * @type {Object.<ol.proj.Units, number>} * @api */ ol.proj.METERS_PER_UNIT = ol.proj.Units.METERS_PER_UNIT; /** * A place to store the mean radius of the Earth. * @private * @type {ol.Sphere} */ ol.proj.SPHERE_ = new ol.Sphere(ol.Sphere.DEFAULT_RADIUS); 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.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 * by default 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. * A custom function can be provided for a specific projection, either * by setting the `getPointResolution` option in the * {@link ol.proj.Projection} constructor or by using * {@link ol.proj.Projection#setGetPointResolution} to change an existing * projection object. * @param {ol.ProjectionLike} projection The projection. * @param {number} resolution Nominal resolution in projection units. * @param {ol.Coordinate} point Point to find adjusted resolution at. * @param {ol.proj.Units=} opt_units Units to get the point resolution in. * Default is the projection's units. * @return {number} Point resolution. * @api */ ol.proj.getPointResolution = function(projection, resolution, point, opt_units) { projection = ol.proj.get(projection); var pointResolution; var getter = projection.getPointResolutionFunc(); if (getter) { pointResolution = getter(resolution, point); } else { var units = projection.getUnits(); if (units == ol.proj.Units.DEGREES && !opt_units || opt_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.proj.SPHERE_.haversineDistance( vertices.slice(0, 2), vertices.slice(2, 4)); var height = ol.proj.SPHERE_.haversineDistance( vertices.slice(4, 6), vertices.slice(6, 8)); pointResolution = (width + height) / 2; var metersPerUnit = opt_units ? ol.proj.Units.METERS_PER_UNIT[opt_units] : 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 */ 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) { projections.forEach(ol.proj.addProjection); }; /** * 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 */ 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 */ 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 */ ol.proj.toLonLat = function(coordinate, opt_projection) { var lonLat = ol.proj.transform(coordinate, opt_projection !== undefined ? opt_projection : 'EPSG:3857', 'EPSG:4326'); var lon = lonLat[0]; if (lon < -180 || lon > 180) { lonLat[0] = ol.math.modulo(lon + 180, 360) - 180; } return lonLat; }; /** * 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 */ 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 && !projection) { var proj4js = ol.proj.proj4.get(); if (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 */ 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) { 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) { 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 */ 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 */ 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); }; /** * Add transforms to and from EPSG:4326 and EPSG:3857. This function is called * by when this module is executed and should only need to be called again after * `ol.proj.clearAllProjections()` is called (e.g. in tests). */ ol.proj.addCommon = 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); }; ol.proj.addCommon(); 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; }; /** * Get the key for a tile coord. * @param {ol.TileCoord} tileCoord The tile coord. * @return {string} Key. */ ol.tilecoord.getKey = function(tileCoord) { return ol.tilecoord.getKeyZXY(tileCoord[0], tileCoord[1], tileCoord[2]); }; /** * Get a tile coord given a key. * @param {string} key The tile coord key. * @return {ol.TileCoord} The tile coord. */ ol.tilecoord.fromKey = function(key) { return key.split('/').map(Number); }; /** * @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 */ 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 // check if we've got a consistent zoom factor and origin var zoomFactor; if (!options.origins) { for (var i = 0, ii = this.resolutions_.length - 1; i < ii; ++i) { if (!zoomFactor) { zoomFactor = this.resolutions_[i] / this.resolutions_[i + 1]; } else { if (this.resolutions_[i] / this.resolutions_[i + 1] !== zoomFactor) { zoomFactor = undefined; break; } } } } /** * @private * @type {number|undefined} */ this.zoomFactor_ = zoomFactor; /** * @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) { this.fullTileRanges_ = options.sizes.map(function(size, z) { 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 Integer 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 tileRange, x, y; var tileCoordExtent = null; var z = tileCoord[0] - 1; if (this.zoomFactor_ === 2) { x = tileCoord[1]; y = tileCoord[2]; } else { tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); } while (z >= this.minZoom) { if (this.zoomFactor_ === 2) { x = Math.floor(x / 2); y = Math.floor(y / 2); tileRange = ol.TileRange.createOrUpdate(x, x, y, y, opt_tileRange); } else { tileRange = this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange); } if (callback.call(opt_this, z, 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 Integer zoom level. * @return {ol.Coordinate} Origin. * @api */ ol.tilegrid.TileGrid.prototype.getOrigin = function(z) { if (this.origin_) { return this.origin_; } else { return this.origins_[z]; } }; /** * Get the resolution for the given zoom level. * @param {number} z Integer zoom level. * @return {number} Resolution. * @api */ ol.tilegrid.TileGrid.prototype.getResolution = function(z) { return this.resolutions_[z]; }; /** * Get the list of resolutions for the tile grid. * @return {Array.<number>} Resolutions. * @api */ 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) { if (this.zoomFactor_ === 2) { var minX = tileCoord[1] * 2; var minY = tileCoord[2] * 2; return ol.TileRange.createOrUpdate(minX, minX + 1, minY, minY + 1, opt_tileRange); } var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); return this.getTileRangeForExtentAndZ( tileCoordExtent, tileCoord[0] + 1, opt_tileRange); } return null; }; /** * Get the extent for a tile range. * @param {number} z Integer zoom level. * @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); }; /** * Get a tile range for the given extent and integer zoom level. * @param {ol.Extent} extent Extent. * @param {number} z Integer zoom level. * @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 tileCoord = ol.tilegrid.TileGrid.tmpTileCoord_; this.getTileCoordForXYAndZ_(extent[0], extent[1], z, false, tileCoord); var minX = tileCoord[1]; var minY = tileCoord[2]; this.getTileCoordForXYAndZ_(extent[2], extent[3], z, true, tileCoord); return ol.TileRange.createOrUpdate( minX, tileCoord[1], minY, tileCoord[2], 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); }; /** * Note that this method should not be called for resolutions that correspond * to an integer zoom level. Instead call the `getTileCoordForXYAndZ_` method. * @param {number} x X. * @param {number} y Y. * @param {number} resolution Resolution (for a non-integer zoom level). * @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); }; /** * Although there is repetition between this method and `getTileCoordForXYAndResolution_`, * they should have separate implementations. This method is for integer zoom * levels. The other method should only be called for resolutions corresponding * to non-integer zoom levels. * @param {number} x Map x coordinate. * @param {number} y Map y coordinate. * @param {number} z Integer zoom level. * @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.getTileCoordForXYAndZ_ = function(x, y, z, reverseIntersectionPolicy, opt_tileCoord) { var origin = this.getOrigin(z); var resolution = this.getResolution(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 = xFromOrigin / tileSize[0]; var tileCoordY = 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) { return this.getTileCoordForXYAndZ_( coordinate[0], coordinate[1], z, false, opt_tileCoord); }; /** * @param {ol.TileCoord} tileCoord Tile coordinate. * @return {number} Tile resolution. */ ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) { 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 */ ol.tilegrid.TileGrid.prototype.getTileSize = function(z) { if (this.tileSize_) { return this.tileSize_; } else { 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 { 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 {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.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 © ' + * '<a href="https://www.opencyclemap.org/">OpenCycleMap</a>' * }), * ol.source.OSM.ATTRIBUTION * ], * .. * * @constructor * @deprecated This class is deprecated and will removed in the next major release. * @param {olx.AttributionOptions} options Attribution options. * @struct * @api */ 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 */ 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; }; goog.provide('ol.CollectionEventType'); /** * @enum {string} */ ol.CollectionEventType = { /** * Triggered when an item is added to the collection. * @event ol.Collection.Event#add * @api */ ADD: 'add', /** * Triggered when an item is removed from the collection. * @event ol.Collection.Event#remove * @api */ REMOVE: 'remove' }; goog.provide('ol.ObjectEventType'); /** * @enum {string} */ ol.ObjectEventType = { /** * Triggered when a property is changed. * @event ol.Object.Event#propertychange * @api */ PROPERTYCHANGE: 'propertychange' }; 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 */ this.type = type; /** * The event target. * @type {Object} * @api */ this.target = null; }; /** * Stop event propagation. * @function * @override * @api */ ol.events.Event.prototype.preventDefault = /** * Stop event propagation. * @function * @override * @api */ 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); 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', CLEAR: 'clear', 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 */ 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 */ 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 */ 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 */ 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 */ 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); } }; goog.provide('ol.Object'); goog.require('ol'); goog.require('ol.ObjectEventType'); 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 */ 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 */ ol.Object.prototype.getKeys = function() { return Object.keys(this.values_); }; /** * Get an object of all property names and values. * @return {Object.<string, *>} Object. * @api */ 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.ObjectEventType.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 */ 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 */ 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 */ 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); } } }; /** * @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 */ 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 */ this.oldValue = oldValue; }; ol.inherits(ol.Object.Event, ol.events.Event); /** * 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.AssertionError'); goog.require('ol.CollectionEventType'); goog.require('ol.Object'); goog.require('ol.events.Event'); /** * @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. * @param {olx.CollectionOptions=} opt_options Collection options. * @template T * @api */ ol.Collection = function(opt_array, opt_options) { ol.Object.call(this); var options = opt_options || {}; /** * @private * @type {boolean} */ this.unique_ = !!options.unique; /** * @private * @type {!Array.<T>} */ this.array_ = opt_array ? opt_array : []; if (this.unique_) { for (var i = 0, ii = this.array_.length; i < ii; ++i) { this.assertUnique_(this.array_[i], i); } } this.updateLength_(); }; ol.inherits(ol.Collection, ol.Object); /** * Remove all elements from the collection. * @api */ 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 */ 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 */ ol.Collection.prototype.forEach = function(f, opt_this) { var fn = (opt_this) ? f.bind(opt_this) : f; var array = this.array_; for (var i = 0, ii = array.length; i < ii; ++i) { fn(array[i], i, array); } }; /** * 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 */ ol.Collection.prototype.getArray = function() { return this.array_; }; /** * Get the element at the provided index. * @param {number} index Index. * @return {T} Element. * @api */ 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 */ 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 */ ol.Collection.prototype.insertAt = function(index, elem) { if (this.unique_) { this.assertUnique_(elem); } this.array_.splice(index, 0, elem); this.updateLength_(); this.dispatchEvent( new ol.Collection.Event(ol.CollectionEventType.ADD, elem)); }; /** * Remove the last element of the collection and return it. * Return `undefined` if the collection is empty. * @return {T|undefined} Element. * @api */ 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 */ ol.Collection.prototype.push = function(elem) { if (this.unique_) { this.assertUnique_(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 */ 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 */ 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.CollectionEventType.REMOVE, prev)); return prev; }; /** * Set the element at the provided index. * @param {number} index Index. * @param {T} elem Element. * @api */ ol.Collection.prototype.setAt = function(index, elem) { var n = this.getLength(); if (index < n) { if (this.unique_) { this.assertUnique_(elem, index); } var prev = this.array_[index]; this.array_[index] = elem; this.dispatchEvent( new ol.Collection.Event(ol.CollectionEventType.REMOVE, prev)); this.dispatchEvent( new ol.Collection.Event(ol.CollectionEventType.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); }; /** * @private * @param {T} elem Element. * @param {number=} opt_except Optional index to ignore. */ ol.Collection.prototype.assertUnique_ = function(elem, opt_except) { for (var i = 0, ii = this.array_.length; i < ii; ++i) { if (this.array_[i] === elem && i !== opt_except) { throw new ol.AssertionError(58); } } }; /** * @enum {string} * @private */ ol.Collection.Property_ = { LENGTH: 'length' }; /** * @classdesc * Events emitted by {@link ol.Collection} instances are instances of this * type. * * @constructor * @extends {ol.events.Event} * @implements {oli.Collection.Event} * @param {ol.CollectionEventType} 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 */ this.element = opt_element; }; ol.inherits(ol.Collection.Event, ol.events.Event); 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.PluggableMap} 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.PluggableMap} * @api */ 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); goog.provide('ol.MapBrowserEvent'); goog.require('ol'); goog.require('ol.MapEvent'); /** * @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.PluggableMap} 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 */ this.originalEvent = browserEvent; /** * The map pixel relative to the viewport corresponding to the original browser event. * @type {ol.Pixel} * @api */ this.pixel = map.getEventPixel(browserEvent); /** * The coordinate in view projection corresponding to the original browser event. * @type {ol.Coordinate} * @api */ 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 */ 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 */ 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 */ ol.MapBrowserEvent.prototype.stopPropagation = function() { ol.MapEvent.prototype.stopPropagation.call(this); this.originalEvent.stopPropagation(); }; 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 */ 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 */ 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 */ ol.has.DEVICE_ORIENTATION = 'DeviceOrientationEvent' in window; /** * Is HTML5 geolocation supported in the current browser? * @const * @type {boolean} * @api */ ol.has.GEOLOCATION = 'geolocation' in navigator; /** * True if browser supports touch events. * @const * @type {boolean} * @api */ 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 */ 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.MapBrowserEventType'); goog.require('ol.events.EventType'); /** * Constants for event names. * @enum {string} */ ol.MapBrowserEventType = { /** * 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 */ SINGLECLICK: 'singleclick', /** * A click with no dragging. A double click will fire two of this. * @event ol.MapBrowserEvent#click * @api */ CLICK: ol.events.EventType.CLICK, /** * A true double click, with no dragging. * @event ol.MapBrowserEvent#dblclick * @api */ 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 */ 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.PluggableMap} 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.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 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.has'); goog.require('ol.MapBrowserEventType'); 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.PluggableMap} map The map with the viewport to listen to events on. * @param {number|undefined} moveTolerance The minimal distance the pointer must travel to trigger a move. * @constructor * @extends {ol.events.EventTarget} */ ol.MapBrowserEventHandler = function(map, moveTolerance) { ol.events.EventTarget.call(this); /** * This is the element that we will listen to the real events on. * @type {ol.PluggableMap} * @private */ this.map_ = map; /** * @type {number} * @private */ this.clickTimeoutId_ = 0; /** * @type {boolean} * @private */ this.dragging_ = false; /** * @type {!Array.<ol.EventsKey>} * @private */ this.dragListenerKeys_ = []; /** * @type {number} * @private */ this.moveTolerance_ = moveTolerance ? moveTolerance * ol.has.DEVICE_PIXEL_RATIO : ol.has.DEVICE_PIXEL_RATIO; /** * 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.MapBrowserEventType.CLICK, this.map_, pointerEvent); this.dispatchEvent(newEvent); if (this.clickTimeoutId_ !== 0) { // double-click clearTimeout(this.clickTimeoutId_); this.clickTimeoutId_ = 0; newEvent = new ol.MapBrowserPointerEvent( ol.MapBrowserEventType.DBLCLICK, this.map_, pointerEvent); this.dispatchEvent(newEvent); } else { // click this.clickTimeoutId_ = setTimeout(function() { this.clickTimeoutId_ = 0; var newEvent = new ol.MapBrowserPointerEvent( ol.MapBrowserEventType.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.MapBrowserEventType.POINTERUP || event.type == ol.MapBrowserEventType.POINTERCANCEL) { delete this.trackedTouches_[event.pointerId]; } else if (event.type == ol.MapBrowserEventType.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.MapBrowserEventType.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 // We only fire click, singleclick, and doubleclick if nobody has called // event.stopPropagation() or event.preventDefault(). if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) { this.emulateClick_(this.down_); } 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.MapBrowserEventType.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.MapBrowserEventType.POINTERMOVE, this.handlePointerMove_, this), ol.events.listen(this.documentPointerEventHandler_, ol.MapBrowserEventType.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.MapBrowserEventType.POINTERCANCEL, this.handlePointerUp_, this) ); } }; /** * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. * @private */ ol.MapBrowserEventHandler.prototype.handlePointerMove_ = function(pointerEvent) { // Between pointerdown and pointerup, pointermove events are triggered. // To avoid a 'false' touchmove event to be dispatched, we test if the pointer // moved a significant distance. if (this.isMoving_(pointerEvent)) { this.dragging_ = true; var newEvent = new ol.MapBrowserPointerEvent( ol.MapBrowserEventType.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 Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ || Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_; }; /** * @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.MapEventType'); /** * @enum {string} */ ol.MapEventType = { /** * Triggered after a map frame is rendered. * @event ol.MapEvent#postrender * @api */ POSTRENDER: 'postrender', /** * Triggered when the map starts moving. * @event ol.MapEvent#movestart * @api */ MOVESTART: 'movestart', /** * Triggered after the map is moved. * @event ol.MapEvent#moveend * @api */ MOVEEND: 'moveend' }; goog.provide('ol.MapProperty'); /** * @enum {string} */ ol.MapProperty = { LAYERGROUP: 'layergroup', SIZE: 'size', TARGET: 'target', VIEW: 'view' }; goog.provide('ol.TileState'); /** * @enum {number} */ ol.TileState = { IDLE: 0, LOADING: 1, LOADED: 2, ERROR: 3, EMPTY: 4, ABORT: 5 }; goog.provide('ol.structs.PriorityQueue'); 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; /** * 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_; 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); 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.TileState'); 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.TileState.LOADED || state === ol.TileState.ERROR || state === ol.TileState.EMPTY || state === ol.TileState.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_(); } }; /** * @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 abortedTiles = false; var state, tile, tileKey; while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && this.getCount() > 0) { tile = /** @type {ol.Tile} */ (this.dequeue()[0]); tileKey = tile.getKey(); state = tile.getState(); if (state === ol.TileState.ABORT) { abortedTiles = true; } else if (state === ol.TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) { this.tilesLoadingKeys_[tileKey] = true; ++this.tilesLoading_; ++newLoads; tile.load(); } } if (newLoads === 0 && abortedTiles) { // Do not stop the render loop when all wanted tiles were aborted due to // a small, saturated tile cache. this.tileChangeCallback_(); } }; 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.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.ViewHint'); /** * @enum {number} */ ol.ViewHint = { ANIMATING: 0, INTERACTING: 1 }; goog.provide('ol.ViewProperty'); /** * @enum {string} */ ol.ViewProperty = { CENTER: 'center', RESOLUTION: 'resolution', ROTATION: 'rotation' }; 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 */ 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 circle. * * @param {ol.Coordinate} coordinate The coordinate. * @param {ol.geom.Circle} circle The circle. * @return {ol.Coordinate} Closest point on the circumference */ ol.coordinate.closestOnCircle = function(coordinate, circle) { var r = circle.getRadius(); var center = circle.getCenter(); var x0 = center[0]; var y0 = center[1]; var x1 = coordinate[0]; var y1 = coordinate[1]; var dx = x1 - x0; var dy = y1 - y0; if (dx === 0 && dy === 0) { dx = 1; } var d = Math.sqrt(dx * dx + dy * dy); var x, y; x = x0 + r * dx / d; y = y0 + r * dy / d; return [x, y]; }; /** * 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 */ 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); }); }; /** * @param {string} hemispheres Hemispheres. * @param {number} degrees Degrees. * @param {number=} opt_fractionDigits The number of digits to include * after the decimal point. Default is `0`. * @return {string} String. */ ol.coordinate.degreesToStringHDMS = function(hemispheres, degrees, opt_fractionDigits) { var normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180; var x = Math.abs(3600 * normalizedDegrees); var dflPrecision = opt_fractionDigits || 0; var precision = Math.pow(10, dflPrecision); var deg = Math.floor(x / 3600); var min = Math.floor((x - deg * 3600) / 60); var sec = x - (deg * 3600) - (min * 60); sec = Math.ceil(sec * precision) / precision; if (sec >= 60) { sec = 0; min += 1; } if (min >= 60) { min = 0; deg += 1; } return deg + '\u00b0 ' + ol.string.padNumber(min, 2) + '\u2032 ' + ol.string.padNumber(sec, 2, dflPrecision) + '\u2033' + (normalizedDegrees == 0 ? '' : ' ' + 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 */ 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 */ 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; }; /** * @param {ol.Coordinate} coord1 First coordinate. * @param {ol.Coordinate} coord2 Second coordinate. * @return {number} Distance between coord1 and coord2. */ ol.coordinate.distance = function(coord1, coord2) { return Math.sqrt(ol.coordinate.squaredDistance(coord1, coord2)); }; /** * 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 */ ol.coordinate.toStringHDMS = function(coordinate, opt_fractionDigits) { if (coordinate) { return ol.coordinate.degreesToStringHDMS('NS', coordinate[1], opt_fractionDigits) + ' ' + ol.coordinate.degreesToStringHDMS('EW', coordinate[0], 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 */ 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.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.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; }; 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.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.geom.Geometry'); goog.require('ol'); goog.require('ol.Object'); goog.require('ol.extent'); goog.require('ol.functions'); goog.require('ol.geom.flat.transform'); goog.require('ol.proj'); goog.require('ol.proj.Units'); goog.require('ol.transform'); /** * @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 * @abstract * @extends {ol.Object} * @api */ 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; /** * @private * @type {ol.Transform} */ this.tmpTransform_ = ol.transform.create(); }; 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 */ 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 */ 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 */ ol.geom.Geometry.prototype.transform = function(source, destination) { var tmpTransform = this.tmpTransform_; source = ol.proj.get(source); var transformFn = source.getUnits() == ol.proj.Units.TILE_PIXELS ? function(inCoordinates, outCoordinates, stride) { var pixelExtent = source.getExtent(); var projectedExtent = source.getWorldExtent(); var scale = ol.extent.getHeight(projectedExtent) / ol.extent.getHeight(pixelExtent); ol.transform.compose(tmpTransform, projectedExtent[0], projectedExtent[3], scale, -scale, 0, 0, 0); ol.geom.flat.transform.transform2D(inCoordinates, 0, inCoordinates.length, stride, tmpTransform, outCoordinates); return ol.proj.getTransform(source, destination)(inCoordinates, outCoordinates, stride); } : ol.proj.getTransform(source, destination); this.applyTransform(transformFn); return this; }; 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 * @abstract * @extends {ol.geom.Geometry} * @api */ 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; } 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; } 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 */ 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 */ 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 */ 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 */ 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 */ 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.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; } } 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'); /** * @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) { 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]; 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 */ 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. * @override * @api */ 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 */ 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. * @override * @api */ 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 */ ol.geom.LinearRing.prototype.getType = function() { return ol.geom.GeometryType.LINEAR_RING; }; /** * @inheritDoc */ ol.geom.LinearRing.prototype.intersectsExtent = function(extent) {}; /** * Set the coordinates of the linear ring. * @param {Array.<ol.Coordinate>} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. * @override * @api */ 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 */ 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. * @override * @api */ 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. * @override * @api */ 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 */ ol.geom.Point.prototype.getType = function() { return ol.geom.GeometryType.POINT; }; /** * @inheritDoc * @api */ ol.geom.Point.prototype.intersectsExtent = function(extent) { return ol.extent.containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]); }; /** * @inheritDoc * @api */ 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.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) { 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) { 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.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 point as XYM coordinate, where M is the * length of the horizontal intersection that the point belongs to. */ 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 for (var r = 0, rr = ends.length; r < rr; ++r) { var end = ends[r]; 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, maxSegmentLength); return opt_dest; } else { return [pointX, y, maxSegmentLength]; } }; /** * @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 as XYM coordinates, where M is the * length of the horizontal intersection that the point belongs to. */ ol.geom.flat.interiorpoint.linearRingss = function(flatCoordinates, offset, endss, stride, flatCenters) { 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.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) { 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) { 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.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 Array of linear * rings that define the polygon. The first linear ring of the array * defines the outer-boundary or surface of the polygon. Each subsequent * linear ring defines a hole in the surface of the polygon. A linear ring * is an array of vertices' coordinates where the first coordinate and the * last are equivalent. * @param {ol.geom.GeometryLayout=} opt_layout Layout. * @api */ 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 */ ol.geom.Polygon.prototype.appendLinearRing = function(linearRing) { 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. * @override * @api */ 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 */ 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. * @override * @api */ 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 as XYM coordinate, where M is the * length of the horizontal intersection that the point belongs to. * @api */ ol.geom.Polygon.prototype.getInteriorPoint = function() { return new ol.geom.Point(this.getFlatInteriorPoint(), ol.geom.GeometryLayout.XYM); }; /** * 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 */ ol.geom.Polygon.prototype.getLinearRing = function(index) { 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 */ 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 */ ol.geom.Polygon.prototype.getType = function() { return ol.geom.GeometryType.POLYGON; }; /** * @inheritDoc * @api */ 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. * @override * @api */ 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) { 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 */ 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(); 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.Object'); goog.require('ol.ResolutionConstraint'); goog.require('ol.RotationConstraint'); goog.require('ol.ViewHint'); goog.require('ol.ViewProperty'); goog.require('ol.array'); goog.require('ol.asserts'); goog.require('ol.coordinate'); goog.require('ol.easing'); goog.require('ol.extent'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Polygon'); goog.require('ol.geom.SimpleGeometry'); goog.require('ol.math'); goog.require('ol.obj'); 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 */ ol.View = function(opt_options) { ol.Object.call(this); var options = ol.obj.assign({}, 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); /** * @private * @const * @type {ol.proj.Projection} */ this.projection_ = ol.proj.createProjection(options.projection, 'EPSG:3857'); this.applyOptions_(options); }; ol.inherits(ol.View, ol.Object); /** * Set up the view with the given options. * @param {olx.ViewOptions} options View options. */ ol.View.prototype.applyOptions_ = function(options) { /** * @type {Object.<string, *>} */ var properties = {}; properties[ol.ViewProperty.CENTER] = options.center !== undefined ? options.center : null; 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_ = { center: centerConstraint, resolution: resolutionConstraint, rotation: rotationConstraint }; if (options.resolution !== undefined) { properties[ol.ViewProperty.RESOLUTION] = options.resolution; } else if (options.zoom !== undefined) { properties[ol.ViewProperty.RESOLUTION] = this.constrainResolution( this.maxResolution_, options.zoom - this.minZoom_); if (this.resolutions_) { // in case map zoom is out of min/max zoom range properties[ol.ViewProperty.RESOLUTION] = ol.math.clamp( Number(this.getResolution() || properties[ol.ViewProperty.RESOLUTION]), this.minResolution_, this.maxResolution_); } } properties[ol.ViewProperty.ROTATION] = options.rotation !== undefined ? options.rotation : 0; this.setProperties(properties); /** * @private * @type {olx.ViewOptions} */ this.options_ = options; }; /** * Get an updated version of the view options used to construct the view. The * current resolution (or zoom), center, and rotation are applied to any stored * options. The provided options can be uesd to apply new min/max zoom or * resolution limits. * @param {olx.ViewOptions} newOptions New options to be applied. * @return {olx.ViewOptions} New options updated with the current view state. */ ol.View.prototype.getUpdatedOptions_ = function(newOptions) { var options = ol.obj.assign({}, this.options_); // preserve resolution (or zoom) if (options.resolution !== undefined) { options.resolution = this.getResolution(); } else { options.zoom = this.getZoom(); } // preserve center options.center = this.getCenter(); // preserve rotation options.rotation = this.getRotation(); return ol.obj.assign({}, options, newOptions); }; /** * 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 animationCount = arguments.length; var callback; if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') { callback = arguments[animationCount - 1]; --animationCount; } if (!this.isDef()) { // if view properties are not yet set, shortcut to the final state var state = arguments[animationCount - 1]; if (state.center) { this.setCenter(state.center); } if (state.zoom !== undefined) { this.setZoom(state.zoom); } if (state.rotation !== undefined) { this.setRotation(state.rotation); } if (callback) { callback(true); } return; } var start = Date.now(); var center = this.getCenter().slice(); var resolution = this.getResolution(); var rotation = this.getRotation(); 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; var delta = ol.math.modulo(options.rotation - rotation + Math.PI, 2 * Math.PI) - Math.PI; animation.targetRotation = rotation + delta; rotation = animation.targetRotation; } animation.callback = callback; // check if animation is a no-op if (ol.View.isNoopAnimation(animation)) { animation.complete = true; // we still push it onto the series for callback handling } else { start += animation.duration; } series.push(animation); } this.animations_.push(series); this.setHint(ol.ViewHint.ANIMATING, 1); this.updateAnimations_(); }; /** * Determine if the view is being animated. * @return {boolean} The view is being animated. * @api */ ol.View.prototype.getAnimating = function() { return this.hints_[ol.ViewHint.ANIMATING] > 0; }; /** * Determine if the user is interacting with the view, such as panning or zooming. * @return {boolean} The view is being interacted with. * @api */ ol.View.prototype.getInteracting = function() { return this.hints_[ol.ViewHint.INTERACTING] > 0; }; /** * Cancel any ongoing animations. * @api */ ol.View.prototype.cancelAnimations = function() { this.setHint(ol.ViewHint.ANIMATING, -this.hints_[ol.ViewHint.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.ViewProperty.CENTER, [x, y]); } if (animation.sourceResolution && animation.targetResolution) { var resolution = progress === 1 ? animation.targetResolution : animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); if (animation.anchor) { this.set(ol.ViewProperty.CENTER, this.calculateCenterZoom(resolution, animation.anchor)); } this.set(ol.ViewProperty.RESOLUTION, resolution); } if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) { var rotation = progress === 1 ? ol.math.modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI : animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation); if (animation.anchor) { this.set(ol.ViewProperty.CENTER, this.calculateCenterRotate(rotation, animation.anchor)); } this.set(ol.ViewProperty.ROTATION, rotation); } more = true; if (!animation.complete) { break; } } if (seriesComplete) { this.animations_[i] = null; this.setHint(ol.ViewHint.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; }; /** * @private * @return {ol.Size} Viewport size or `[100, 100]` when no viewport is found. */ ol.View.prototype.getSizeFromViewport_ = function() { var size = [100, 100]; var selector = '.ol-viewport[data-view="' + ol.getUid(this) + '"]'; var element = document.querySelector(selector); if (element) { var metrics = getComputedStyle(element); size[0] = parseInt(metrics.width, 10); size[1] = parseInt(metrics.height, 10); } return size; }; /** * 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 */ ol.View.prototype.getCenter = function() { return /** @type {ol.Coordinate|undefined} */ ( this.get(ol.ViewProperty.CENTER)); }; /** * @return {ol.Constraints} Constraints. */ ol.View.prototype.getConstraints = function() { return this.constraints_; }; /** * @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=} opt_size Box pixel size. If not provided, the size of the * first map that uses this view will be used. * @return {ol.Extent} Extent. * @api */ ol.View.prototype.calculateExtent = function(opt_size) { var size = opt_size || this.getSizeFromViewport_(); 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 maximum zoom level for the view. * @return {number} The maximum zoom level. * @api */ ol.View.prototype.getMaxZoom = function() { return /** @type {number} */ (this.getZoomForResolution(this.minResolution_)); }; /** * Set a new maximum zoom level for the view. * @param {number} zoom The maximum zoom level. * @api */ ol.View.prototype.setMaxZoom = function(zoom) { this.applyOptions_(this.getUpdatedOptions_({maxZoom: zoom})); }; /** * Get the minimum zoom level for the view. * @return {number} The minimum zoom level. * @api */ ol.View.prototype.getMinZoom = function() { return /** @type {number} */ (this.getZoomForResolution(this.maxResolution_)); }; /** * Set a new minimum zoom level for the view. * @param {number} zoom The minimum zoom level. * @api */ ol.View.prototype.setMinZoom = function(zoom) { this.applyOptions_(this.getUpdatedOptions_({minZoom: zoom})); }; /** * Get the view projection. * @return {ol.proj.Projection} The projection of the view. * @api */ ol.View.prototype.getProjection = function() { return this.projection_; }; /** * Get the view resolution. * @return {number|undefined} The resolution of the view. * @observable * @api */ ol.View.prototype.getResolution = function() { return /** @type {number|undefined} */ ( this.get(ol.ViewProperty.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 */ 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=} opt_size Box pixel size. * @return {number} The resolution at which the provided extent will render at * the given size. * @api */ ol.View.prototype.getResolutionForExtent = function(extent, opt_size) { var size = opt_size || this.getSizeFromViewport_(); 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); return resolution; }); }; /** * Get the view rotation. * @return {number} The rotation of the view in radians. * @observable * @api */ ol.View.prototype.getRotation = function() { return /** @type {number} */ (this.get(ol.ViewProperty.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; return value; }); }; /** * @return {olx.ViewState} View state. */ ol.View.prototype.getState = function() { 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, zoom: this.getZoom() }); }; /** * Get the current zoom level. If you configured your view with a resolutions * array (this is rare), this method may return non-integer zoom levels (so * the zoom level is not safe to use as an index into a resolutions array). * @return {number|undefined} Zoom. * @api */ ol.View.prototype.getZoom = function() { var zoom; var resolution = this.getResolution(); if (resolution !== undefined) { zoom = this.getZoomForResolution(resolution); } return zoom; }; /** * Get the zoom level for a resolution. * @param {number} resolution The resolution. * @return {number|undefined} The zoom level for the provided resolution. * @api */ ol.View.prototype.getZoomForResolution = function(resolution) { var offset = this.minZoom_ || 0; var max, zoomFactor; if (this.resolutions_) { var nearest = ol.array.linearFindNearest(this.resolutions_, resolution, 1); offset = nearest; max = this.resolutions_[nearest]; if (nearest == this.resolutions_.length - 1) { zoomFactor = 2; } else { zoomFactor = max / this.resolutions_[nearest + 1]; } } else { max = this.maxResolution_; zoomFactor = this.zoomFactor_; } return offset + Math.log(max / resolution) / Math.log(zoomFactor); }; /** * Get the resolution for a zoom level. * @param {number} zoom Zoom level. * @return {number} The view resolution for the provided zoom level. * @api */ ol.View.prototype.getResolutionForZoom = function(zoom) { return /** @type {number} */ (this.constrainResolution( this.maxResolution_, zoom - this.minZoom_, 0)); }; /** * 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} geometryOrExtent The geometry or * extent to fit the view to. * @param {olx.view.FitOptions=} opt_options Options. * @api */ ol.View.prototype.fit = function(geometryOrExtent, opt_options) { var options = opt_options || {}; var size = options.size; if (!size) { size = this.getSizeFromViewport_(); } /** @type {ol.geom.SimpleGeometry} */ var geometry; if (!(geometryOrExtent instanceof ol.geom.SimpleGeometry)) { ol.asserts.assert(Array.isArray(geometryOrExtent), 24); // Invalid extent or geometry provided as `geometry` ol.asserts.assert(!ol.extent.isEmpty(geometryOrExtent), 25); // Cannot fit empty extent provided as `geometry` geometry = ol.geom.Polygon.fromExtent(geometryOrExtent); } else if (geometryOrExtent.getType() === ol.geom.GeometryType.CIRCLE) { geometryOrExtent = geometryOrExtent.getExtent(); geometry = ol.geom.Polygon.fromExtent(geometryOrExtent); geometry.rotate(this.getRotation(), ol.extent.getCenter(geometryOrExtent)); } else { geometry = geometryOrExtent; } 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(); 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]; var callback = options.callback ? options.callback : ol.nullFunction; if (options.duration !== undefined) { this.animate({ resolution: resolution, center: center, duration: options.duration, easing: options.easing }, callback); } else { this.setResolution(resolution); this.setCenter(center); setTimeout(callback.bind(undefined, true), 0); } }; /** * 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 */ 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 */ ol.View.prototype.setCenter = function(center) { this.set(ol.ViewProperty.CENTER, center); if (this.getAnimating()) { this.cancelAnimations(); } }; /** * @param {ol.ViewHint} hint Hint. * @param {number} delta Delta. * @return {number} New value. */ ol.View.prototype.setHint = function(hint, delta) { this.hints_[hint] += delta; this.changed(); return this.hints_[hint]; }; /** * Set the resolution for this view. * @param {number|undefined} resolution The resolution of the view. * @observable * @api */ ol.View.prototype.setResolution = function(resolution) { this.set(ol.ViewProperty.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 */ ol.View.prototype.setRotation = function(rotation) { this.set(ol.ViewProperty.ROTATION, rotation); if (this.getAnimating()) { this.cancelAnimations(); } }; /** * Zoom to a specific zoom level. * @param {number} zoom Zoom level. * @api */ ol.View.prototype.setZoom = function(zoom) { this.setResolution(this.getResolutionForZoom(zoom)); }; /** * @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/openlayers/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[minZoom]; minResolution = resolutions[maxZoom] !== undefined ? resolutions[maxZoom] : 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 { return ol.RotationConstraint.none; } } else { return ol.RotationConstraint.disable; } }; /** * Determine if an animation involves no view change. * @param {ol.ViewAnimation} animation The animation. * @return {boolean} The animation involves no view change. */ ol.View.isNoopAnimation = function(animation) { if (animation.sourceCenter && animation.targetCenter) { if (!ol.coordinate.equals(animation.sourceCenter, animation.targetCenter)) { return false; } } if (animation.sourceResolution !== animation.targetResolution) { return false; } if (animation.sourceRotation !== animation.targetRotation) { return false; } return true; }; 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 = 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 = 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.layer.Property'); /** * @enum {string} */ ol.layer.Property = { OPACITY: 'opacity', VISIBLE: 'visible', EXTENT: 'extent', Z_INDEX: 'zIndex', MAX_RESOLUTION: 'maxResolution', MIN_RESOLUTION: 'minResolution', SOURCE: 'source' }; goog.provide('ol.layer.Base'); goog.require('ol'); goog.require('ol.Object'); goog.require('ol.layer.Property'); 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 * @abstract * @extends {ol.Object} * @param {olx.layer.BaseOptions} options Layer options. * @api */ ol.layer.Base = function(options) { ol.Object.call(this); /** * @type {Object.<string, *>} */ var properties = ol.obj.assign({}, options); properties[ol.layer.Property.OPACITY] = options.opacity !== undefined ? options.opacity : 1; properties[ol.layer.Property.VISIBLE] = options.visible !== undefined ? options.visible : true; properties[ol.layer.Property.Z_INDEX] = options.zIndex !== undefined ? options.zIndex : 0; properties[ol.layer.Property.MAX_RESOLUTION] = options.maxResolution !== undefined ? options.maxResolution : Infinity; properties[ol.layer.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 }); /** * The layer type. * @type {ol.LayerType} * @protected; */ this.type; }; ol.inherits(ol.layer.Base, ol.Object); /** * Get the layer type (used when creating a layer renderer). * @return {ol.LayerType} The layer type. */ ol.layer.Base.prototype.getType = function() { return this.type; }; /** * @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 */ ol.layer.Base.prototype.getExtent = function() { return /** @type {ol.Extent|undefined} */ ( this.get(ol.layer.Property.EXTENT)); }; /** * Return the maximum resolution of the layer. * @return {number} The maximum resolution of the layer. * @observable * @api */ ol.layer.Base.prototype.getMaxResolution = function() { return /** @type {number} */ ( this.get(ol.layer.Property.MAX_RESOLUTION)); }; /** * Return the minimum resolution of the layer. * @return {number} The minimum resolution of the layer. * @observable * @api */ ol.layer.Base.prototype.getMinResolution = function() { return /** @type {number} */ ( this.get(ol.layer.Property.MIN_RESOLUTION)); }; /** * Return the opacity of the layer (between 0 and 1). * @return {number} The opacity of the layer. * @observable * @api */ ol.layer.Base.prototype.getOpacity = function() { return /** @type {number} */ (this.get(ol.layer.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 */ ol.layer.Base.prototype.getVisible = function() { return /** @type {boolean} */ (this.get(ol.layer.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.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 */ ol.layer.Base.prototype.setExtent = function(extent) { this.set(ol.layer.Property.EXTENT, extent); }; /** * Set the maximum resolution at which the layer is visible. * @param {number} maxResolution The maximum resolution of the layer. * @observable * @api */ ol.layer.Base.prototype.setMaxResolution = function(maxResolution) { this.set(ol.layer.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 */ ol.layer.Base.prototype.setMinResolution = function(minResolution) { this.set(ol.layer.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 */ ol.layer.Base.prototype.setOpacity = function(opacity) { this.set(ol.layer.Property.OPACITY, opacity); }; /** * Set the visibility of the layer (`true` or `false`). * @param {boolean} visible The visibility of the layer. * @observable * @api */ ol.layer.Base.prototype.setVisible = function(visible) { this.set(ol.layer.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.Property.Z_INDEX, zindex); }; 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.Collection'); goog.require('ol.CollectionEventType'); goog.require('ol.Object'); goog.require('ol.ObjectEventType'); goog.require('ol.asserts'); 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 */ 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(), {unique: true}); } 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(undefined, {unique: true}); } this.setLayers(layers); }; ol.inherits(ol.layer.Group, ol.layer.Base); /** * @private */ ol.layer.Group.prototype.handleLayerChange_ = function() { 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.CollectionEventType.ADD, this.handleLayersAdd_, this), ol.events.listen(layers, ol.CollectionEventType.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.ObjectEventType.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(); this.listenerKeys_[key] = [ ol.events.listen(layer, ol.ObjectEventType.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(); 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 */ 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 */ 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} * @private */ ol.layer.Group.Property_ = { LAYERS: 'layers' }; goog.provide('ol.PluginType'); /** * A plugin type used when registering a plugin. The supported plugin types are * 'MAP_RENDERER', and 'LAYER_RENDERER'. * @enum {string} */ ol.PluginType = { MAP_RENDERER: 'MAP_RENDERER', LAYER_RENDERER: 'LAYER_RENDERER' }; goog.provide('ol.plugins'); goog.require('ol.PluginType'); /** * The registry of map renderer plugins. * @type {Array<olx.MapRendererPlugin>} * @private */ ol.plugins.mapRendererPlugins_ = []; /** * Get all registered map renderer plugins. * @return {Array<olx.MapRendererPlugin>} The registered map renderer plugins. */ ol.plugins.getMapRendererPlugins = function() { return ol.plugins.mapRendererPlugins_; }; /** * The registry of layer renderer plugins. * @type {Array<olx.LayerRendererPlugin>} * @private */ ol.plugins.layerRendererPlugins_ = []; /** * Get all registered layer renderer plugins. * @return {Array<olx.LayerRendererPlugin>} The registered layer renderer plugins. */ ol.plugins.getLayerRendererPlugins = function() { return ol.plugins.layerRendererPlugins_; }; /** * Register a plugin. * @param {ol.PluginType} type The plugin type. * @param {*} plugin The plugin. */ ol.plugins.register = function(type, plugin) { var plugins; switch (type) { case ol.PluginType.MAP_RENDERER: { plugins = ol.plugins.mapRendererPlugins_; plugins.push(/** @type {olx.MapRendererPlugin} */ (plugin)); break; } case ol.PluginType.LAYER_RENDERER: { plugins = ol.plugins.layerRendererPlugins_; plugins.push(/** @type {olx.LayerRendererPlugin} */ (plugin)); break; } default: { throw new Error('Unsupported plugin type: ' + type); } } }; /** * Register multiple plugins. * @param {ol.PluginType} type The plugin type. * @param {Array} plugins The plugins. */ ol.plugins.registerMultiple = function(type, plugins) { for (var i = 0, ii = plugins.length; i < ii; ++i) { ol.plugins.register(type, plugins[i]); } }; goog.provide('ol.renderer.Type'); /** * Available renderers: `'canvas'` or `'webgl'`. * @enum {string} */ ol.renderer.Type = { CANVAS: 'canvas', WEBGL: 'webgl' }; goog.provide('ol.PluggableMap'); goog.require('ol'); goog.require('ol.Collection'); goog.require('ol.CollectionEventType'); goog.require('ol.MapBrowserEvent'); goog.require('ol.MapBrowserEventHandler'); goog.require('ol.MapBrowserEventType'); goog.require('ol.MapEvent'); goog.require('ol.MapEventType'); goog.require('ol.MapProperty'); goog.require('ol.Object'); goog.require('ol.ObjectEventType'); goog.require('ol.TileQueue'); goog.require('ol.View'); goog.require('ol.ViewHint'); goog.require('ol.asserts'); 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.layer.Group'); goog.require('ol.obj'); goog.require('ol.plugins'); goog.require('ol.renderer.Type'); goog.require('ol.size'); goog.require('ol.structs.PriorityQueue'); goog.require('ol.transform'); /** * @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 */ ol.PluggableMap = function(options) { ol.Object.call(this); var optionsInternal = ol.PluggableMap.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_ = null; /** * @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.MapBrowserEventType.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, options.moveTolerance); for (var key in ol.MapBrowserEventType) { ol.events.listen(this.mapBrowserEventHandler_, ol.MapBrowserEventType[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>} * @protected */ this.controls = optionsInternal.controls || new ol.Collection(); /** * @type {ol.Collection.<ol.interaction.Interaction>} * @protected */ this.interactions = optionsInternal.interactions || new ol.Collection(); /** * @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_ = optionsInternal.mapRendererPlugin['create'](this.viewport_, this); /** * @type {function(Event)|undefined} * @private */ this.handleResize_; /** * @private * @type {ol.Coordinate} */ this.focus_ = null; /** * @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.MapProperty.LAYERGROUP), this.handleLayerGroupChanged_, this); ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.VIEW), this.handleViewChanged_, this); ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.SIZE), this.handleSizeChanged_, this); ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.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.PluggableMap} */ function(control) { control.setMap(this); }, this); ol.events.listen(this.controls, ol.CollectionEventType.ADD, /** * @param {ol.Collection.Event} event Collection event. */ function(event) { event.element.setMap(this); }, this); ol.events.listen(this.controls, ol.CollectionEventType.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.PluggableMap} */ function(interaction) { interaction.setMap(this); }, this); ol.events.listen(this.interactions, ol.CollectionEventType.ADD, /** * @param {ol.Collection.Event} event Collection event. */ function(event) { event.element.setMap(this); }, this); ol.events.listen(this.interactions, ol.CollectionEventType.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.CollectionEventType.ADD, /** * @param {ol.Collection.Event} event Collection event. */ function(event) { this.addOverlayInternal_(/** @type {ol.Overlay} */ (event.element)); }, this); ol.events.listen(this.overlays_, ol.CollectionEventType.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.PluggableMap, ol.Object); /** * Add the given control to the map. * @param {ol.control.Control} control Control. * @api */ ol.PluggableMap.prototype.addControl = function(control) { this.getControls().push(control); }; /** * Add the given interaction to the map. * @param {ol.interaction.Interaction} interaction Interaction to add. * @api */ ol.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.prototype.addOverlay = function(overlay) { this.getOverlays().push(overlay); }; /** * This deals with map's overlay collection changes. * @param {ol.Overlay} overlay Overlay. * @private */ ol.PluggableMap.prototype.addOverlayInternal_ = function(overlay) { var id = overlay.getId(); if (id !== undefined) { this.overlayIdIndex_[id.toString()] = overlay; } overlay.setMap(this); }; /** * * @inheritDoc */ ol.PluggableMap.prototype.disposeInternal = function() { this.mapBrowserEventHandler_.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 the `layerFilter` option in `opt_options`. * @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 */ ol.PluggableMap.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); }; /** * Get all features that intersect a pixel on the viewport. * @param {ol.Pixel} pixel Pixel. * @param {olx.AtPixelOptions=} opt_options Optional options. * @return {Array.<ol.Feature|ol.render.Feature>} The detected features or * `null` if none were found. * @api */ ol.PluggableMap.prototype.getFeaturesAtPixel = function(pixel, opt_options) { var features = null; this.forEachFeatureAtPixel(pixel, function(feature) { if (!features) { features = []; } features.push(feature); }, opt_options); return features; }; /** * 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 receive 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 */ ol.PluggableMap.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.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.prototype.getTarget = function() { return /** @type {Element|string|undefined} */ ( this.get(ol.MapProperty.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.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.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.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.prototype.getLayerGroup = function() { return /** @type {ol.layer.Group} */ (this.get(ol.MapProperty.LAYERGROUP)); }; /** * Get the collection of layers associated with this map. * @return {!ol.Collection.<ol.layer.Base>} Layers. * @api */ ol.PluggableMap.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 */ ol.PluggableMap.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.PluggableMap.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 */ ol.PluggableMap.prototype.getSize = function() { return /** @type {ol.Size|undefined} */ (this.get(ol.MapProperty.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 */ ol.PluggableMap.prototype.getView = function() { return /** @type {ol.View} */ (this.get(ol.MapProperty.VIEW)); }; /** * Get the element that serves as the map viewport. * @return {Element} Viewport. * @api */ ol.PluggableMap.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.PluggableMap.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.PluggableMap.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.PluggableMap.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.PluggableMap.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.PluggableMap.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.PluggableMap.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.ViewHint.ANIMATING]) { maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0; maxNewLoads = 2; } if (hints[ol.ViewHint.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.PluggableMap.prototype.handleSizeChanged_ = function() { this.render(); }; /** * @private */ ol.PluggableMap.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(); } 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) { this.renderer_.removeLayerRenderers(); 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.PluggableMap.prototype.handleTileChange_ = function() { this.render(); }; /** * @private */ ol.PluggableMap.prototype.handleViewPropertyChanged_ = function() { this.render(); }; /** * @private */ ol.PluggableMap.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.viewport_.setAttribute('data-view', ol.getUid(view)); this.viewPropertyListenerKey_ = ol.events.listen( view, ol.ObjectEventType.PROPERTYCHANGE, this.handleViewPropertyChanged_, this); this.viewChangeListenerKey_ = ol.events.listen( view, ol.events.EventType.CHANGE, this.handleViewPropertyChanged_, this); } this.render(); }; /** * @private */ ol.PluggableMap.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.ObjectEventType.PROPERTYCHANGE, this.render, this), ol.events.listen( layerGroup, ol.events.EventType.CHANGE, this.render, this) ]; } this.render(); }; /** * @return {boolean} Is rendered. */ ol.PluggableMap.prototype.isRendered = function() { return !!this.frameState_; }; /** * Requests an immediate render in a synchronous manner. * @api */ ol.PluggableMap.prototype.renderSync = function() { if (this.animationDelayKey_) { cancelAnimationFrame(this.animationDelayKey_); } this.animationDelay_(); }; /** * Request a map rendering (at the next animation frame). * @api */ ol.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.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 */ ol.PluggableMap.prototype.removeOverlay = function(overlay) { return this.getOverlays().remove(overlay); }; /** * @param {number} time Time. * @private */ ol.PluggableMap.prototype.renderFrame_ = function(time) { var i, ii, viewState; var size = this.getSize(); var view = this.getView(); var extent = ol.extent.createEmpty(); var previousFrameState = this.frameState_; /** @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(); var center = viewState.center; var pixelResolution = viewState.resolution / this.pixelRatio_; center[0] = Math.round(center[0] / pixelResolution) * pixelResolution; center[1] = Math.round(center[1] / pixelResolution) * pixelResolution; frameState = /** @type {olx.FrameState} */ ({ animate: false, coordinateToPixelTransform: this.coordinateToPixelTransform_, extent: extent, focus: !this.focus_ ? 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) { 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); if (previousFrameState) { var moveStart = !this.previousExtent_ || (!ol.extent.isEmpty(this.previousExtent_) && !ol.extent.equals(frameState.extent, this.previousExtent_)); if (moveStart) { this.dispatchEvent( new ol.MapEvent(ol.MapEventType.MOVESTART, this, previousFrameState)); this.previousExtent_ = ol.extent.createOrUpdateEmpty(this.previousExtent_); } } var idle = this.previousExtent_ && !frameState.viewHints[ol.ViewHint.ANIMATING] && !frameState.viewHints[ol.ViewHint.INTERACTING] && !ol.extent.equals(frameState.extent, this.previousExtent_); if (idle) { this.dispatchEvent( new ol.MapEvent(ol.MapEventType.MOVEEND, this, frameState)); ol.extent.clone(frameState.extent, this.previousExtent_); } } this.dispatchEvent( new ol.MapEvent(ol.MapEventType.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 */ ol.PluggableMap.prototype.setLayerGroup = function(layerGroup) { this.set(ol.MapProperty.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.PluggableMap.prototype.setSize = function(size) { this.set(ol.MapProperty.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 */ ol.PluggableMap.prototype.setTarget = function(target) { this.set(ol.MapProperty.TARGET, target); }; /** * Set the view for this map. * @param {ol.View} view The view that controls this map. * @observable * @api */ ol.PluggableMap.prototype.setView = function(view) { this.set(ol.MapProperty.VIEW, view); }; /** * @param {ol.Feature} feature Feature. */ ol.PluggableMap.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 */ ol.PluggableMap.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.PluggableMap.prototype.unskipFeature = function(feature) { var featureUid = ol.getUid(feature).toString(); delete this.skippedFeatureUids_[featureUid]; this.render(); }; /** * @type {Array.<ol.renderer.Type>} * @const */ ol.PluggableMap.DEFAULT_RENDERER_TYPES = [ ol.renderer.Type.CANVAS, ol.renderer.Type.WEBGL ]; /** * @const * @type {string} */ ol.PluggableMap.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'; /** * @param {olx.MapOptions} options Map options. * @return {ol.MapOptionsInternal} Internal map options. */ ol.PluggableMap.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.PluggableMap.LOGO_URL] = 'https://openlayers.org/'; } 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.MapProperty.LAYERGROUP] = layerGroup; values[ol.MapProperty.TARGET] = options.target; values[ol.MapProperty.VIEW] = options.view !== undefined ? options.view : new ol.View(); /** * @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) { rendererTypes = rendererTypes.concat(ol.PluggableMap.DEFAULT_RENDERER_TYPES); } } else { rendererTypes = ol.PluggableMap.DEFAULT_RENDERER_TYPES; } /** * @type {olx.MapRendererPlugin} */ var mapRendererPlugin; var mapRendererPlugins = ol.plugins.getMapRendererPlugins(); outer: for (var i = 0, ii = rendererTypes.length; i < ii; ++i) { var rendererType = rendererTypes[i]; for (var j = 0, jj = mapRendererPlugins.length; j < jj; ++j) { var candidate = mapRendererPlugins[j]; if (candidate['handles'](rendererType)) { mapRendererPlugin = candidate; break outer; } } } if (!mapRendererPlugin) { throw new Error('Unable to create a map renderer for types: ' + rendererTypes.join(', ')); } 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; } } 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; } } 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, mapRendererPlugin: mapRendererPlugin, values: values }; }; goog.provide('ol.control.Control'); goog.require('ol'); goog.require('ol.MapEventType'); goog.require('ol.Object'); goog.require('ol.dom'); goog.require('ol.events'); /** * @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 */ 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.PluggableMap} */ 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.PluggableMap} Map. * @api */ 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.PluggableMap} map Map. * @override * @api */ 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.MapEventType.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 selectable. * * @const * @type {string} */ ol.css.CLASS_SELECTABLE = 'ol-selectable'; /** * 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'; /** * Get the list of font families from a font spec. Note that this doesn't work * for font families that have commas in them. * @param {string} The CSS font property. * @return {Object.<string>} The font families (or null if the input spec is invalid). */ ol.css.getFontFamilies = (function() { var style; var cache = {}; return function(font) { if (!style) { style = document.createElement('div').style; } if (!(font in cache)) { style.font = font; var family = style.fontFamily; style.font = ''; if (!family) { return null; } cache[font] = family.split(/,\s?/); } return cache[font]; }; })(); goog.provide('ol.render.EventType'); /** * @enum {string} */ ol.render.EventType = { /** * @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.layer.Property'); goog.require('ol.obj'); goog.require('ol.render.EventType'); 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 * @abstract * @extends {ol.layer.Base} * @fires ol.render.Event * @param {olx.layer.LayerOptions} options Layer options. * @api */ 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.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 */ ol.layer.Layer.prototype.getSource = function() { var source = this.get(ol.layer.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.PluggableMap} 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.EventType.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 */ ol.layer.Layer.prototype.setSource = function(source) { this.set(ol.layer.Property.SOURCE, source); }; // FIXME handle date line wrap goog.provide('ol.control.Attribution'); goog.require('ol'); goog.require('ol.array'); goog.require('ol.control.Control'); goog.require('ol.css'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.layer.Layer'); 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 */ 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 }); /** * A list of currently rendered resolutions. * @type {Array.<string>} * @private */ this.renderedAttributions_ = []; /** * @private * @type {boolean} */ this.renderedVisible_ = true; /** * @private * @type {Object.<string, Element>} */ this.logoElements_ = {}; }; ol.inherits(ol.control.Attribution, ol.control.Control); /** * Get a list of visible attributions. * @param {olx.FrameState} frameState Frame state. * @return {Array.<string>} Attributions. * @private */ ol.control.Attribution.prototype.getSourceAttributions_ = function(frameState) { /** * Used to determine if an attribution already exists. * @type {Object.<string, boolean>} */ var lookup = {}; /** * A list of visible attributions. * @type {Array.<string>} */ var visibleAttributions = []; var layerStatesArray = frameState.layerStatesArray; var resolution = frameState.viewState.resolution; for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) { var layerState = layerStatesArray[i]; if (!ol.layer.Layer.visibleAtResolution(layerState, resolution)) { continue; } var source = layerState.layer.getSource(); if (!source) { continue; } var attributionGetter = source.getAttributions2(); if (!attributionGetter) { continue; } var attributions = attributionGetter(frameState); if (!attributions) { continue; } if (Array.isArray(attributions)) { for (var j = 0, jj = attributions.length; j < jj; ++j) { if (!(attributions[j] in lookup)) { visibleAttributions.push(attributions[j]); lookup[attributions[j]] = true; } } } else { if (!(attributions in lookup)) { visibleAttributions.push(attributions); lookup[attributions] = true; } } } return visibleAttributions; }; /** * 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); if (ol.array.equals(attributions, this.renderedAttributions_)) { return; } // remove everything but the logo while (this.ulElement_.lastChild !== this.logoLi_) { this.ulElement_.removeChild(this.ulElement_.lastChild); } // append the attributions for (var i = 0, ii = attributions.length; i < ii; ++i) { var element = document.createElement('LI'); element.innerHTML = attributions[i]; this.ulElement_.appendChild(element); } if (attributions.length === 0 && this.renderedAttributions_.length > 0) { this.element.classList.add('ol-logo-only'); } else if (this.renderedAttributions_.length === 0 && attributions.length > 0) { this.element.classList.remove('ol-logo-only'); } var visible = attributions.length > 0 || !ol.obj.isEmpty(frameState.logos); if (this.renderedVisible_ != visible) { this.element.style.display = visible ? '' : 'none'; this.renderedVisible_ = visible; } this.renderedAttributions_ = attributions; 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 */ 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 */ 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 */ 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 */ ol.control.Attribution.prototype.getCollapsed = function() { return this.collapsed_; }; 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 */ 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; } if (view.getRotation() !== undefined) { if (this.duration_ > 0) { 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 */ 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.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 */ 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; }; 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]; // we don't want a duration of 0 (divide by zero) // we also make sure the user panned for a duration of at least one frame // (1/60s) to compute sane displacement values if (duration < 1000 / 60) { return false; } 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_; }; /** * @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_; }; goog.provide('ol.interaction.Property'); /** * @enum {string} */ ol.interaction.Property = { ACTIVE: 'active' }; // FIXME factor out key precondition (shift et. al) goog.provide('ol.interaction.Interaction'); goog.require('ol'); goog.require('ol.Object'); goog.require('ol.easing'); goog.require('ol.interaction.Property'); goog.require('ol.math'); /** * @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.PluggableMap} */ 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.Property.ACTIVE)); }; /** * Get the map associated with this interaction. * @return {ol.PluggableMap} 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.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.PluggableMap} map Map. */ ol.interaction.Interaction.prototype.setMap = function(map) { this.map_ = map; }; /** * @param {ol.View} view View. * @param {ol.Coordinate} delta Delta. * @param {number=} opt_duration Duration. */ ol.interaction.Interaction.pan = function(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.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(view, rotation, opt_anchor, opt_duration) { rotation = view.constrainRotation(rotation, 0); ol.interaction.Interaction.rotateWithoutConstraints( view, rotation, opt_anchor, opt_duration); }; /** * @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(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.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(view, resolution, opt_anchor, opt_duration, opt_direction) { resolution = view.constrainResolution(resolution, 0, opt_direction); ol.interaction.Interaction.zoomWithoutConstraints( view, resolution, opt_anchor, opt_duration); }; /** * @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(view, delta, opt_anchor, opt_duration) { var currentResolution = view.getResolution(); var resolution = view.constrainResolution(currentResolution, delta, 0); if (resolution !== undefined) { var resolutions = view.getResolutions(); resolution = ol.math.clamp( resolution, view.getMinResolution() || resolutions[resolutions.length - 1], view.getMaxResolution() || resolutions[0]); } // If we have a constraint on center, we need to change the anchor so that the // new center is within the extent. We first calculate the new center, apply // the constraint to it, and then calculate back the anchor if (opt_anchor && resolution !== undefined && resolution !== currentResolution) { var currentCenter = view.getCenter(); var center = view.calculateCenterZoom(resolution, opt_anchor); center = view.constrainCenter(center); opt_anchor = [ (resolution * currentCenter[0] - currentResolution * center[0]) / (resolution - currentResolution), (resolution * currentCenter[1] - currentResolution * center[1]) / (resolution - currentResolution) ]; } ol.interaction.Interaction.zoomWithoutConstraints( view, resolution, opt_anchor, opt_duration); }; /** * @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(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); } } }; goog.provide('ol.interaction.DoubleClickZoom'); goog.require('ol'); goog.require('ol.MapBrowserEventType'); 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 */ 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.MapBrowserEventType.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( view, delta, anchor, this.duration_); mapBrowserEvent.preventDefault(); stopEvent = true; } return !stopEvent; }; goog.provide('ol.events.condition'); goog.require('ol.MapBrowserEventType'); 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 */ 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 */ 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 */ 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 */ ol.events.condition.click = function(mapBrowserEvent) { return mapBrowserEvent.type == ol.MapBrowserEventType.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 */ 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 */ ol.events.condition.singleClick = function(mapBrowserEvent) { return mapBrowserEvent.type == ol.MapBrowserEventType.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 */ ol.events.condition.doubleClick = function(mapBrowserEvent) { return mapBrowserEvent.type == ol.MapBrowserEventType.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 */ 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 */ 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 */ 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 */ 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.MapBrowserEventType'); 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.<string, 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.MapBrowserEventType.POINTERDOWN || type === ol.MapBrowserEventType.POINTERDRAG || type === ol.MapBrowserEventType.POINTERUP); }; /** * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. * @private */ ol.interaction.Pointer.prototype.updateTrackedPointers_ = function(mapBrowserEvent) { if (this.isPointerDraggingEvent_(mapBrowserEvent)) { var event = mapBrowserEvent.pointerEvent; var id = event.pointerId.toString(); if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERUP) { delete this.trackedPointers_[id]; } else if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERDOWN) { this.trackedPointers_[id] = event; } else if (id in this.trackedPointers_) { // update only when there was a pointerdown event for this pointer this.trackedPointers_[id] = 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.MapBrowserEventType.POINTERDRAG) { this.handleDragEvent_(mapBrowserEvent); } else if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERUP) { var handledUp = this.handleUpEvent_(mapBrowserEvent); this.handlingDownUpSequence = handledUp && this.targetPointers.length > 0; } } else { if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERDOWN) { var handled = this.handleDownEvent_(mapBrowserEvent); this.handlingDownUpSequence = handled; stopEvent = this.shouldStopEvent(handled); } else if (mapBrowserEvent.type == ol.MapBrowserEventType.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.ViewHint'); 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 */ 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; /** * @type {number} */ this.lastPointersCount_; /** * @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) { var targetPointers = this.targetPointers; var centroid = ol.interaction.Pointer.centroid(targetPointers); if (targetPointers.length == this.lastPointersCount_) { 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); } } else if (this.kinetic_) { // reset so we don't overestimate the kinetic energy after // after one finger down, tiny drag, second finger down this.kinetic_.begin(); } this.lastCentroid = centroid; this.lastPointersCount_ = targetPointers.length; }; /** * @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.ViewHint.INTERACTING, -1); return false; } else { if (this.kinetic_) { // reset so we don't overestimate the kinetic energy after // after one finger up, tiny drag, second finger up this.kinetic_.begin(); } 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.ViewHint.INTERACTING, 1); } // stop any current animation if (view.getAnimating()) { 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.RotationConstraint'); goog.require('ol.ViewHint'); 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 */ 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 view = map.getView(); if (view.getConstraints().rotation === ol.RotationConstraint.disable) { return; } 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 rotation = view.getRotation(); ol.interaction.Interaction.rotateWithoutConstraints( 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.ViewHint.INTERACTING, -1); var rotation = view.getRotation(); ol.interaction.Interaction.rotate(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.ViewHint.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.PluggableMap} */ 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.PluggableMap} 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'); /** * @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 */ 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 {number} * @private */ this.minArea_ = options.minArea !== undefined ? options.minArea : 64; /** * @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. * @this {ol.interaction.DragBox} */ 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 >= this.minArea_; }; /** * @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 */ ol.interaction.DragBox.prototype.getGeometry = function() { return this.box_.getGeometry(); }; /** * To be overridden by child classes. * FIXME: use constructor option instead of relying on overriding. * @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} * @private */ ol.interaction.DragBox.EventType_ = { /** * Triggered upon drag box start. * @event ol.interaction.DragBox.Event#boxstart * @api */ 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 */ 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 */ 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 */ 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)); var center = ol.extent.getCenter(extent); center = view.constrainCenter(center); view.animate({ resolution: resolution, center: center, 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 */ 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(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 */ 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( view, delta, undefined, this.duration_); mapBrowserEvent.preventDefault(); stopEvent = true; } } return !stopEvent; }; goog.provide('ol.interaction.MouseWheelZoom'); goog.require('ol'); goog.require('ol.ViewHint'); 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 */ 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 {boolean} */ this.constrainResolution_ = options.constrainResolution || false; /** * @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.ViewHint.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(view.constrainCenter(center)); } view.setResolution(resolution); if (rebound === 0 && this.constrainResolution_) { view.animate({ resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1), easing: ol.easing.easeOut, anchor: this.lastAnchor_, duration: this.duration_ }); } 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.ViewHint.INTERACTING, -1); }; /** * @private * @param {ol.PluggableMap} 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(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} * @private */ ol.interaction.MouseWheelZoom.Mode_ = { TRACKPAD: 'trackpad', WHEEL: 'wheel' }; goog.provide('ol.interaction.PinchRotate'); goog.require('ol'); goog.require('ol.ViewHint'); goog.require('ol.functions'); goog.require('ol.interaction.Interaction'); goog.require('ol.interaction.Pointer'); goog.require('ol.RotationConstraint'); /** * @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 */ 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) { 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; var view = map.getView(); if (view.getConstraints().rotation === ol.RotationConstraint.disable) { return; } // 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 rotation = view.getRotation(); map.render(); ol.interaction.Interaction.rotateWithoutConstraints(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.ViewHint.INTERACTING, -1); if (this.rotating_) { var rotation = view.getRotation(); ol.interaction.Interaction.rotate( 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.ViewHint.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.ViewHint'); 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 */ 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) { 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; var map = mapBrowserEvent.map; var view = map.getView(); var resolution = view.getResolution(); var maxResolution = view.getMaxResolution(); var minResolution = view.getMinResolution(); var newResolution = resolution * scaleDelta; if (newResolution > maxResolution) { scaleDelta = maxResolution / resolution; newResolution = maxResolution; } else if (newResolution < minResolution) { scaleDelta = minResolution / resolution; newResolution = minResolution; } if (scaleDelta != 1.0) { this.lastScaleDelta_ = scaleDelta; } // 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(view, newResolution, 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.ViewHint.INTERACTING, -1); var resolution = view.getResolution(); if (this.constrainResolution_ || resolution < view.getMinResolution() || resolution > view.getMaxResolution()) { // 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(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.ViewHint.INTERACTING, 1); } return true; } else { return false; } }; /** * @inheritDoc */ ol.interaction.PinchZoom.prototype.shouldStopEvent = ol.functions.FALSE; goog.provide('ol.interaction'); 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 */ 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({ constrainResolution: options.constrainResolution, 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({ constrainResolution: options.constrainResolution, 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.ImageBase'); goog.require('ol'); goog.require('ol.events.EventTarget'); goog.require('ol.events.EventType'); /** * @constructor * @abstract * @extends {ol.events.EventTarget} * @param {ol.Extent} extent Extent. * @param {number|undefined} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {ol.ImageState} state State. */ ol.ImageBase = function(extent, resolution, pixelRatio, state) { ol.events.EventTarget.call(this); /** * @protected * @type {ol.Extent} */ this.extent = extent; /** * @private * @type {number} */ this.pixelRatio_ = pixelRatio; /** * @protected * @type {number|undefined} */ this.resolution = resolution; /** * @protected * @type {ol.ImageState} */ this.state = state; }; ol.inherits(ol.ImageBase, ol.events.EventTarget); /** * @protected */ ol.ImageBase.prototype.changed = function() { this.dispatchEvent(ol.events.EventType.CHANGE); }; /** * @return {ol.Extent} Extent. */ ol.ImageBase.prototype.getExtent = function() { return this.extent; }; /** * @abstract * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image. */ ol.ImageBase.prototype.getImage = function() {}; /** * @return {number} PixelRatio. */ ol.ImageBase.prototype.getPixelRatio = function() { return this.pixelRatio_; }; /** * @return {number} Resolution. */ ol.ImageBase.prototype.getResolution = function() { return /** @type {number} */ (this.resolution); }; /** * @return {ol.ImageState} State. */ ol.ImageBase.prototype.getState = function() { return this.state; }; /** * Load not yet loaded URI. * @abstract */ ol.ImageBase.prototype.load = function() {}; goog.provide('ol.ImageState'); /** * @enum {number} */ ol.ImageState = { IDLE: 0, LOADING: 1, LOADED: 2, ERROR: 3 }; goog.provide('ol.ImageCanvas'); goog.require('ol'); goog.require('ol.ImageBase'); goog.require('ol.ImageState'); /** * @constructor * @extends {ol.ImageBase} * @param {ol.Extent} extent Extent. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {HTMLCanvasElement} canvas Canvas. * @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to * support asynchronous canvas drawing. */ ol.ImageCanvas = function(extent, resolution, pixelRatio, 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.ImageState.IDLE : ol.ImageState.LOADED; ol.ImageBase.call(this, extent, resolution, pixelRatio, state); /** * @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.ImageState.ERROR; } else { this.state = ol.ImageState.LOADED; } this.changed(); }; /** * @inheritDoc */ ol.ImageCanvas.prototype.load = function() { if (this.state == ol.ImageState.IDLE) { this.state = ol.ImageState.LOADING; this.changed(); this.loader_(this.handleLoad_.bind(this)); } }; /** * @inheritDoc */ ol.ImageCanvas.prototype.getImage = function() { return this.canvas_; }; goog.provide('ol.LayerType'); /** * A layer type used when creating layer renderers. * @enum {string} */ ol.LayerType = { IMAGE: 'IMAGE', TILE: 'TILE', VECTOR_TILE: 'VECTOR_TILE', VECTOR: 'VECTOR' }; goog.provide('ol.layer.VectorRenderType'); /** * @enum {string} * Render mode for vector layers: * * `'image'`: Vector layers are rendered as images. Great performance, but * point symbols and texts are always rotated with the view and pixels are * scaled during zoom animations. * * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering * even during animations, but slower performance. * @api */ ol.layer.VectorRenderType = { IMAGE: 'image', VECTOR: 'vector' }; 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.EventType} 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); goog.provide('ol.structs.LRUCache'); goog.require('ol'); goog.require('ol.asserts'); goog.require('ol.events.EventTarget'); goog.require('ol.events.EventType'); /** * 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 * @extends {ol.events.EventTarget} * @fires ol.events.Event * @struct * @template T * @param {number=} opt_highWaterMark High water mark. */ ol.structs.LRUCache = function(opt_highWaterMark) { ol.events.EventTarget.call(this); /** * @type {number} */ this.highWaterMark = opt_highWaterMark !== undefined ? opt_highWaterMark : 2048; /** * @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; }; ol.inherits(ol.structs.LRUCache, ol.events.EventTarget); /** * @return {boolean} Can expire cache. */ ol.structs.LRUCache.prototype.canExpireCache = function() { return this.getCount() > this.highWaterMark; }; /** * FIXME empty description for jsdoc */ ol.structs.LRUCache.prototype.clear = function() { this.count_ = 0; this.entries_ = {}; this.oldest_ = null; this.newest_ = null; this.dispatchEvent(ol.events.EventType.CLEAR); }; /** * @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_; }; /** * Remove an entry from the cache. * @param {string} key The entry key. * @return {T} The removed entry. */ ol.structs.LRUCache.prototype.remove = 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_) { this.newest_ = /** @type {ol.LRUCacheEntry} */ (entry.older); if (this.newest_) { this.newest_.newer = null; } } else if (entry === this.oldest_) { this.oldest_ = /** @type {ol.LRUCacheEntry} */ (entry.newer); if (this.oldest_) { this.oldest_.older = null; } } else { entry.newer.older = entry.older; entry.older.newer = entry.newer; } delete this.entries_[key]; --this.count_; 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_; } 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_; } return values; }; /** * @return {T} Last value. */ ol.structs.LRUCache.prototype.peekLast = function() { return this.oldest_.value_; }; /** * @return {string} Last key. */ ol.structs.LRUCache.prototype.peekLastKey = function() { return this.oldest_.key_; }; /** * Get the key of the newest item in the cache. Throws if the cache is empty. * @return {string} The newest key. */ ol.structs.LRUCache.prototype.peekFirstKey = function() { return this.newest_.key_; }; /** * @return {T} value Value. */ ol.structs.LRUCache.prototype.pop = function() { var entry = this.oldest_; 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.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_; }; /** * Prune the cache. */ ol.structs.LRUCache.prototype.prune = function() { while (this.canExpireCache()) { this.pop(); } }; goog.provide('ol.render.canvas'); goog.require('ol.css'); goog.require('ol.dom'); goog.require('ol.obj'); goog.require('ol.structs.LRUCache'); goog.require('ol.transform'); /** * @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 {number} */ ol.render.canvas.defaultLineDashOffset = 0; /** * @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 {Array.<number>} */ ol.render.canvas.defaultPadding = [0, 0, 0, 0]; /** * @const * @type {number} */ ol.render.canvas.defaultLineWidth = 1; /** * @type {ol.structs.LRUCache.<HTMLCanvasElement>} */ ol.render.canvas.labelCache = new ol.structs.LRUCache(); /** * @type {!Object.<string, number>} */ ol.render.canvas.checkedFonts_ = {}; /** * @type {CanvasRenderingContext2D} */ ol.render.canvas.measureContext_ = null; /** * @type {!Object.<string, number>} */ ol.render.canvas.textHeights_ = {}; /** * Clears the label cache when a font becomes available. * @param {string} fontSpec CSS font spec. */ ol.render.canvas.checkFont = (function() { var retries = 60; var checked = ol.render.canvas.checkedFonts_; var labelCache = ol.render.canvas.labelCache; var font = '32px monospace'; var text = 'wmytzilWMYTZIL@#/&?$%10'; var interval, referenceWidth; function isAvailable(fontFamily) { var context = ol.render.canvas.getMeasureContext(); context.font = font; referenceWidth = context.measureText(text).width; var available = true; if (fontFamily != 'monospace') { context.font = '32px ' + fontFamily + ',monospace'; var width = context.measureText(text).width; // If width and referenceWidth are the same, then the 'monospace' // fallback was used instead of the font we wanted, so the font is not // available. available = width != referenceWidth; } return available; } function check() { var done = true; for (var font in checked) { if (checked[font] < retries) { if (isAvailable(font)) { checked[font] = retries; ol.obj.clear(ol.render.canvas.textHeights_); // Make sure that loaded fonts are picked up by Safari ol.render.canvas.measureContext_ = null; labelCache.clear(); } else { ++checked[font]; done = false; } } } if (done) { window.clearInterval(interval); interval = undefined; } } return function(fontSpec) { var fontFamilies = ol.css.getFontFamilies(fontSpec); if (!fontFamilies) { return; } for (var i = 0, ii = fontFamilies.length; i < ii; ++i) { var fontFamily = fontFamilies[i]; if (!(fontFamily in checked)) { checked[fontFamily] = retries; if (!isAvailable(fontFamily)) { checked[fontFamily] = 0; if (interval === undefined) { interval = window.setInterval(check, 32); } } } } }; })(); /** * @return {CanvasRenderingContext2D} Measure context. */ ol.render.canvas.getMeasureContext = function() { var context = ol.render.canvas.measureContext_; if (!context) { context = ol.render.canvas.measureContext_ = ol.dom.createCanvasContext2D(1, 1); } return context; }; /** * @param {string} font Font to use for measuring. * @return {ol.Size} Measurement. */ ol.render.canvas.measureTextHeight = (function() { var span; var heights = ol.render.canvas.textHeights_; return function(font) { var height = heights[font]; if (height == undefined) { if (!span) { span = document.createElement('span'); span.textContent = 'M'; span.style.margin = span.style.padding = '0 !important'; span.style.position = 'absolute !important'; span.style.left = '-99999px !important'; } span.style.font = font; document.body.appendChild(span); height = heights[font] = span.offsetHeight; document.body.removeChild(span); } return height; }; })(); /** * @param {string} font Font. * @param {string} text Text. * @return {number} Width. */ ol.render.canvas.measureTextWidth = function(font, text) { var measureContext = ol.render.canvas.getMeasureContext(); if (font != measureContext.font) { measureContext.font = font; } return measureContext.measureText(text).width; }; /** * @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); } }; ol.render.canvas.resetTransform_ = ol.transform.create(); /** * @param {CanvasRenderingContext2D} context Context. * @param {ol.Transform|null} transform Transform. * @param {number} opacity Opacity. * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image. * @param {number} originX Origin X. * @param {number} originY Origin Y. * @param {number} w Width. * @param {number} h Height. * @param {number} x X. * @param {number} y Y. * @param {number} scale Scale. */ ol.render.canvas.drawImage = function(context, transform, opacity, image, originX, originY, w, h, x, y, scale) { var alpha; if (opacity != 1) { alpha = context.globalAlpha; context.globalAlpha = alpha * opacity; } if (transform) { context.setTransform.apply(context, transform); } context.drawImage(image, originX, originY, w, h, x, y, w * scale, h * scale); if (alpha) { context.globalAlpha = alpha; } if (transform) { context.setTransform.apply(context, ol.render.canvas.resetTransform_); } }; goog.provide('ol.color'); goog.require('ol.asserts'); goog.require('ol.math'); /** * This RegExp matches # followed by 3, 4, 6, or 8 hex digits. * @const * @type {RegExp} * @private */ ol.color.HEX_COLOR_RE_ = /^#(?:[0-9a-f]{3,4}){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 var d; // number of digits per channel if (n <= 4) { d = 1; } else { d = 2; } var hasAlpha = n === 4 || n === 8; 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 (hasAlpha) { a = parseInt(s.substr(1 + 3 * d, d), 16); } else { a = 255; } if (d == 1) { r = (r << 4) + r; g = (g << 4) + g; b = (b << 4) + b; if (hasAlpha) { a = (a << 4) + a; } } color = [r, g, b, a / 255]; } 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.render.VectorContext'); /** * Context for drawing geometries. A vector context is available on render * events and does not need to be constructed directly. * @constructor * @abstract * @struct * @api */ ol.render.VectorContext = function() { }; /** * Render a geometry with a custom renderer. * * @param {ol.geom.SimpleGeometry} geometry Geometry. * @param {ol.Feature|ol.render.Feature} feature Feature. * @param {Function} renderer Renderer. */ ol.render.VectorContext.prototype.drawCustom = function(geometry, feature, renderer) {}; /** * Render a geometry. * * @param {ol.geom.Geometry} geometry The geometry to render. */ ol.render.VectorContext.prototype.drawGeometry = function(geometry) {}; /** * Set the rendering style. * * @param {ol.style.Style} style The rendering style. */ ol.render.VectorContext.prototype.setStyle = function(style) {}; /** * @param {ol.geom.Circle} circleGeometry Circle geometry. * @param {ol.Feature} feature Feature. */ ol.render.VectorContext.prototype.drawCircle = function(circleGeometry, feature) {}; /** * @param {ol.Feature} feature Feature. * @param {ol.style.Style} style Style. */ ol.render.VectorContext.prototype.drawFeature = function(feature, style) {}; /** * @param {ol.geom.GeometryCollection} geometryCollectionGeometry Geometry * collection. * @param {ol.Feature} feature Feature. */ ol.render.VectorContext.prototype.drawGeometryCollection = function(geometryCollectionGeometry, feature) {}; /** * @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) {}; /** * @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) {}; /** * @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) {}; /** * @param {ol.geom.MultiPolygon} multiPolygonGeometry MultiPolygon geometry. * @param {ol.Feature|ol.render.Feature} feature Feature. */ ol.render.VectorContext.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {}; /** * @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) {}; /** * @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) {}; /** * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. * @param {ol.Feature|ol.render.Feature} feature Feature. */ ol.render.VectorContext.prototype.drawText = function(geometry, feature) {}; /** * @param {ol.style.Fill} fillStyle Fill style. * @param {ol.style.Stroke} strokeStyle Stroke style. */ ol.render.VectorContext.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {}; /** * @param {ol.style.Image} imageStyle Image style. * @param {ol.DeclutterGroup=} opt_declutterGroup Declutter. */ ol.render.VectorContext.prototype.setImageStyle = function(imageStyle, opt_declutterGroup) {}; /** * @param {ol.style.Text} textStyle Text style. * @param {ol.DeclutterGroup=} opt_declutterGroup Declutter. */ ol.render.VectorContext.prototype.setTextStyle = function(textStyle, opt_declutterGroup) {}; // 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; } 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_); 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. * @override * @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. * @override * @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. * @override * @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: } }; /** * 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. * @override * @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. * @override */ 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. * @override */ 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. * @override */ 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. * @override */ 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. * @override */ 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. * @override */ 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. * @override */ 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.lineDashOffset = strokeState.lineDashOffset; } context.lineJoin = strokeState.lineJoin; context.lineWidth = strokeState.lineWidth; context.miterLimit = strokeState.miterLimit; context.strokeStyle = strokeState.strokeStyle; this.contextStrokeState_ = { lineCap: strokeState.lineCap, lineDash: strokeState.lineDash, lineDashOffset: strokeState.lineDashOffset, 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.lineDashOffset != strokeState.lineDashOffset) { contextStrokeState.lineDashOffset = context.lineDashOffset = strokeState.lineDashOffset; } } 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_; var textAlign = textState.textAlign ? textState.textAlign : ol.render.canvas.defaultTextAlign; if (!contextTextState) { context.font = textState.font; context.textAlign = textAlign; context.textBaseline = textState.textBaseline; this.contextTextState_ = { font: textState.font, textAlign: textAlign, textBaseline: textState.textBaseline }; } else { if (contextTextState.font != textState.font) { contextTextState.font = context.font = textState.font; } if (contextTextState.textAlign != textAlign) { contextTextState.textAlign = 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. * @override */ 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 strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); 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, lineDashOffset: strokeStyleLineDashOffset ? strokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset, 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. * @override */ 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(); 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.pixelRatio_; 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. * @override */ 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 textStrokeStyleLineDashOffset = textStrokeStyle.getLineDashOffset(); 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, lineDashOffset: textStrokeStyleLineDashOffset ? textStrokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset, 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.ImageState'); goog.require('ol.Observable'); goog.require('ol.TileState'); 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.ImageState.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.ImageState.LOADED && imageState != ol.ImageState.ERROR) { ol.events.listen(image, ol.events.EventType.CHANGE, this.handleImageChange_, this); } if (imageState == ol.ImageState.IDLE) { image.load(); imageState = image.getState(); } return imageState == ol.ImageState.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.PluggableMap} map Map. * @param {olx.FrameState} frameState Frame state. */ var postRenderFunction = function(tileSource, map, frameState) { var tileSourceKey = ol.getUid(tileSource).toString(); if (tileSourceKey in frameState.usedTiles) { tileSource.expireCache(frameState.viewState.projection, frameState.usedTiles[tileSourceKey]); } }.bind(null, tileSource); frameState.postRenderFunctions.push( /** @type {ol.PostRenderFunction} */ (postRenderFunction) ); } }; /** * @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 = minZoom; z <= currentZ; ++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.TileState.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.EventType'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.Immediate'); goog.require('ol.renderer.Layer'); goog.require('ol.transform'); /** * @constructor * @abstract * @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.EventType} 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.EventType.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.EventType.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.EventType.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.extent'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.transform'); /** * @constructor * @abstract * @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; }; 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 && !ol.extent.containsExtent(extent, frameState.extent) && ol.extent.intersects(extent, frameState.extent); 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 ImageCanvas 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.ImageCanvas'); goog.require('ol.LayerType'); goog.require('ol.ViewHint'); goog.require('ol.array'); goog.require('ol.extent'); goog.require('ol.layer.VectorRenderType'); goog.require('ol.obj'); goog.require('ol.plugins'); goog.require('ol.renderer.Type'); goog.require('ol.renderer.canvas.IntermediateCanvas'); goog.require('ol.transform'); /** * @constructor * @extends {ol.renderer.canvas.IntermediateCanvas} * @param {ol.layer.Image} imageLayer Single image layer. * @api */ 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(); /** * @type {!Array.<string>} */ this.skippedFeatures_ = []; /** * @private * @type {ol.renderer.canvas.VectorLayer} */ this.vectorRenderer_ = null; }; ol.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.IntermediateCanvas); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.canvas.ImageLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.CANVAS && (layer.getType() === ol.LayerType.IMAGE || layer.getType() === ol.LayerType.VECTOR && /** @type {ol.layer.Vector} */ (layer).getRenderMode() === ol.layer.VectorRenderType.IMAGE); }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.canvas.ImageLayer} The layer renderer. */ ol.renderer.canvas.ImageLayer['create'] = function(mapRenderer, layer) { var renderer = new ol.renderer.canvas.ImageLayer(/** @type {ol.layer.Image} */ (layer)); if (layer.getType() === ol.LayerType.VECTOR) { var candidates = ol.plugins.getLayerRendererPlugins(); for (var i = 0, ii = candidates.length; i < ii; ++i) { var candidate = /** @type {Object.<string, Function>} */ (candidates[i]); if (candidate !== ol.renderer.canvas.ImageLayer && candidate['handles'](ol.renderer.Type.CANVAS, layer)) { renderer.setVectorRenderer(candidate['create'](mapRenderer, layer)); } } } return renderer; }; /** * @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.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && !ol.extent.isEmpty(renderedExtent)) { var projection = viewState.projection; if (!ol.ENABLE_RASTER_REPROJECTION) { var sourceProjection = imageSource.getProjection(); if (sourceProjection) { projection = sourceProjection; } } var vectorRenderer = this.vectorRenderer_; if (vectorRenderer) { var context = vectorRenderer.context; var imageFrameState = /** @type {olx.FrameState} */ (ol.obj.assign({}, frameState, { size: [ ol.extent.getWidth(renderedExtent) / viewResolution, ol.extent.getHeight(renderedExtent) / viewResolution ], viewState: /** @type {olx.ViewState} */ (ol.obj.assign({}, frameState.viewState, { rotation: 0 })) })); var skippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); if (vectorRenderer.prepareFrame(imageFrameState, layerState) && (vectorRenderer.replayGroupChanged || !ol.array.equals(skippedFeatures, this.skippedFeatures_))) { context.canvas.width = imageFrameState.size[0] * pixelRatio; context.canvas.height = imageFrameState.size[1] * pixelRatio; vectorRenderer.composeFrame(imageFrameState, layerState, context); this.image_ = new ol.ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas); this.skippedFeatures_ = skippedFeatures; } } else { image = imageSource.getImage( renderedExtent, viewResolution, pixelRatio, projection); if (image) { var loaded = this.loadImage(image); if (loaded) { this.image_ = image; } } } } 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.updateLogos(frameState, imageSource); this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio; } return !!this.image_; }; /** * @inheritDoc */ ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { if (this.vectorRenderer_) { return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg); } else { return ol.renderer.canvas.IntermediateCanvas.prototype.forEachFeatureAtCoordinate.call(this, coordinate, frameState, hitTolerance, callback, thisArg); } }; /** * @param {ol.renderer.canvas.VectorLayer} renderer Vector renderer. */ ol.renderer.canvas.ImageLayer.prototype.setVectorRenderer = function(renderer) { this.vectorRenderer_ = renderer; }; goog.provide('ol.style.IconImageCache'); goog.require('ol.color'); /** * Singleton class. Available through {@link ol.style.iconImageCache}. * @constructor */ ol.style.IconImageCache = function() { /** * @type {Object.<string, ol.style.IconImage>} * @private */ this.cache_ = {}; /** * @type {number} * @private */ this.cacheSize_ = 0; /** * @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) { 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_; }; /** * Set the cache size of the icon cache. Default is `32`. Change this value when * your map uses more than 32 different icon images and you are not caching icon * styles on the application level. * @param {number} maxCacheSize Cache max size. * @api */ ol.style.IconImageCache.prototype.setSize = function(maxCacheSize) { this.maxCacheSize_ = maxCacheSize; this.expire(); }; goog.provide('ol.style'); goog.require('ol.style.IconImageCache'); /** * The {@link ol.style.IconImageCache} for {@link ol.style.Icon} images. * @api */ ol.style.iconImageCache = new ol.style.IconImageCache(); 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.plugins'); goog.require('ol.style'); goog.require('ol.transform'); /** * @constructor * @abstract * @extends {ol.Disposable} * @param {Element} container Container. * @param {ol.PluggableMap} map Map. * @struct */ ol.renderer.Map = function(container, map) { ol.Disposable.call(this); /** * @private * @type {ol.PluggableMap} */ 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.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)); }; /** * Removes all layer renderers. */ ol.renderer.Map.prototype.removeLayerRenderers = function() { for (var key in this.layerRenderers_) { this.removeLayerRendererByKey_(key).dispose(); } }; /** * @param {ol.PluggableMap} 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 layerRendererPlugins = ol.plugins.getLayerRendererPlugins(); var renderer; var type = this.getType(); for (var i = 0, ii = layerRendererPlugins.length; i < ii; ++i) { var plugin = layerRendererPlugins[i]; if (plugin['handles'](type, layer)) { renderer = plugin['create'](this, layer); break; } } if (renderer) { this.layerRenderers_[layerKey] = renderer; this.layerRendererListeners_[layerKey] = ol.events.listen(renderer, ol.events.EventType.CHANGE, this.handleLayerRendererChange_, this); } else { throw new Error('Unable to create renderer for layer: ' + layer.getType()); } return renderer; } }; /** * @param {string} layerKey Layer key. * @protected * @return {ol.renderer.Layer} Layer renderer. */ ol.renderer.Map.prototype.getLayerRendererByKey = function(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.PluggableMap} Map. */ ol.renderer.Map.prototype.getMap = function() { return this.map_; }; /** * @abstract * @return {ol.renderer.Type} 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) { var layerRenderer = this.layerRenderers_[layerKey]; delete this.layerRenderers_[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.PluggableMap} 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; }; // 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.Layer'); goog.require('ol.render.Event'); goog.require('ol.render.EventType'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.Immediate'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.Type'); goog.require('ol.source.State'); /** * @constructor * @extends {ol.renderer.Map} * @param {Element} container Container. * @param {ol.PluggableMap} map Map. * @api */ 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_.style.display = 'block'; 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); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @return {boolean} The renderer can render the layer. */ ol.renderer.canvas.Map['handles'] = function(type) { return type === ol.renderer.Type.CANVAS; }; /** * Create the map renderer. * @param {Element} container Container. * @param {ol.PluggableMap} map Map. * @return {ol.renderer.canvas.Map} The map renderer. */ ol.renderer.canvas.Map['create'] = function(container, map) { return new ol.renderer.canvas.Map(container, map); }; /** * @param {ol.render.EventType} 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.EventType.PRECOMPOSE, frameState); var layerStatesArray = frameState.layerStatesArray; ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); if (rotation) { context.save(); 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); } } if (rotation) { context.restore(); } this.dispatchComposeEvent_( ol.render.EventType.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.renderer.canvas.TileLayer'); goog.require('ol'); goog.require('ol.LayerType'); goog.require('ol.TileRange'); goog.require('ol.TileState'); goog.require('ol.ViewHint'); goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.renderer.Type'); goog.require('ol.renderer.canvas.IntermediateCanvas'); goog.require('ol.transform'); /** * @constructor * @extends {ol.renderer.canvas.IntermediateCanvas} * @param {ol.layer.Tile|ol.layer.VectorTile} tileLayer Tile layer. * @api */ ol.renderer.canvas.TileLayer = function(tileLayer) { ol.renderer.canvas.IntermediateCanvas.call(this, tileLayer); /** * @protected * @type {CanvasRenderingContext2D} */ this.context = this.context === null ? null : ol.dom.createCanvasContext2D(); /** * @private * @type {number} */ this.oversampling_; /** * @private * @type {ol.Extent} */ this.renderedExtent_ = null; /** * @protected * @type {number} */ this.renderedRevision; /** * @protected * @type {!Array.<ol.Tile>} */ this.renderedTiles = []; /** * @protected * @type {ol.Extent} */ this.tmpExtent = ol.extent.createEmpty(); /** * @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); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.canvas.TileLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.CANVAS && layer.getType() === ol.LayerType.TILE; }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.canvas.TileLayer} The layer renderer. */ ol.renderer.canvas.TileLayer['create'] = function(mapRenderer, layer) { return new ol.renderer.canvas.TileLayer(/** @type {ol.layer.Tile} */ (layer)); }; /** * @private * @param {ol.Tile} tile Tile. * @return {boolean} Tile is drawable. */ ol.renderer.canvas.TileLayer.prototype.isDrawableTile_ = function(tile) { var tileState = tile.getState(); var useInterimTilesOnError = this.getLayer().getUseInterimTilesOnError(); return tileState == ol.TileState.LOADED || tileState == ol.TileState.EMPTY || tileState == ol.TileState.ERROR && !useInterimTilesOnError; }; /** * @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 oversampling = Math.round(viewResolution / tileResolution) || 1; 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.getTileRangeForExtentAndZ(extent, z); 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 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); if (tile.getState() == ol.TileState.ERROR) { if (!tileLayer.getUseInterimTilesOnError()) { // When useInterimTilesOnError is false, we consider the error tile as loaded. tile.setState(ol.TileState.LOADED); } else if (tileLayer.getPreload() > 0) { // Preloaded tiles for lower resolutions might have finished loading. newTiles = true; } } if (!this.isDrawableTile_(tile)) { tile = tile.getInterimTile(); } if (this.isDrawableTile_(tile)) { var uid = ol.getUid(this); if (tile.getState() == ol.TileState.LOADED) { tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; var inTransition = tile.inTransition(uid); if (!newTiles && (inTransition || this.renderedTiles.indexOf(tile) === -1)) { newTiles = true; } } if (tile.getAlpha(uid, frameState.time) === 1) { // don't look for alt tiles if alpha is 1 continue; } } var childTileRange = tileGrid.getTileCoordChildTileRange( tile.tileCoord, tmpTileRange, tmpExtent); var covered = false; if (childTileRange) { covered = findLoadedTiles(z + 1, childTileRange); } if (!covered) { tileGrid.forEachTileCoordParentTileRange( tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); } } } var renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; var hints = frameState.viewHints; var animatingOrInteracting = hints[ol.ViewHint.ANIMATING] || hints[ol.ViewHint.INTERACTING]; if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && ( newTiles || !(this.renderedExtent_ && ol.extent.containsExtent(this.renderedExtent_, extent)) || this.renderedRevision != sourceRevision || oversampling != this.oversampling_ || !animatingOrInteracting && renderedResolution != this.renderedResolution )) { var context = this.context; if (context) { var tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection); var width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling); var height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling); var canvas = context.canvas; if (canvas.width != width || canvas.height != height) { this.oversampling_ = oversampling; canvas.width = width; canvas.height = height; } else { if (this.renderedExtent_ && !ol.extent.equals(imageExtent, this.renderedExtent_)) { context.clearRect(0, 0, width, height); } oversampling = this.oversampling_; } } this.renderedTiles.length = 0; /** @type {Array.<number>} */ var zs = Object.keys(tilesToDrawByZ).map(Number); zs.sort(function(a, b) { if (a === z) { return 1; } else if (b === z) { return -1; } else { return a > b ? 1 : a < b ? -1 : 0; } }); 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 / oversampling; y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling; w = currentTilePixelSize[0] * currentScale / oversampling; h = currentTilePixelSize[1] * currentScale / oversampling; this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ); this.renderedTiles.push(tile); } } this.renderedRevision = sourceRevision; this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; this.renderedExtent_ = imageExtent; } var scale = this.renderedResolution / viewResolution; var transform = ol.transform.compose(this.imageTransform_, pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, scale, scale, 0, (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio, (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio); 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. * @param {boolean} transition Apply an alpha transition. */ ol.renderer.canvas.TileLayer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter, transition) { var image = tile.getImage(this.getLayer()); if (!image) { return; } var uid = ol.getUid(this); var alpha = transition ? tile.getAlpha(uid, frameState.time) : 1; if (alpha === 1 && !this.getLayer().getSource().getOpaque(frameState.viewState.projection)) { this.context.clearRect(x, y, w, h); } var alphaChanged = alpha !== this.context.globalAlpha; if (alphaChanged) { this.context.save(); this.context.globalAlpha = alpha; } this.context.drawImage(image, gutter, gutter, image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h); if (alphaChanged) { this.context.restore(); } if (alpha !== 1) { frameState.animate = true; } else if (transition) { tile.endTransition(uid); } }; /** * @inheritDoc */ ol.renderer.canvas.TileLayer.prototype.getImage = function() { var context = this.context; return context ? context.canvas : null; }; /** * @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_; }; /** * @fileoverview * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} */ goog.provide('ol.ext.rbush'); /** @typedef {function(*)} */ ol.ext.rbush = function() {}; (function() {(function (exports) { 'use strict'; var quickselect = partialSort; 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; } var rbush_1 = rbush; function rbush(maxEntries, format) { if (!(this instanceof rbush)) return new rbush(maxEntries, format); 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; } var node = this._build(data.slice(), 0, data.length - 1, 0); if (!this.data.children.length) { this.data = node; } else if (this.data.height === node.height) { this._splitRoot(this.data, node); } else { if (this.data.height < node.height) { var tmpNode = this.data; this.data = node; node = tmpNode; } 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; while (node || path.length) { if (!node) { node = path.pop(); parent = path[path.length - 1]; i = indexes.pop(); goingUp = true; } if (node.leaf) { index = findItem(item, node.children, equalsFn); if (index !== -1) { node.children.splice(index, 1); path.push(node); this._condense(path); return this; } } if (!goingUp && !node.leaf && contains(node, bbox)) { path.push(node); indexes.push(i); i = 0; parent = node; node = node.children[0]; } else if (parent) { i++; node = parent.children[i]; goingUp = false; } else node = null; } 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) { node = createNode(items.slice(left, right + 1)); calcBBox(node, this.toBBox); return node; } if (!height) { height = Math.ceil(Math.log(N) / Math.log(M)); M = Math.ceil(N / Math.pow(M, height - 1)); } node = createNode([]); node.leaf = false; node.height = height; 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); 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; if (enlargement < minEnlargement) { minEnlargement = enlargement; minArea = area < minArea ? area : minArea; targetNode = child; } else if (enlargement === minEnlargement) { 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 = []; var node = this._chooseSubtree(bbox, this.data, level, insertPath); node.children.push(item); extend(node, bbox); while (level >= 0) { if (insertPath[level].children.length > this._maxEntries) { this._split(insertPath, level); level--; } else break; } this._adjustParentBBoxes(bbox, insertPath, level); }, _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) { 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); if (overlap < minOverlap) { minOverlap = overlap; index = i; minArea = area < minArea ? area : minArea; } else if (overlap === minOverlap) { if (area < minArea) { minArea = area; index = i; } } } return index; }, _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 (xMargin < yMargin) node.children.sort(compareMinX); }, _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) { for (var i = level; i >= 0; i--) { extend(path[i], bbox); } }, _condense: function (path) { 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) { 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; } function calcBBox(node, toBBox) { distBBox(node, 0, node.children.length, toBBox, node); } 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 }; } 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); } } exports['default'] = rbush_1; }((this.rbush = this.rbush || {})));}).call(ol.ext); ol.ext.rbush = ol.ext.rbush.default; goog.provide('ol.render.ReplayGroup'); /** * Base class for replay groups. * @constructor * @abstract */ 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.ReplayType'); /** * @enum {string} */ ol.render.ReplayType = { CIRCLE: 'Circle', DEFAULT: 'Default', IMAGE: 'Image', LINE_STRING: 'LineString', POLYGON: 'Polygon', TEXT: 'Text' }; 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.flat.textpath'); goog.require('ol.math'); /** * @param {Array.<number>} flatCoordinates Path to put text on. * @param {number} offset Start offset of the `flatCoordinates`. * @param {number} end End offset of the `flatCoordinates`. * @param {number} stride Stride. * @param {string} text Text to place on the path. * @param {function(string):number} measure Measure function returning the * width of the character passed as 1st argument. * @param {number} startM m along the path where the text starts. * @param {number} maxAngle Max angle between adjacent chars in radians. * @return {Array.<Array.<*>>} The result array of null if `maxAngle` was * exceeded. Entries of the array are x, y, anchorX, angle, chunk. */ ol.geom.flat.textpath.lineString = function( flatCoordinates, offset, end, stride, text, measure, startM, maxAngle) { var result = []; // Keep text upright var reverse = flatCoordinates[offset] > flatCoordinates[end - stride]; var numChars = text.length; var x1 = flatCoordinates[offset]; var y1 = flatCoordinates[offset + 1]; offset += stride; var x2 = flatCoordinates[offset]; var y2 = flatCoordinates[offset + 1]; var segmentM = 0; var segmentLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); var chunk = ''; var chunkLength = 0; var data, index, previousAngle; for (var i = 0; i < numChars; ++i) { index = reverse ? numChars - i - 1 : i; var char = text.charAt(index); chunk = reverse ? char + chunk : chunk + char; var charLength = measure(chunk) - chunkLength; chunkLength += charLength; var charM = startM + charLength / 2; while (offset < end - stride && segmentM + segmentLength < charM) { x1 = x2; y1 = y2; offset += stride; x2 = flatCoordinates[offset]; y2 = flatCoordinates[offset + 1]; segmentM += segmentLength; segmentLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } var segmentPos = charM - segmentM; var angle = Math.atan2(y2 - y1, x2 - x1); if (reverse) { angle += angle > 0 ? -Math.PI : Math.PI; } if (previousAngle !== undefined) { var delta = angle - previousAngle; delta += (delta > Math.PI) ? -2 * Math.PI : (delta < -Math.PI) ? 2 * Math.PI : 0; if (Math.abs(delta) > maxAngle) { return null; } } var interpolate = segmentPos / segmentLength; var x = ol.math.lerp(x1, x2, interpolate); var y = ol.math.lerp(y1, y2, interpolate); if (previousAngle == angle) { if (reverse) { data[0] = x; data[1] = y; data[2] = charLength / 2; } data[4] = chunk; } else { chunk = char; chunkLength = charLength; data = [x, y, charLength / 2, angle, chunk]; if (reverse) { result.unshift(data); } else { result.push(data); } previousAngle = angle; } startM += charLength; } return result; }; goog.provide('ol.render.canvas.Instruction'); /** * @enum {number} */ ol.render.canvas.Instruction = { BEGIN_GEOMETRY: 0, BEGIN_PATH: 1, CIRCLE: 2, CLOSE_PATH: 3, CUSTOM: 4, DRAW_CHARS: 5, DRAW_IMAGE: 6, END_GEOMETRY: 7, FILL: 8, MOVE_TO_LINE_TO: 9, SET_FILL_STYLE: 10, SET_STROKE_STYLE: 11, STROKE: 12 }; 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, ol.render.ReplayType.DEFAULT ]; /** * @const * @enum {number} */ ol.render.replay.TEXT_ALIGN = {}; ol.render.replay.TEXT_ALIGN['left'] = 0; ol.render.replay.TEXT_ALIGN['end'] = 0; ol.render.replay.TEXT_ALIGN['center'] = 0.5; ol.render.replay.TEXT_ALIGN['right'] = 1; ol.render.replay.TEXT_ALIGN['start'] = 1; ol.render.replay.TEXT_ALIGN['top'] = 0; ol.render.replay.TEXT_ALIGN['middle'] = 0.5; ol.render.replay.TEXT_ALIGN['hanging'] = 0.2; ol.render.replay.TEXT_ALIGN['alphabetic'] = 0.8; ol.render.replay.TEXT_ALIGN['ideographic'] = 0.8; ol.render.replay.TEXT_ALIGN['bottom'] = 1; 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.GeometryType'); goog.require('ol.geom.flat.inflate'); goog.require('ol.geom.flat.length'); goog.require('ol.geom.flat.textpath'); 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.render.replay'); goog.require('ol.transform'); /** * @constructor * @extends {ol.render.VectorContext} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. * @struct */ ol.render.canvas.Replay = function(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { ol.render.VectorContext.call(this); /** * @type {?} */ this.declutterTree = declutterTree; /** * @private * @type {ol.Extent} */ this.tmpExtent_ = ol.extent.createEmpty(); /** * @protected * @type {number} */ this.tolerance = tolerance; /** * @protected * @const * @type {ol.Extent} */ this.maxExtent = maxExtent; /** * @protected * @type {boolean} */ this.overlaps = overlaps; /** * @protected * @type {number} */ this.pixelRatio = pixelRatio; /** * @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; /** * @private * @type {ol.Extent} */ this.bufferedMaxExtent_ = null; /** * @protected * @type {Array.<*>} */ this.instructions = []; /** * @protected * @type {Array.<number>} */ this.coordinates = []; /** * @private * @type {Object.<number,ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>>} */ this.coordinateCache_ = {}; /** * @private * @type {!ol.Transform} */ this.renderedTransform_ = ol.transform.create(); /** * @protected * @type {Array.<*>} */ this.hitDetectionInstructions = []; /** * @private * @type {Array.<number>} */ this.pixelCoordinates_ = null; /** * @protected * @type {ol.CanvasFillStrokeState} */ this.state = /** @type {ol.CanvasFillStrokeState} */ ({}); /** * @private * @type {number} */ this.viewRotation_ = 0; /** * @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 {CanvasRenderingContext2D} context Context. * @param {ol.Coordinate} p1 1st point of the background box. * @param {ol.Coordinate} p2 2nd point of the background box. * @param {ol.Coordinate} p3 3rd point of the background box. * @param {ol.Coordinate} p4 4th point of the background box. * @param {Array.<*>} fillInstruction Fill instruction. * @param {Array.<*>} strokeInstruction Stroke instruction. */ ol.render.canvas.Replay.prototype.replayTextBackground_ = function(context, p1, p2, p3, p4, fillInstruction, strokeInstruction) { context.beginPath(); context.moveTo.apply(context, p1); context.lineTo.apply(context, p2); context.lineTo.apply(context, p3); context.lineTo.apply(context, p4); context.lineTo.apply(context, p1); if (fillInstruction) { this.fillOrigin_ = /** @type {Array.<number>} */ (fillInstruction[2]); this.fill_(context); } if (strokeInstruction) { this.setStrokeStyle_(context, /** @type {Array.<*>} */ (strokeInstruction)); context.stroke(); } }; /** * @param {CanvasRenderingContext2D} context Context. * @param {number} x X. * @param {number} y Y. * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image. * @param {number} anchorX Anchor X. * @param {number} anchorY Anchor Y. * @param {ol.DeclutterGroup} declutterGroup Declutter group. * @param {number} height Height. * @param {number} opacity Opacity. * @param {number} originX Origin X. * @param {number} originY Origin Y. * @param {number} rotation Rotation. * @param {number} scale Scale. * @param {boolean} snapToPixel Snap to pixel. * @param {number} width Width. * @param {Array.<number>} padding Padding. * @param {Array.<*>} fillInstruction Fill instruction. * @param {Array.<*>} strokeInstruction Stroke instruction. */ ol.render.canvas.Replay.prototype.replayImage_ = function(context, x, y, image, anchorX, anchorY, declutterGroup, height, opacity, originX, originY, rotation, scale, snapToPixel, width, padding, fillInstruction, strokeInstruction) { var fillStroke = fillInstruction || strokeInstruction; var localTransform = this.tmpLocalTransform_; anchorX *= scale; anchorY *= scale; x -= anchorX; y -= anchorY; if (snapToPixel) { x = Math.round(x); y = Math.round(y); } var w = (width + originX > image.width) ? image.width - originX : width; var h = (height + originY > image.height) ? image.height - originY : height; var box = this.tmpExtent_; var boxW = padding[3] + w * scale + padding[1]; var boxH = padding[0] + h * scale + padding[2]; var boxX = x - padding[3]; var boxY = y - padding[0]; /** @type {ol.Coordinate} */ var p1; /** @type {ol.Coordinate} */ var p2; /** @type {ol.Coordinate} */ var p3; /** @type {ol.Coordinate} */ var p4; if (fillStroke || rotation !== 0) { p1 = [boxX, boxY]; p2 = [boxX + boxW, boxY]; p3 = [boxX + boxW, boxY + boxH]; p4 = [boxX, boxY + boxH]; } var transform = null; if (rotation !== 0) { var centerX = x + anchorX; var centerY = y + anchorY; transform = ol.transform.compose(localTransform, centerX, centerY, 1, 1, rotation, -centerX, -centerY); ol.extent.createOrUpdateEmpty(box); ol.extent.extendCoordinate(box, ol.transform.apply(localTransform, p1)); ol.extent.extendCoordinate(box, ol.transform.apply(localTransform, p2)); ol.extent.extendCoordinate(box, ol.transform.apply(localTransform, p3)); ol.extent.extendCoordinate(box, ol.transform.apply(localTransform, p4)); } else { ol.extent.createOrUpdate(boxX, boxY, boxX + boxW, boxY + boxH, box); } var canvas = context.canvas; var intersects = box[0] <= canvas.width && box[2] >= 0 && box[1] <= canvas.height && box[3] >= 0; if (declutterGroup) { if (!intersects && declutterGroup[4] == 1) { return; } ol.extent.extend(declutterGroup, box); var declutterArgs = intersects ? [context, transform ? transform.slice(0) : null, opacity, image, originX, originY, w, h, x, y, scale] : null; if (declutterArgs && fillStroke) { declutterArgs.push(fillInstruction, strokeInstruction, p1, p2, p3, p4); } declutterGroup.push(declutterArgs); } else if (intersects) { if (fillStroke) { this.replayTextBackground_(context, p1, p2, p3, p4, /** @type {Array.<*>} */ (fillInstruction), /** @type {Array.<*>} */ (strokeInstruction)); } ol.render.canvas.drawImage(context, transform, opacity, image, originX, originY, w, h, x, y, scale); } }; /** * @protected * @param {Array.<number>} dashArray Dash array. * @return {Array.<number>} Dash array with pixel ratio applied */ ol.render.canvas.Replay.prototype.applyPixelRatio = function(dashArray) { var pixelRatio = this.pixelRatio; return pixelRatio == 1 ? dashArray : dashArray.map(function(dash) { return dash * pixelRatio; }); }; /** * @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; }; /** * @param {Array.<number>} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {Array.<number>} ends Ends. * @param {number} stride Stride. * @param {Array.<number>} replayEnds Replay ends. * @return {number} Offset. */ ol.render.canvas.Replay.prototype.drawCustomCoordinates_ = function(flatCoordinates, offset, ends, stride, replayEnds) { for (var i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var replayEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); replayEnds.push(replayEnd); offset = end; } return offset; }; /** * @inheritDoc. */ ol.render.canvas.Replay.prototype.drawCustom = function(geometry, feature, renderer) { this.beginGeometry(geometry, feature); var type = geometry.getType(); var stride = geometry.getStride(); var replayBegin = this.coordinates.length; var flatCoordinates, replayEnd, replayEnds, replayEndss; var offset; if (type == ol.geom.GeometryType.MULTI_POLYGON) { geometry = /** @type {ol.geom.MultiPolygon} */ (geometry); flatCoordinates = geometry.getOrientedFlatCoordinates(); replayEndss = []; var endss = geometry.getEndss(); offset = 0; for (var i = 0, ii = endss.length; i < ii; ++i) { var myEnds = []; offset = this.drawCustomCoordinates_(flatCoordinates, offset, endss[i], stride, myEnds); replayEndss.push(myEnds); } this.instructions.push([ol.render.canvas.Instruction.CUSTOM, replayBegin, replayEndss, geometry, renderer, ol.geom.flat.inflate.coordinatesss]); } else if (type == ol.geom.GeometryType.POLYGON || type == ol.geom.GeometryType.MULTI_LINE_STRING) { replayEnds = []; flatCoordinates = (type == ol.geom.GeometryType.POLYGON) ? /** @type {ol.geom.Polygon} */ (geometry).getOrientedFlatCoordinates() : geometry.getFlatCoordinates(); offset = this.drawCustomCoordinates_(flatCoordinates, 0, /** @type {ol.geom.Polygon|ol.geom.MultiLineString} */ (geometry).getEnds(), stride, replayEnds); this.instructions.push([ol.render.canvas.Instruction.CUSTOM, replayBegin, replayEnds, geometry, renderer, ol.geom.flat.inflate.coordinatess]); } else if (type == ol.geom.GeometryType.LINE_STRING || type == ol.geom.GeometryType.MULTI_POINT) { flatCoordinates = geometry.getFlatCoordinates(); replayEnd = this.appendFlatCoordinates( flatCoordinates, 0, flatCoordinates.length, stride, false, false); this.instructions.push([ol.render.canvas.Instruction.CUSTOM, replayBegin, replayEnd, geometry, renderer, ol.geom.flat.inflate.coordinates]); } else if (type == ol.geom.GeometryType.POINT) { flatCoordinates = geometry.getFlatCoordinates(); this.coordinates.push(flatCoordinates[0], flatCoordinates[1]); replayEnd = this.coordinates.length; this.instructions.push([ol.render.canvas.Instruction.CUSTOM, replayBegin, replayEnd, geometry, renderer]); } this.endGeometry(geometry, feature); }; /** * @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. */ ol.render.canvas.Replay.prototype.fill_ = function(context) { if (this.fillOrigin_) { var origin = ol.transform.apply(this.renderedTransform_, this.fillOrigin_.slice()); context.translate(origin[0], origin[1]); context.rotate(this.viewRotation_); } context.fill(); if (this.fillOrigin_) { context.setTransform.apply(context, ol.render.canvas.resetTransform_); } }; /** * @private * @param {CanvasRenderingContext2D} context Context. * @param {Array.<*>} instruction Instruction. */ ol.render.canvas.Replay.prototype.setStrokeStyle_ = function(context, instruction) { context.strokeStyle = /** @type {ol.ColorLike} */ (instruction[1]); context.lineWidth = /** @type {number} */ (instruction[2]); context.lineCap = /** @type {string} */ (instruction[3]); context.lineJoin = /** @type {string} */ (instruction[4]); context.miterLimit = /** @type {number} */ (instruction[5]); if (ol.has.CANVAS_LINE_DASH) { context.lineDashOffset = /** @type {number} */ (instruction[7]); context.setLineDash(/** @type {Array.<number>} */ (instruction[6])); } }; /** * @param {ol.DeclutterGroup} declutterGroup Declutter group. * @param {ol.Feature|ol.render.Feature} feature Feature. */ ol.render.canvas.Replay.prototype.renderDeclutter_ = function(declutterGroup, feature) { if (declutterGroup && declutterGroup.length > 5) { var groupCount = declutterGroup[4]; if (groupCount == 1 || groupCount == declutterGroup.length - 5) { /** @type {ol.RBushEntry} */ var box = { minX: /** @type {number} */ (declutterGroup[0]), minY: /** @type {number} */ (declutterGroup[1]), maxX: /** @type {number} */ (declutterGroup[2]), maxY: /** @type {number} */ (declutterGroup[3]), value: feature }; if (!this.declutterTree.collides(box)) { this.declutterTree.insert(box); var drawImage = ol.render.canvas.drawImage; for (var j = 5, jj = declutterGroup.length; j < jj; ++j) { var declutterData = /** @type {Array} */ (declutterGroup[j]); if (declutterData) { if (declutterData.length > 11) { this.replayTextBackground_(declutterData[0], declutterData[13], declutterData[14], declutterData[15], declutterData[16], declutterData[11], declutterData[12]); } drawImage.apply(undefined, declutterData); } } } declutterGroup.length = 5; ol.extent.createOrUpdateEmpty(declutterGroup); } } }; /** * @private * @param {CanvasRenderingContext2D} context Context. * @param {ol.Transform} transform Transform. * @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, transform, skippedFeaturesHash, instructions, featureCallback, opt_hitExtent) { /** @type {Array.<number>} */ var pixelCoordinates; if (this.pixelCoordinates_ && ol.array.equals(transform, this.renderedTransform_)) { pixelCoordinates = this.pixelCoordinates_; } else { if (!this.pixelCoordinates_) { this.pixelCoordinates_ = []; } pixelCoordinates = ol.geom.flat.transform.transform2D( this.coordinates, 0, this.coordinates.length, 2, transform, this.pixelCoordinates_); ol.transform.setFromArray(this.renderedTransform_, transform); } 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 anchorX, anchorY, prevX, prevY, roundX, roundY, declutterGroup, image; var pendingFill = 0; var pendingStroke = 0; var lastFillInstruction = null; var lastStrokeInstruction = null; var coordinateCache = this.coordinateCache_; var viewRotation = this.viewRotation_; var state = /** @type {olx.render.State} */ ({ context: context, pixelRatio: this.pixelRatio, resolution: this.resolution, rotation: viewRotation }); // 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 /** @type {ol.Feature|ol.render.Feature} */ feature, 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); pendingFill = 0; } if (pendingStroke > batchSize) { context.stroke(); pendingStroke = 0; } if (!pendingFill && !pendingStroke) { context.beginPath(); prevX = prevY = NaN; } ++i; break; case ol.render.canvas.Instruction.CIRCLE: 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.CUSTOM: d = /** @type {number} */ (instruction[1]); dd = instruction[2]; var geometry = /** @type {ol.geom.SimpleGeometry} */ (instruction[3]); var renderer = instruction[4]; var fn = instruction.length == 6 ? instruction[5] : undefined; state.geometry = geometry; state.feature = feature; if (!(i in coordinateCache)) { coordinateCache[i] = []; } var coords = coordinateCache[i]; if (fn) { fn(pixelCoordinates, d, dd, 2, coords); } else { coords[0] = pixelCoordinates[d]; coords[1] = pixelCoordinates[d + 1]; coords.length = 2; } renderer(coords, state); ++i; break; case ol.render.canvas.Instruction.DRAW_IMAGE: d = /** @type {number} */ (instruction[1]); dd = /** @type {number} */ (instruction[2]); image = /** @type {HTMLCanvasElement|HTMLVideoElement|Image} */ (instruction[3]); // Remaining arguments in DRAW_IMAGE are in alphabetical order anchorX = /** @type {number} */ (instruction[4]); anchorY = /** @type {number} */ (instruction[5]); declutterGroup = featureCallback ? null : /** @type {ol.DeclutterGroup} */ (instruction[6]); var height = /** @type {number} */ (instruction[7]); var opacity = /** @type {number} */ (instruction[8]); var originX = /** @type {number} */ (instruction[9]); var originY = /** @type {number} */ (instruction[10]); var rotateWithView = /** @type {boolean} */ (instruction[11]); var rotation = /** @type {number} */ (instruction[12]); var scale = /** @type {number} */ (instruction[13]); var snapToPixel = /** @type {boolean} */ (instruction[14]); var width = /** @type {number} */ (instruction[15]); var padding, backgroundFill, backgroundStroke; if (instruction.length > 16) { padding = /** @type {Array.<number>} */ (instruction[16]); backgroundFill = /** @type {boolean} */ (instruction[17]); backgroundStroke = /** @type {boolean} */ (instruction[18]); } else { padding = ol.render.canvas.defaultPadding; backgroundFill = backgroundStroke = false; } if (rotateWithView) { rotation += viewRotation; } for (; d < dd; d += 2) { this.replayImage_(context, pixelCoordinates[d], pixelCoordinates[d + 1], image, anchorX, anchorY, declutterGroup, height, opacity, originX, originY, rotation, scale, snapToPixel, width, padding, backgroundFill ? /** @type {Array.<*>} */ (lastFillInstruction) : null, backgroundStroke ? /** @type {Array.<*>} */ (lastStrokeInstruction) : null); } this.renderDeclutter_(declutterGroup, feature); ++i; break; case ol.render.canvas.Instruction.DRAW_CHARS: var begin = /** @type {number} */ (instruction[1]); var end = /** @type {number} */ (instruction[2]); var baseline = /** @type {number} */ (instruction[3]); declutterGroup = featureCallback ? null : /** @type {ol.DeclutterGroup} */ (instruction[4]); var overflow = /** @type {number} */ (instruction[5]); var fillKey = /** @type {string} */ (instruction[6]); var maxAngle = /** @type {number} */ (instruction[7]); var measure = /** @type {function(string):number} */ (instruction[8]); var offsetY = /** @type {number} */ (instruction[9]); var strokeKey = /** @type {string} */ (instruction[10]); var strokeWidth = /** @type {number} */ (instruction[11]); var text = /** @type {string} */ (instruction[12]); var textKey = /** @type {string} */ (instruction[13]); var textScale = /** @type {number} */ (instruction[14]); var pathLength = ol.geom.flat.length.lineString(pixelCoordinates, begin, end, 2); var textLength = measure(text); if (overflow || textLength <= pathLength) { var textAlign = /** @type {ol.render.canvas.TextReplay} */ (this).textStates[textKey].textAlign; var startM = (pathLength - textLength) * ol.render.replay.TEXT_ALIGN[textAlign]; var parts = ol.geom.flat.textpath.lineString( pixelCoordinates, begin, end, 2, text, measure, startM, maxAngle); if (parts) { var c, cc, chars, label, part; if (strokeKey) { for (c = 0, cc = parts.length; c < cc; ++c) { part = parts[c]; // x, y, anchorX, rotation, chunk chars = /** @type {string} */ (part[4]); label = /** @type {ol.render.canvas.TextReplay} */ (this).getImage(chars, textKey, '', strokeKey); anchorX = /** @type {number} */ (part[2]) + strokeWidth; anchorY = baseline * label.height + (0.5 - baseline) * 2 * strokeWidth - offsetY; this.replayImage_(context, /** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label, anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, /** @type {number} */ (part[3]), textScale, false, label.width, ol.render.canvas.defaultPadding, null, null); } } if (fillKey) { for (c = 0, cc = parts.length; c < cc; ++c) { part = parts[c]; // x, y, anchorX, rotation, chunk chars = /** @type {string} */ (part[4]); label = /** @type {ol.render.canvas.TextReplay} */ (this).getImage(chars, textKey, fillKey, ''); anchorX = /** @type {number} */ (part[2]); anchorY = baseline * label.height - offsetY; this.replayImage_(context, /** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label, anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, /** @type {number} */ (part[3]), textScale, false, label.width, ol.render.canvas.defaultPadding, null, null); } } } } this.renderDeclutter_(declutterGroup, feature); ++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); } ++i; break; case ol.render.canvas.Instruction.MOVE_TO_LINE_TO: d = /** @type {number} */ (instruction[1]); 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: lastFillInstruction = instruction; this.fillOrigin_ = instruction[2]; if (pendingFill) { this.fill_(context); pendingFill = 0; if (pendingStroke) { context.stroke(); pendingStroke = 0; } } context.fillStyle = /** @type {ol.ColorLike} */ (instruction[1]); ++i; break; case ol.render.canvas.Instruction.SET_STROKE_STYLE: lastStrokeInstruction = instruction; if (pendingStroke) { context.stroke(); pendingStroke = 0; } this.setStrokeStyle_(context, /** @type {Array.<*>} */ (instruction)); ++i; break; case ol.render.canvas.Instruction.STROKE: if (batchSize) { pendingStroke++; } else { context.stroke(); } ++i; break; default: ++i; // consume the instruction anyway, to avoid an infinite loop break; } } if (pendingFill) { this.fill_(context); } if (pendingStroke) { context.stroke(); } return 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. */ ol.render.canvas.Replay.prototype.replay = function( context, transform, viewRotation, skippedFeaturesHash) { this.viewRotation_ = viewRotation; this.replay_(context, transform, skippedFeaturesHash, this.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) { this.viewRotation_ = viewRotation; return this.replay_(context, transform, skippedFeaturesHash, this.hitDetectionInstructions, 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) { begin = i; } else if (type == ol.render.canvas.Instruction.BEGIN_GEOMETRY) { instruction[2] = i; ol.array.reverseSubArray(this.hitDetectionInstructions, begin, i); begin = -1; } } }; /** * @inheritDoc */ ol.render.canvas.Replay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { 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 strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); state.lineDashOffset = strokeStyleLineDashOffset ? strokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset; 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.lineDashOffset = undefined; state.lineJoin = undefined; state.lineWidth = undefined; state.miterLimit = undefined; } }; /** * @param {ol.CanvasFillStrokeState} state State. * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. */ ol.render.canvas.Replay.prototype.applyFill = function(state, geometry) { var fillStyle = state.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); }; /** * @param {ol.CanvasFillStrokeState} state State. */ ol.render.canvas.Replay.prototype.applyStroke = function(state) { this.instructions.push([ ol.render.canvas.Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth * this.pixelRatio, state.lineCap, state.lineJoin, state.miterLimit, this.applyPixelRatio(state.lineDash), state.lineDashOffset * this.pixelRatio ]); }; /** * @param {ol.CanvasFillStrokeState} state State. * @param {function(this:ol.render.canvas.Replay, ol.CanvasFillStrokeState, (ol.geom.Geometry|ol.render.Feature))} applyFill Apply fill. * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. */ ol.render.canvas.Replay.prototype.updateFillStyle = function(state, applyFill, geometry) { var fillStyle = state.fillStyle; if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) { applyFill.call(this, state, geometry); state.currentFillStyle = fillStyle; } }; /** * @param {ol.CanvasFillStrokeState} state State. * @param {function(this:ol.render.canvas.Replay, ol.CanvasFillStrokeState)} applyStroke Apply stroke. */ ol.render.canvas.Replay.prototype.updateStrokeStyle = function(state, applyStroke) { var strokeStyle = state.strokeStyle; var lineCap = state.lineCap; var lineDash = state.lineDash; var lineDashOffset = state.lineDashOffset; var lineJoin = state.lineJoin; var lineWidth = state.lineWidth; var miterLimit = state.miterLimit; if (state.currentStrokeStyle != strokeStyle || state.currentLineCap != lineCap || (lineDash != state.currentLineDash && !ol.array.equals(state.currentLineDash, lineDash)) || state.currentLineDashOffset != lineDashOffset || state.currentLineJoin != lineJoin || state.currentLineWidth != lineWidth || state.currentMiterLimit != miterLimit) { applyStroke.call(this, state); state.currentStrokeStyle = strokeStyle; state.currentLineCap = lineCap; state.currentLineDash = lineDash; state.currentLineDashOffset = lineDashOffset; state.currentLineJoin = lineJoin; state.currentLineWidth = lineWidth; state.currentMiterLimit = miterLimit; } }; /** * @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) { this.beginGeometryInstruction1_[2] = this.instructions.length; this.beginGeometryInstruction1_ = 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() { 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_; }; 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 {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. * @struct */ ol.render.canvas.ImageReplay = function( tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); /** * @private * @type {ol.DeclutterGroup} */ this.declutterGroup_ = null; /** * @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; } 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.declutterGroup_, this.height_, this.opacity_, this.originX_, this.originY_, this.rotateWithView_, this.rotation_, this.scale_ * this.pixelRatio, 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.declutterGroup_, 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; } 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.declutterGroup_, this.height_, this.opacity_, this.originX_, this.originY_, this.rotateWithView_, this.rotation_, this.scale_ * this.pixelRatio, 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.declutterGroup_, 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, declutterGroup) { var anchor = imageStyle.getAnchor(); var size = imageStyle.getSize(); var hitDetectionImage = imageStyle.getHitDetectionImage(1); var image = imageStyle.getImage(1); var origin = imageStyle.getOrigin(); this.anchorX_ = anchor[0]; this.anchorY_ = anchor[1]; this.declutterGroup_ = /** @type {ol.DeclutterGroup} */ (declutterGroup); 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.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 {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. * @struct */ ol.render.canvas.LineStringReplay = function( tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); }; 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.drawLineString = function(lineStringGeometry, feature) { var state = this.state; var strokeStyle = state.strokeStyle; var lineWidth = state.lineWidth; if (strokeStyle === undefined || lineWidth === undefined) { return; } this.updateStrokeStyle(state, this.applyStroke); 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, state.lineDashOffset ], [ 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; var strokeStyle = state.strokeStyle; var lineWidth = state.lineWidth; if (strokeStyle === undefined || lineWidth === undefined) { return; } this.updateStrokeStyle(state, this.applyStroke); 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, state.lineDashOffset ], [ 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; if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) { this.instructions.push([ol.render.canvas.Instruction.STROKE]); } this.reverseHitDetectionInstructions(); this.state = null; }; /** * @inheritDoc. */ ol.render.canvas.LineStringReplay.prototype.applyStroke = function(state) { if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) { this.instructions.push([ol.render.canvas.Instruction.STROKE]); state.lastStroke = this.coordinates.length; } state.lastStroke = 0; ol.render.canvas.Replay.prototype.applyStroke.call(this, state); this.instructions.push([ol.render.canvas.Instruction.BEGIN_PATH]); }; goog.provide('ol.render.canvas.PolygonReplay'); goog.require('ol'); goog.require('ol.color'); 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 {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. * @struct */ ol.render.canvas.PolygonReplay = function( tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); }; 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) { 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; var fillStyle = state.fillStyle; var strokeStyle = state.strokeStyle; if (fillStyle === undefined && strokeStyle === undefined) { return; } 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, state.lineDashOffset ]); } 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) { 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; 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, state.lineDashOffset ]); } 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; var fillStyle = state.fillStyle; var strokeStyle = state.strokeStyle; if (fillStyle === undefined && strokeStyle === undefined) { return; } 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, state.lineDashOffset ]); } 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() { 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); } } }; /** * @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; if (fillStyle !== undefined) { this.updateFillStyle(state, this.applyFill, geometry); } if (state.strokeStyle !== undefined) { this.updateStrokeStyle(state, this.applyStroke); } }; goog.provide('ol.geom.flat.straightchunk'); /** * @param {number} maxAngle Maximum acceptable angle delta between segments. * @param {Array.<number>} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {number} end End. * @param {number} stride Stride. * @return {Array.<number>} Start and end of the first suitable chunk of the * given `flatCoordinates`. */ ol.geom.flat.straightchunk.lineString = function(maxAngle, flatCoordinates, offset, end, stride) { var chunkStart = offset; var chunkEnd = offset; var chunkM = 0; var m = 0; var start = offset; var acos, i, m12, m23, x1, y1, x12, y12, x23, y23; for (i = offset; i < end; i += stride) { var x2 = flatCoordinates[i]; var y2 = flatCoordinates[i + 1]; if (x1 !== undefined) { x23 = x2 - x1; y23 = y2 - y1; m23 = Math.sqrt(x23 * x23 + y23 * y23); if (x12 !== undefined) { m += m12; acos = Math.acos((x12 * x23 + y12 * y23) / (m12 * m23)); if (acos > maxAngle) { if (m > chunkM) { chunkM = m; chunkStart = start; chunkEnd = i; } m = 0; start = i - stride; } } m12 = m23; x12 = x23; y12 = y23; } x1 = x2; y1 = y2; } m += m23; return m > chunkM ? [start, i] : [chunkStart, chunkEnd]; }; goog.provide('ol.style.TextPlacement'); /** * Text placement. One of `'point'`, `'line'`. Default is `'point'`. Note that * `'line'` requires the underlying geometry to be a {@link ol.geom.LineString}, * {@link ol.geom.Polygon}, {@link ol.geom.MultiLineString} or * {@link ol.geom.MultiPolygon}. * @enum {string} */ ol.style.TextPlacement = { POINT: 'point', LINE: 'line' }; goog.provide('ol.render.canvas.TextReplay'); goog.require('ol'); goog.require('ol.colorlike'); goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.geom.flat.straightchunk'); goog.require('ol.geom.GeometryType'); goog.require('ol.has'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.Instruction'); goog.require('ol.render.canvas.Replay'); goog.require('ol.render.replay'); goog.require('ol.style.TextPlacement'); /** * @constructor * @extends {ol.render.canvas.Replay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. * @struct */ ol.render.canvas.TextReplay = function( tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); /** * @private * @type {ol.DeclutterGroup} */ this.declutterGroup_; /** * @private * @type {Array.<HTMLCanvasElement>} */ this.labels_ = 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 {?ol.CanvasFillState} */ this.textFillState_ = null; /** * @type {Object.<string, ol.CanvasFillState>} */ this.fillStates = {}; /** * @private * @type {?ol.CanvasStrokeState} */ this.textStrokeState_ = null; /** * @type {Object.<string, ol.CanvasStrokeState>} */ this.strokeStates = {}; /** * @private * @type {ol.CanvasTextState} */ this.textState_ = /** @type {ol.CanvasTextState} */ ({}); /** * @type {Object.<string, ol.CanvasTextState>} */ this.textStates = {}; /** * @private * @type {string} */ this.textKey_ = ''; /** * @private * @type {string} */ this.fillKey_ = ''; /** * @private * @type {string} */ this.strokeKey_ = ''; /** * @private * @type {Object.<string, Object.<string, number>>} */ this.widths_ = {}; var labelCache = ol.render.canvas.labelCache; labelCache.prune(); }; ol.inherits(ol.render.canvas.TextReplay, ol.render.canvas.Replay); /** * @param {string} font Font to use for measuring. * @param {Array.<string>} lines Lines to measure. * @param {Array.<number>} widths Array will be populated with the widths of * each line. * @return {number} Width of the whole text. */ ol.render.canvas.TextReplay.measureTextWidths = function(font, lines, widths) { var numLines = lines.length; var width = 0; var currentWidth, i; for (i = 0; i < numLines; ++i) { currentWidth = ol.render.canvas.measureTextWidth(font, lines[i]); width = Math.max(width, currentWidth); widths.push(currentWidth); } return width; }; /** * @inheritDoc */ ol.render.canvas.TextReplay.prototype.drawText = function(geometry, feature) { var fillState = this.textFillState_; var strokeState = this.textStrokeState_; var textState = this.textState_; if (this.text_ === '' || !textState || (!fillState && !strokeState)) { return; } var begin = this.coordinates.length; var geometryType = geometry.getType(); var flatCoordinates = null; var end = 2; var stride = 2; var i, ii; if (textState.placement === ol.style.TextPlacement.LINE) { if (!ol.extent.intersects(this.getBufferedMaxExtent(), geometry.getExtent())) { return; } var ends; flatCoordinates = geometry.getFlatCoordinates(); stride = geometry.getStride(); if (geometryType == ol.geom.GeometryType.LINE_STRING) { ends = [flatCoordinates.length]; } else if (geometryType == ol.geom.GeometryType.MULTI_LINE_STRING) { ends = geometry.getEnds(); } else if (geometryType == ol.geom.GeometryType.POLYGON) { ends = geometry.getEnds().slice(0, 1); } else if (geometryType == ol.geom.GeometryType.MULTI_POLYGON) { var endss = geometry.getEndss(); ends = []; for (i = 0, ii = endss.length; i < ii; ++i) { ends.push(endss[i][0]); } } this.beginGeometry(geometry, feature); var textAlign = textState.textAlign; var flatOffset = 0; var flatEnd; for (var o = 0, oo = ends.length; o < oo; ++o) { if (textAlign == undefined) { var range = ol.geom.flat.straightchunk.lineString( textState.maxAngle, flatCoordinates, flatOffset, ends[o], stride); flatOffset = range[0]; flatEnd = range[1]; } else { flatEnd = ends[o]; } for (i = flatOffset; i < flatEnd; i += stride) { this.coordinates.push(flatCoordinates[i], flatCoordinates[i + 1]); } end = this.coordinates.length; flatOffset = ends[o]; this.drawChars_(begin, end, this.declutterGroup_); begin = end; } this.endGeometry(geometry, feature); } else { var label = this.getImage(this.text_, this.textKey_, this.fillKey_, this.strokeKey_); var width = label.width / this.pixelRatio; switch (geometryType) { case ol.geom.GeometryType.POINT: case ol.geom.GeometryType.MULTI_POINT: flatCoordinates = geometry.getFlatCoordinates(); end = flatCoordinates.length; break; case ol.geom.GeometryType.LINE_STRING: flatCoordinates = /** @type {ol.geom.LineString} */ (geometry).getFlatMidpoint(); break; case ol.geom.GeometryType.CIRCLE: flatCoordinates = /** @type {ol.geom.Circle} */ (geometry).getCenter(); break; case ol.geom.GeometryType.MULTI_LINE_STRING: flatCoordinates = /** @type {ol.geom.MultiLineString} */ (geometry).getFlatMidpoints(); end = flatCoordinates.length; break; case ol.geom.GeometryType.POLYGON: flatCoordinates = /** @type {ol.geom.Polygon} */ (geometry).getFlatInteriorPoint(); if (!textState.overflow && flatCoordinates[2] / this.resolution < width) { return; } stride = 3; break; case ol.geom.GeometryType.MULTI_POLYGON: var interiorPoints = /** @type {ol.geom.MultiPolygon} */ (geometry).getFlatInteriorPoints(); flatCoordinates = []; for (i = 0, ii = interiorPoints.length; i < ii; i += 3) { if (textState.overflow || interiorPoints[i + 2] / this.resolution >= width) { flatCoordinates.push(interiorPoints[i], interiorPoints[i + 1]); } } end = flatCoordinates.length; if (end == 0) { return; } break; default: } end = this.appendFlatCoordinates(flatCoordinates, 0, end, stride, false, false); this.beginGeometry(geometry, feature); if (textState.backgroundFill || textState.backgroundStroke) { this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke); this.updateFillStyle(this.state, this.applyFill, geometry); this.updateStrokeStyle(this.state, this.applyStroke); } this.drawTextImage_(label, begin, end); this.endGeometry(geometry, feature); } }; /** * @param {string} text Text. * @param {string} textKey Text style key. * @param {string} fillKey Fill style key. * @param {string} strokeKey Stroke style key. * @return {HTMLCanvasElement} Image. */ ol.render.canvas.TextReplay.prototype.getImage = function(text, textKey, fillKey, strokeKey) { var label; var key = strokeKey + textKey + text + fillKey + this.pixelRatio; var labelCache = ol.render.canvas.labelCache; if (!labelCache.containsKey(key)) { var strokeState = strokeKey ? this.strokeStates[strokeKey] || this.textStrokeState_ : null; var fillState = fillKey ? this.fillStates[fillKey] || this.textFillState_ : null; var textState = this.textStates[textKey] || this.textState_; var pixelRatio = this.pixelRatio; var scale = textState.scale * pixelRatio; var align = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign]; var strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0; var lines = text.split('\n'); var numLines = lines.length; var widths = []; var width = ol.render.canvas.TextReplay.measureTextWidths(textState.font, lines, widths); var lineHeight = ol.render.canvas.measureTextHeight(textState.font); var height = lineHeight * numLines; var renderWidth = (width + strokeWidth); var context = ol.dom.createCanvasContext2D( Math.ceil(renderWidth * scale), Math.ceil((height + strokeWidth) * scale)); label = context.canvas; labelCache.set(key, label); if (scale != 1) { context.scale(scale, scale); } context.font = textState.font; if (strokeKey) { context.strokeStyle = strokeState.strokeStyle; context.lineWidth = strokeWidth * (ol.has.SAFARI ? scale : 1); context.lineCap = strokeState.lineCap; context.lineJoin = strokeState.lineJoin; context.miterLimit = strokeState.miterLimit; if (ol.has.CANVAS_LINE_DASH && strokeState.lineDash.length) { context.setLineDash(strokeState.lineDash); context.lineDashOffset = strokeState.lineDashOffset; } } if (fillKey) { context.fillStyle = fillState.fillStyle; } context.textBaseline = 'middle'; context.textAlign = 'center'; var leftRight = (0.5 - align); var x = align * label.width / scale + leftRight * strokeWidth; var i; if (strokeKey) { for (i = 0; i < numLines; ++i) { context.strokeText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight); } } if (fillKey) { for (i = 0; i < numLines; ++i) { context.fillText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight); } } } return labelCache.get(key); }; /** * @private * @param {HTMLCanvasElement} label Label. * @param {number} begin Begin. * @param {number} end End. */ ol.render.canvas.TextReplay.prototype.drawTextImage_ = function(label, begin, end) { var textState = this.textState_; var strokeState = this.textStrokeState_; var pixelRatio = this.pixelRatio; var align = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign]; var baseline = ol.render.replay.TEXT_ALIGN[textState.textBaseline]; var strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0; var anchorX = align * label.width / pixelRatio + 2 * (0.5 - align) * strokeWidth; var anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth; this.instructions.push([ol.render.canvas.Instruction.DRAW_IMAGE, begin, end, label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, this.declutterGroup_, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, 1, true, label.width, textState.padding == ol.render.canvas.defaultPadding ? ol.render.canvas.defaultPadding : textState.padding.map(function(p) { return p * pixelRatio; }), !!textState.backgroundFill, !!textState.backgroundStroke ]); this.hitDetectionInstructions.push([ol.render.canvas.Instruction.DRAW_IMAGE, begin, end, label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, this.declutterGroup_, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, 1 / pixelRatio, true, label.width, textState.padding, !!textState.backgroundFill, !!textState.backgroundStroke ]); }; /** * @private * @param {number} begin Begin. * @param {number} end End. * @param {ol.DeclutterGroup} declutterGroup Declutter group. */ ol.render.canvas.TextReplay.prototype.drawChars_ = function(begin, end, declutterGroup) { var strokeState = this.textStrokeState_; var textState = this.textState_; var fillState = this.textFillState_; var strokeKey = this.strokeKey_; if (strokeState) { if (!(strokeKey in this.strokeStates)) { this.strokeStates[strokeKey] = /** @type {ol.CanvasStrokeState} */ ({ strokeStyle: strokeState.strokeStyle, lineCap: strokeState.lineCap, lineDashOffset: strokeState.lineDashOffset, lineWidth: strokeState.lineWidth, lineJoin: strokeState.lineJoin, miterLimit: strokeState.miterLimit, lineDash: strokeState.lineDash }); } } var textKey = this.textKey_; if (!(this.textKey_ in this.textStates)) { this.textStates[this.textKey_] = /** @type {ol.CanvasTextState} */ ({ font: textState.font, textAlign: textState.textAlign || ol.render.canvas.defaultTextAlign, scale: textState.scale }); } var fillKey = this.fillKey_; if (fillState) { if (!(fillKey in this.fillStates)) { this.fillStates[fillKey] = /** @type {ol.CanvasFillState} */ ({ fillStyle: fillState.fillStyle }); } } var pixelRatio = this.pixelRatio; var baseline = ol.render.replay.TEXT_ALIGN[textState.textBaseline]; var offsetY = this.textOffsetY_ * pixelRatio; var text = this.text_; var font = textState.font; var textScale = textState.scale; var strokeWidth = strokeState ? strokeState.lineWidth * textScale / 2 : 0; var widths = this.widths_[font]; if (!widths) { this.widths_[font] = widths = {}; } this.instructions.push([ol.render.canvas.Instruction.DRAW_CHARS, begin, end, baseline, declutterGroup, textState.overflow, fillKey, textState.maxAngle, function(text) { var width = widths[text]; if (!width) { width = widths[text] = ol.render.canvas.measureTextWidth(font, text); } return width * textScale * pixelRatio; }, offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1 ]); this.hitDetectionInstructions.push([ol.render.canvas.Instruction.DRAW_CHARS, begin, end, baseline, declutterGroup, textState.overflow, fillKey, textState.maxAngle, function(text) { var width = widths[text]; if (!width) { width = widths[text] = ol.render.canvas.measureTextWidth(font, text); } return width * textScale; }, offsetY, strokeKey, strokeWidth, text, textKey, 1 / pixelRatio ]); }; /** * @inheritDoc */ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle, declutterGroup) { var textState, fillState, strokeState; if (!textStyle) { this.text_ = ''; } else { this.declutterGroup_ = /** @type {ol.DeclutterGroup} */ (declutterGroup); var textFillStyle = textStyle.getFill(); if (!textFillStyle) { fillState = this.textFillState_ = null; } else { fillState = this.textFillState_; if (!fillState) { fillState = this.textFillState_ = /** @type {ol.CanvasFillState} */ ({}); } fillState.fillStyle = ol.colorlike.asColorLike( textFillStyle.getColor() || ol.render.canvas.defaultFillStyle); } var textStrokeStyle = textStyle.getStroke(); if (!textStrokeStyle) { strokeState = this.textStrokeState_ = null; } else { strokeState = this.textStrokeState_; if (!strokeState) { strokeState = this.textStrokeState_ = /** @type {ol.CanvasStrokeState} */ ({}); } var lineDash = textStrokeStyle.getLineDash(); var lineDashOffset = textStrokeStyle.getLineDashOffset(); var lineWidth = textStrokeStyle.getWidth(); var miterLimit = textStrokeStyle.getMiterLimit(); strokeState.lineCap = textStrokeStyle.getLineCap() || ol.render.canvas.defaultLineCap; strokeState.lineDash = lineDash ? lineDash.slice() : ol.render.canvas.defaultLineDash; strokeState.lineDashOffset = lineDashOffset === undefined ? ol.render.canvas.defaultLineDashOffset : lineDashOffset; strokeState.lineJoin = textStrokeStyle.getLineJoin() || ol.render.canvas.defaultLineJoin; strokeState.lineWidth = lineWidth === undefined ? ol.render.canvas.defaultLineWidth : lineWidth; strokeState.miterLimit = miterLimit === undefined ? ol.render.canvas.defaultMiterLimit : miterLimit; strokeState.strokeStyle = ol.colorlike.asColorLike( textStrokeStyle.getColor() || ol.render.canvas.defaultStrokeStyle); } textState = this.textState_; var font = textStyle.getFont() || ol.render.canvas.defaultFont; ol.render.canvas.checkFont(font); var textScale = textStyle.getScale(); textState.overflow = textStyle.getOverflow(); textState.font = font; textState.maxAngle = textStyle.getMaxAngle(); textState.placement = textStyle.getPlacement(); textState.textAlign = textStyle.getTextAlign(); textState.textBaseline = textStyle.getTextBaseline() || ol.render.canvas.defaultTextBaseline; textState.backgroundFill = textStyle.getBackgroundFill(); textState.backgroundStroke = textStyle.getBackgroundStroke(); textState.padding = textStyle.getPadding() || ol.render.canvas.defaultPadding; textState.scale = textScale === undefined ? 1 : textScale; var textOffsetX = textStyle.getOffsetX(); var textOffsetY = textStyle.getOffsetY(); var textRotateWithView = textStyle.getRotateWithView(); var textRotation = textStyle.getRotation(); this.text_ = textStyle.getText() || ''; this.textOffsetX_ = textOffsetX === undefined ? 0 : textOffsetX; this.textOffsetY_ = textOffsetY === undefined ? 0 : textOffsetY; this.textRotateWithView_ = textRotateWithView === undefined ? false : textRotateWithView; this.textRotation_ = textRotation === undefined ? 0 : textRotation; this.strokeKey_ = strokeState ? (typeof strokeState.strokeStyle == 'string' ? strokeState.strokeStyle : ol.getUid(strokeState.strokeStyle)) + strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth + strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' : ''; this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?'); this.fillKey_ = fillState ? (typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + ol.getUid(fillState.fillStyle))) : ''; } }; 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.ReplayType'); goog.require('ol.render.canvas.Replay'); 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 {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay group can have overlapping geometries. * @param {?} declutterTree Declutter tree * for declutter processing in postrender. * @param {number=} opt_renderBuffer Optional rendering buffer. * @struct */ ol.render.canvas.ReplayGroup = function( tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree, opt_renderBuffer) { ol.render.ReplayGroup.call(this); /** * Declutter tree. * @private */ this.declutterTree_ = declutterTree; /** * @type {ol.DeclutterGroup} * @private */ this.declutterGroup_ = null; /** * @private * @type {number} */ this.tolerance_ = tolerance; /** * @private * @type {ol.Extent} */ this.maxExtent_ = maxExtent; /** * @private * @type {boolean} */ this.overlaps_ = overlaps; /** * @private * @type {number} */ this.pixelRatio_ = pixelRatio; /** * @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; }; /** * @param {!Object.<string, Array.<*>>} declutterReplays Declutter replays. * @param {CanvasRenderingContext2D} context Context. * @param {number} rotation Rotation. */ ol.render.canvas.ReplayGroup.replayDeclutter = function(declutterReplays, context, rotation) { var zs = Object.keys(declutterReplays).map(Number).sort(ol.array.numberSafeCompareFunction); var skippedFeatureUids = {}; for (var z = 0, zz = zs.length; z < zz; ++z) { var replayData = declutterReplays[zs[z].toString()]; for (var i = 0, ii = replayData.length; i < ii;) { var replay = replayData[i++]; var transform = replayData[i++]; replay.replay(context, transform, rotation, skippedFeatureUids); } } }; /** * @param {boolean} group Group with previous replay. * @return {ol.DeclutterGroup} Declutter instruction group. */ ol.render.canvas.ReplayGroup.prototype.addDeclutter = function(group) { var declutter = null; if (this.declutterTree_) { if (group) { declutter = this.declutterGroup_; /** @type {number} */ (declutter[4])++; } else { declutter = this.declutterGroup_ = ol.extent.createEmpty(); declutter.push(1); } } return declutter; }; /** * @param {CanvasRenderingContext2D} context Context. * @param {ol.Transform} transform Transform. */ ol.render.canvas.ReplayGroup.prototype.clip = function(context, transform) { var flatClipCoords = this.getClipCoords(transform); 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(); }; /** * @param {Array.<ol.render.ReplayType>} replays Replays. * @return {boolean} Has replays of the provided types. */ ol.render.canvas.ReplayGroup.prototype.hasReplays = function(replays) { for (var zIndex in this.replaysByZIndex_) { var candidates = this.replaysByZIndex_[zIndex]; for (var i = 0, ii = replays.length; i < ii; ++i) { if (replays[i] in candidates) { return true; } } } return false; }; /** * 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. * @param {Object.<string, ol.DeclutterGroup>} declutterReplays Declutter * replays. * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function( coordinate, resolution, rotation, hitTolerance, skippedFeaturesHash, callback, declutterReplays) { 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); var declutteredFeatures; if (this.declutterTree_) { declutteredFeatures = this.declutterTree_.all().map(function(entry) { return entry.value; }); } /** * @param {ol.Feature|ol.render.Feature} feature Feature. * @return {?} Callback result. */ function hitDetectionCallback(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; if (!declutteredFeatures || declutteredFeatures.indexOf(feature) !== -1) { result = callback(feature); } if (result) { return result; } else { context.clearRect(0, 0, contextSize, contextSize); return undefined; } } } } } } return this.replayHitDetection_(context, transform, rotation, skippedFeaturesHash, hitDetectionCallback, hitExtent, declutterReplays); }; /** * @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]; replay = new Constructor(this.tolerance_, this.maxExtent_, this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_); replays[replayType] = replay; } return replay; }; /** * @return {Object.<string, Object.<ol.render.ReplayType, ol.render.canvas.Replay>>} Replays. */ ol.render.canvas.ReplayGroup.prototype.getReplays = function() { return this.replaysByZIndex_; }; /** * @inheritDoc */ ol.render.canvas.ReplayGroup.prototype.isEmpty = function() { return ol.obj.isEmpty(this.replaysByZIndex_); }; /** * @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 {Array.<ol.render.ReplayType>=} opt_replayTypes Ordered replay types * to replay. Default is {@link ol.render.replay.ORDER} * @param {Object.<string, ol.DeclutterGroup>=} opt_declutterReplays Declutter * replays. */ ol.render.canvas.ReplayGroup.prototype.replay = function(context, transform, viewRotation, skippedFeaturesHash, opt_replayTypes, opt_declutterReplays) { /** @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 context.save(); this.clip(context, transform); 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) { var zIndexKey = zs[i].toString(); replays = this.replaysByZIndex_[zIndexKey]; for (j = 0, jj = replayTypes.length; j < jj; ++j) { var replayType = replayTypes[j]; replay = replays[replayType]; if (replay !== undefined) { if (opt_declutterReplays && (replayType == ol.render.ReplayType.IMAGE || replayType == ol.render.ReplayType.TEXT)) { var declutter = opt_declutterReplays[zIndexKey]; if (!declutter) { opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)]; } else { declutter.push(replay, transform.slice(0)); } } else { replay.replay(context, 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. * @param {Object.<string, ol.DeclutterGroup>=} opt_declutterReplays Declutter * replays. * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( context, transform, viewRotation, skippedFeaturesHash, featureCallback, opt_hitExtent, opt_declutterReplays) { /** @type {Array.<number>} */ var zs = Object.keys(this.replaysByZIndex_).map(Number); zs.sort(ol.array.numberSafeCompareFunction); var i, j, replays, replay, result; for (i = zs.length - 1; i >= 0; --i) { var zIndexKey = zs[i].toString(); replays = this.replaysByZIndex_[zIndexKey]; for (j = ol.render.replay.ORDER.length - 1; j >= 0; --j) { var replayType = ol.render.replay.ORDER[j]; replay = replays[replayType]; if (replay !== undefined) { if (opt_declutterReplays && (replayType == ol.render.ReplayType.IMAGE || replayType == ol.render.ReplayType.TEXT)) { var declutter = opt_declutterReplays[zIndexKey]; if (!declutter) { opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)]; } else { declutter.push(replay, transform.slice(0)); } } else { 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, number, boolean, Array.<ol.DeclutterGroup>)>} */ ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_ = { 'Circle': ol.render.canvas.PolygonReplay, 'Default': ol.render.canvas.Replay, '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.ImageState'); goog.require('ol.geom.GeometryType'); 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, replayGroup.addDeclutter(false)); textReplay.drawText(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.ImageState.LOADED || imageState == ol.ImageState.ERROR) { imageStyle.unlistenImageChange(listener, thisArg); } else { if (imageState == ol.ImageState.IDLE) { imageStyle.load(); } imageState = imageStyle.getImageState(); 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 renderer = style.getRenderer(); if (renderer) { ol.renderer.vector.renderGeometry_(replayGroup, simplifiedGeometry, style, feature); } else { var geometryRenderer = ol.renderer.vector.GEOMETRY_RENDERERS_[simplifiedGeometry.getType()]; geometryRenderer(replayGroup, simplifiedGeometry, style, feature); } }; /** * @param {ol.render.ReplayGroup} replayGroup Replay group. * @param {ol.geom.Geometry} geometry Geometry. * @param {ol.style.Style} style Style. * @param {ol.Feature|ol.render.Feature} feature Feature. * @private */ ol.renderer.vector.renderGeometry_ = function(replayGroup, geometry, style, feature) { if (geometry.getType() == ol.geom.GeometryType.GEOMETRY_COLLECTION) { var geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries(); for (var i = 0, ii = geometries.length; i < ii; ++i) { ol.renderer.vector.renderGeometry_(replayGroup, geometries[i], style, feature); } return; } var replay = replayGroup.getReplay(style.getZIndex(), ol.render.ReplayType.DEFAULT); replay.drawCustom(/** @type {ol.geom.SimpleGeometry} */ (geometry), feature, style.getRenderer()); }; /** * @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, replayGroup.addDeclutter(false)); textReplay.drawText(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, replayGroup.addDeclutter(false)); textReplay.drawText(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, replayGroup.addDeclutter(false)); textReplay.drawText(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.ImageState.LOADED) { return; } var imageReplay = replayGroup.getReplay( style.getZIndex(), ol.render.ReplayType.IMAGE); imageReplay.setImageStyle(imageStyle, replayGroup.addDeclutter(false)); imageReplay.drawPoint(geometry, feature); } var textStyle = style.getText(); if (textStyle) { var textReplay = replayGroup.getReplay( style.getZIndex(), ol.render.ReplayType.TEXT); textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(!!imageStyle)); textReplay.drawText(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.ImageState.LOADED) { return; } var imageReplay = replayGroup.getReplay( style.getZIndex(), ol.render.ReplayType.IMAGE); imageReplay.setImageStyle(imageStyle, replayGroup.addDeclutter(false)); imageReplay.drawMultiPoint(geometry, feature); } var textStyle = style.getText(); if (textStyle) { var textReplay = replayGroup.getReplay( style.getZIndex(), ol.render.ReplayType.TEXT); textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(!!imageStyle)); textReplay.drawText(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, replayGroup.addDeclutter(false)); textReplay.drawText(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.LayerType'); goog.require('ol.ViewHint'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.ext.rbush'); goog.require('ol.extent'); goog.require('ol.render.EventType'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.Type'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.vector'); /** * @constructor * @extends {ol.renderer.canvas.Layer} * @param {ol.layer.Vector} vectorLayer Vector layer. * @api */ ol.renderer.canvas.VectorLayer = function(vectorLayer) { ol.renderer.canvas.Layer.call(this, vectorLayer); /** * Declutter tree. * @private */ this.declutterTree_ = vectorLayer.getDeclutter() ? ol.ext.rbush(9) : null; /** * @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; /** * A new replay group had to be created by `prepareFrame()` * @type {boolean} */ this.replayGroupChanged = true; /** * @type {CanvasRenderingContext2D} */ this.context = ol.dom.createCanvasContext2D(); ol.events.listen(ol.render.canvas.labelCache, ol.events.EventType.CLEAR, this.handleFontsChanged_, this); }; ol.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.canvas.VectorLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.CANVAS && layer.getType() === ol.LayerType.VECTOR; }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.canvas.VectorLayer} The layer renderer. */ ol.renderer.canvas.VectorLayer['create'] = function(mapRenderer, layer) { return new ol.renderer.canvas.VectorLayer(/** @type {ol.layer.Vector} */ (layer)); }; /** * @inheritDoc */ ol.renderer.canvas.VectorLayer.prototype.disposeInternal = function() { ol.events.unlisten(ol.render.canvas.labelCache, ol.events.EventType.CLEAR, this.handleFontsChanged_, this); ol.renderer.canvas.Layer.prototype.disposeInternal.call(this); }; /** * @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()) { if (this.declutterTree_) { this.declutterTree_.clear(); } var layer = /** @type {ol.layer.Vector} */ (this.getLayer()); var drawOffsetX = 0; var drawOffsetY = 0; var replayContext; var transparentLayer = layerState.opacity !== 1; var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER); if (transparentLayer || hasRenderListeners) { 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; } var alpha = replayContext.globalAlpha; if (!transparentLayer) { // 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 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, 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, 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, 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) { if (hasRenderListeners) { this.dispatchRenderEvent(replayContext, frameState, transform); } if (transparentLayer) { var mainContextAlpha = context.globalAlpha; context.globalAlpha = layerState.opacity; context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); context.globalAlpha = mainContextAlpha; } else { context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); } replayContext.translate(-drawOffsetX, -drawOffsetY); } if (!transparentLayer) { 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 = /** @type {ol.layer.Vector} */ (this.getLayer()); /** @type {Object.<string, boolean>} */ var features = {}; var result = 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); } }, null); return result; } }; /** * @param {ol.events.Event} event Event. */ ol.renderer.canvas.VectorLayer.prototype.handleFontsChanged_ = function(event) { var layer = this.getLayer(); if (layer.getVisible() && this.replayGroup_) { layer.changed(); } }; /** * 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.updateLogos(frameState, vectorSource); var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; var interacting = frameState.viewHints[ol.ViewHint.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)) { this.replayGroupChanged = false; return true; } this.replayGroup_ = null; this.dirty_ = false; var replayGroup = new ol.render.canvas.ReplayGroup( ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution, pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, 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; } }.bind(this); 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); for (var i = 0, ii = features.length; i < ii; ++i) { renderFeature(features[i]); } } else { vectorSource.forEachFeatureInExtent(extent, renderFeature, this); } replayGroup.finish(); this.renderedResolution_ = resolution; this.renderedRevision_ = vectorLayerRevision; this.renderedRenderOrder_ = vectorLayerRenderOrder; this.renderedExtent_ = extent; this.replayGroup_ = replayGroup; this.replayGroupChanged = true; 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); } return loading; }; goog.provide('ol.layer.VectorTileRenderType'); /** * @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.VectorTileRenderType = { IMAGE: 'image', HYBRID: 'hybrid', VECTOR: 'vector' }; goog.provide('ol.renderer.canvas.VectorTileLayer'); goog.require('ol'); goog.require('ol.LayerType'); goog.require('ol.TileState'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.ext.rbush'); goog.require('ol.extent'); goog.require('ol.layer.VectorTileRenderType'); goog.require('ol.proj'); goog.require('ol.proj.Units'); 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.Type'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.vector'); goog.require('ol.transform'); /** * @constructor * @extends {ol.renderer.canvas.TileLayer} * @param {ol.layer.VectorTile} layer VectorTile layer. * @api */ ol.renderer.canvas.VectorTileLayer = function(layer) { /** * @type {CanvasRenderingContext2D} */ this.context = null; ol.renderer.canvas.TileLayer.call(this, layer); /** * Declutter tree. * @private */ this.declutterTree_ = layer.getDeclutter() ? ol.ext.rbush(9) : null; /** * @private * @type {boolean} */ this.dirty_ = false; /** * @private * @type {number} */ this.renderedLayerRevision_; /** * @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.VectorTileRenderType.VECTOR ? 1 : 0; ol.events.listen(ol.render.canvas.labelCache, ol.events.EventType.CLEAR, this.handleFontsChanged_, this); }; ol.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.TileLayer); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.canvas.VectorTileLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.CANVAS && layer.getType() === ol.LayerType.VECTOR_TILE; }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.canvas.VectorTileLayer} The layer renderer. */ ol.renderer.canvas.VectorTileLayer['create'] = function(mapRenderer, layer) { return new ol.renderer.canvas.VectorTileLayer(/** @type {ol.layer.VectorTile} */ (layer)); }; /** * @const * @type {!Object.<string, Array.<ol.render.ReplayType>>} */ ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS = { 'image': [ol.render.ReplayType.POLYGON, ol.render.ReplayType.CIRCLE, ol.render.ReplayType.LINE_STRING, ol.render.ReplayType.IMAGE, ol.render.ReplayType.TEXT], 'hybrid': [ol.render.ReplayType.POLYGON, ol.render.ReplayType.LINE_STRING] }; /** * @const * @type {!Object.<string, Array.<ol.render.ReplayType>>} */ ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS = { 'image': [ol.render.ReplayType.DEFAULT], 'hybrid': [ol.render.ReplayType.IMAGE, ol.render.ReplayType.TEXT, ol.render.ReplayType.DEFAULT], 'vector': ol.render.replay.ORDER }; /** * @inheritDoc */ ol.renderer.canvas.VectorTileLayer.prototype.disposeInternal = function() { ol.events.unlisten(ol.render.canvas.labelCache, ol.events.EventType.CLEAR, this.handleFontsChanged_, this); ol.renderer.canvas.TileLayer.prototype.disposeInternal.call(this); }; /** * @inheritDoc */ ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) { var layer = this.getLayer(); var layerRevision = layer.getRevision(); if (this.renderedLayerRevision_ != layerRevision) { this.renderedTiles.length = 0; var renderMode = layer.getRenderMode(); if (!this.context && renderMode != ol.layer.VectorTileRenderType.VECTOR) { this.context = ol.dom.createCanvasContext2D(); } if (this.context && renderMode == ol.layer.VectorTileRenderType.VECTOR) { this.context = null; } } this.renderedLayerRevision_ = layerRevision; return ol.renderer.canvas.TileLayer.prototype.prepareFrame.apply(this, arguments); }; /** * @param {ol.VectorImageTile} 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 = /** @type {ol.RenderOrderFunction} */ (layer.getRenderOrder()) || null; var replayState = tile.getReplayState(layer); if (!replayState.dirty && replayState.renderedRevision == revision && replayState.renderedRenderOrder == renderOrder) { return; } var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var sourceTileGrid = source.getTileGrid(); var tileGrid = source.getTileGridForProjection(projection); var resolution = tileGrid.getResolution(tile.tileCoord[0]); var tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); var zIndexKeys = {}; for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } var sourceTileCoord = sourceTile.tileCoord; var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); var sharedExtent = ol.extent.getIntersection(tileExtent, sourceTileExtent); var bufferedExtent = ol.extent.equals(sourceTileExtent, sharedExtent) ? null : ol.extent.buffer(sharedExtent, layer.getRenderBuffer() * resolution); var tileProjection = sourceTile.getProjection(); var reproject = false; if (!ol.proj.equivalent(projection, tileProjection)) { reproject = true; sourceTile.setProjection(projection); } replayState.dirty = false; var replayGroup = new ol.render.canvas.ReplayGroup(0, sharedExtent, resolution, pixelRatio, source.getOverlaps(), this.declutterTree_, layer.getRenderBuffer()); var squaredTolerance = ol.renderer.vector.getSquaredTolerance( resolution, pixelRatio); /** * @param {ol.Feature|ol.render.Feature} feature Feature. * @this {ol.renderer.canvas.VectorTileLayer} */ var renderFeature = function(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) { var dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup); this.dirty_ = this.dirty_ || dirty; replayState.dirty = replayState.dirty || dirty; } }; var features = sourceTile.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) { if (tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS) { // projected tile extent tileProjection.setWorldExtent(sourceTileExtent); // tile extent in tile pixel space tileProjection.setExtent(sourceTile.getExtent()); } feature.getGeometry().transform(tileProjection, projection); } if (!bufferedExtent || ol.extent.intersects(bufferedExtent, feature.getGeometry().getExtent())) { renderFeature.call(this, feature); } } replayGroup.finish(); for (var r in replayGroup.getReplays()) { zIndexKeys[r] = true; } sourceTile.setReplayGroup(layer, tile.tileCoord.toString(), replayGroup); } replayState.renderedRevision = revision; replayState.renderedRenderOrder = renderOrder; }; /** * @inheritDoc */ ol.renderer.canvas.VectorTileLayer.prototype.drawTileImage = function( tile, frameState, layerState, x, y, w, h, gutter, transition) { var vectorImageTile = /** @type {ol.VectorImageTile} */ (tile); this.createReplayGroup_(vectorImageTile, frameState); if (this.context) { this.renderTileImage_(vectorImageTile, 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.VectorImageTile>} */ var renderedTiles = this.renderedTiles; var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); var bufferedExtent, found; var i, ii, replayGroup; var tile, tileCoord, tileExtent; for (i = 0, ii = renderedTiles.length; i < ii; ++i) { tile = renderedTiles[i]; tileCoord = tile.wrappedTileCoord; tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); bufferedExtent = ol.extent.buffer(tileExtent, hitTolerance * resolution, bufferedExtent); if (!ol.extent.containsCoordinate(bufferedExtent, coordinate)) { continue; } for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); found = found || 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); } }, null); } } return found; }; /** * @param {ol.VectorTile} tile Tile. * @param {olx.FrameState} frameState Frame state. * @return {ol.Transform} transform Transform. * @private */ ol.renderer.canvas.VectorTileLayer.prototype.getReplayTransform_ = function(tile, frameState) { 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]); 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); }; /** * @param {ol.events.Event} event Event. */ ol.renderer.canvas.VectorTileLayer.prototype.handleFontsChanged_ = function(event) { var layer = this.getLayer(); if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) { layer.changed(); } }; /** * 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 layer = this.getLayer(); var declutterReplays = layer.getDeclutter() ? {} : null; var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var renderMode = layer.getRenderMode(); var replayTypes = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode]; var pixelRatio = frameState.pixelRatio; var rotation = frameState.viewState.rotation; var size = frameState.size; var offsetX, offsetY; if (rotation) { offsetX = Math.round(pixelRatio * size[0] / 2); offsetY = Math.round(pixelRatio * size[1] / 2); ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY); } if (declutterReplays) { this.declutterTree_.clear(); } var tiles = this.renderedTiles; var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); var clips = []; var zs = []; for (var i = tiles.length - 1; i >= 0; --i) { var tile = /** @type {ol.VectorImageTile} */ (tiles[i]); if (tile.getState() == ol.TileState.ABORT) { continue; } var tileCoord = tile.tileCoord; var worldOffset = tileGrid.getTileCoordExtent(tileCoord)[0] - tileGrid.getTileCoordExtent(tile.wrappedTileCoord)[0]; var transform = undefined; for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } var replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString()); if (renderMode != ol.layer.VectorTileRenderType.VECTOR && !replayGroup.hasReplays(replayTypes)) { continue; } if (!transform) { transform = this.getTransform(frameState, worldOffset); } var currentZ = sourceTile.tileCoord[0]; var currentClip = replayGroup.getClipCoords(transform); context.save(); context.globalAlpha = layerState.opacity; // Create a clip mask for regions in this low resolution tile that are // already filled by a higher resolution tile 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(); } } replayGroup.replay(context, transform, rotation, {}, replayTypes, declutterReplays); context.restore(); clips.push(currentClip); zs.push(currentZ); } } if (declutterReplays) { ol.render.canvas.ReplayGroup.replayDeclutter(declutterReplays, context, rotation); } if (rotation) { ol.render.canvas.rotateAtOffset(context, rotation, /** @type {number} */ (offsetX), /** @type {number} */ (offsetY)); } 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); } return loading; }; /** * @param {ol.VectorImageTile} 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(layer); 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.wrappedTileCoord; var z = tileCoord[0]; var pixelRatio = frameState.pixelRatio; var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); var resolution = tileGrid.getResolution(z); var context = tile.getContext(layer); var size = source.getTilePixelSize(z, pixelRatio, frameState.viewState.projection); context.canvas.width = size[0]; context.canvas.height = size[1]; var tileExtent = tileGrid.getTileCoordExtent(tileCoord); for (var i = 0, ii = tile.tileKeys.length; i < ii; ++i) { var sourceTile = tile.getTile(tile.tileKeys[i]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } var pixelScale = pixelRatio / resolution; var transform = ol.transform.reset(this.tmpTransform_); ol.transform.scale(transform, pixelScale, -pixelScale); ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]); var replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); replayGroup.replay(context, transform, 0, {}, replays); } } }; goog.provide('ol.CanvasMap'); goog.require('ol'); goog.require('ol.PluggableMap'); goog.require('ol.PluginType'); goog.require('ol.control'); goog.require('ol.interaction'); goog.require('ol.obj'); goog.require('ol.plugins'); goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.Map'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); goog.require('ol.renderer.canvas.VectorTileLayer'); ol.plugins.register(ol.PluginType.MAP_RENDERER, ol.renderer.canvas.Map); ol.plugins.registerMultiple(ol.PluginType.LAYER_RENDERER, [ ol.renderer.canvas.ImageLayer, ol.renderer.canvas.TileLayer, ol.renderer.canvas.VectorLayer, ol.renderer.canvas.VectorTileLayer ]); /** * @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.CanvasMap({ * 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.PluggableMap} * @param {olx.MapOptions} options Map options. * @fires ol.MapBrowserEvent * @fires ol.MapEvent * @fires ol.render.Event#postcompose * @fires ol.render.Event#precompose * @api */ ol.CanvasMap = function(options) { options = ol.obj.assign({}, options); delete options.renderer; if (!options.controls) { options.controls = ol.control.defaults(); } if (!options.interactions) { options.interactions = ol.interaction.defaults(); } ol.PluggableMap.call(this, options); }; ol.inherits(ol.CanvasMap, ol.PluggableMap); 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 * overridden 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 */ 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 */ 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; }; })(); // 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 */ 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(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 */ 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 */ 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 */ 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 */ 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.ProjectionLike} projection The projection to report mouse * position in. * @observable * @api */ ol.control.MousePosition.prototype.setProjection = function(projection) { this.set(ol.control.MousePosition.Property_.PROJECTION, ol.proj.get(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} * @private */ ol.control.MousePosition.Property_ = { PROJECTION: 'projection', COORDINATE_FORMAT: 'coordinateFormat' }; goog.provide('ol.OverlayPositioning'); /** * Overlay position: `'bottom-left'`, `'bottom-center'`, `'bottom-right'`, * `'center-left'`, `'center-center'`, `'center-right'`, `'top-left'`, * `'top-center'`, `'top-right'` * @enum {string} */ ol.OverlayPositioning = { 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' }; goog.provide('ol.Overlay'); goog.require('ol'); goog.require('ol.MapEventType'); goog.require('ol.Object'); goog.require('ol.OverlayPositioning'); goog.require('ol.css'); 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 */ ol.Overlay = function(options) { ol.Object.call(this); /** * @protected * @type {olx.OverlayOptions} */ this.options = options; /** * @protected * @type {number|string|undefined} */ this.id = options.id; /** * @protected * @type {boolean} */ this.insertFirst = options.insertFirst !== undefined ? options.insertFirst : true; /** * @protected * @type {boolean} */ this.stopEvent = options.stopEvent !== undefined ? options.stopEvent : true; /** * @protected * @type {Element} */ this.element = document.createElement('DIV'); this.element.className = options.className !== undefined ? options.className : 'ol-overlay-container ' + ol.css.CLASS_SELECTABLE; this.element.style.position = 'absolute'; /** * @protected * @type {boolean} */ this.autoPan = options.autoPan !== undefined ? options.autoPan : false; /** * @protected * @type {olx.OverlayPanOptions} */ this.autoPanAnimation = options.autoPanAnimation || /** @type {olx.OverlayPanOptions} */ ({}); /** * @protected * @type {number} */ this.autoPanMargin = options.autoPanMargin !== undefined ? options.autoPanMargin : 20; /** * @protected * @type {{bottom_: string, * left_: string, * right_: string, * top_: string, * visible: boolean}} */ this.rendered = { bottom_: '', left_: '', right_: '', top_: '', visible: true }; /** * @protected * @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.OverlayPositioning} */ (options.positioning) : ol.OverlayPositioning.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 */ 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.PluggableMap|undefined} The map that the overlay is part of. * @observable * @api */ ol.Overlay.prototype.getMap = function() { return /** @type {ol.PluggableMap|undefined} */ ( this.get(ol.Overlay.Property.MAP)); }; /** * Get the offset of this overlay. * @return {Array.<number>} The offset. * @observable * @api */ 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 */ 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.OverlayPositioning} How the overlay is positioned * relative to its point on the map. * @observable * @api */ ol.Overlay.prototype.getPositioning = function() { return /** @type {ol.OverlayPositioning} */ ( 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.MapEventType.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) && 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 */ ol.Overlay.prototype.setElement = function(element) { this.set(ol.Overlay.Property.ELEMENT, element); }; /** * Set the map to be associated with this overlay. * @param {ol.PluggableMap|undefined} map The map that the overlay is part of. * @observable * @api */ 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 */ 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 */ 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). * @protected */ ol.Overlay.prototype.panIntoView = function() { var map = this.getMap(); if (!map || !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. * @protected */ 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.OverlayPositioning} positioning how the overlay is * positioned relative to its point on the map. * @observable * @api */ 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 || !map.isRendered() || !position) { 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(); this.setVisible(true); var offsetX = offset[0]; var offsetY = offset[1]; if (positioning == ol.OverlayPositioning.BOTTOM_RIGHT || positioning == ol.OverlayPositioning.CENTER_RIGHT || positioning == ol.OverlayPositioning.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.OverlayPositioning.BOTTOM_CENTER || positioning == ol.OverlayPositioning.CENTER_CENTER || positioning == ol.OverlayPositioning.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.OverlayPositioning.BOTTOM_LEFT || positioning == ol.OverlayPositioning.BOTTOM_CENTER || positioning == ol.OverlayPositioning.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.OverlayPositioning.CENTER_LEFT || positioning == ol.OverlayPositioning.CENTER_CENTER || positioning == ol.OverlayPositioning.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; } } }; /** * returns the options this Overlay has been created with * @public * @return {olx.OverlayOptions} overlay options */ ol.Overlay.prototype.getOptions = function() { return this.options; }; /** * @enum {string} * @protected */ 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.PluggableMap'); goog.require('ol.MapEventType'); goog.require('ol.MapProperty'); goog.require('ol.Object'); goog.require('ol.ObjectEventType'); goog.require('ol.Overlay'); goog.require('ol.OverlayPositioning'); goog.require('ol.ViewProperty'); 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); /** * @type {Element} * @private */ this.ovmapDiv_ = document.createElement('DIV'); this.ovmapDiv_.className = 'ol-overviewmap-map'; /** * @type {ol.PluggableMap} * @private */ this.ovmap_ = new ol.PluggableMap({ controls: new ol.Collection(), interactions: new ol.Collection(), 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.OverlayPositioning.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(this.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 }); /* Interactive map */ var scope = this; var overlay = this.boxOverlay_; var overlayBox = this.boxOverlay_.getElement(); /* Functions definition */ var computeDesiredMousePosition = function(mousePosition) { return { clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2), clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2) }; }; var move = function(event) { var coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event)); overlay.setPosition(coordinates); }; var endMoving = function(event) { var coordinates = ovmap.getEventCoordinate(event); scope.getMap().getView().setCenter(coordinates); window.removeEventListener('mousemove', move); window.removeEventListener('mouseup', endMoving); }; /* Binding */ overlayBox.addEventListener('mousedown', function() { window.addEventListener('mousemove', move); window.addEventListener('mouseup', endMoving); }); }; 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); } this.ovmap_.setTarget(null); } ol.control.Control.prototype.setMap.call(this, map); if (map) { this.ovmap_.setTarget(this.ovmapDiv_); this.listenerKeys.push(ol.events.listen( map, ol.ObjectEventType.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.MapProperty.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.ViewProperty.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.ViewProperty.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 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); }; /** * 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.MapEventType.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 */ 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 */ 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 */ 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 */ ol.control.OverviewMap.prototype.getCollapsed = function() { return this.collapsed_; }; /** * Return the overview map. * @return {ol.PluggableMap} Overview map. * @api */ ol.control.OverviewMap.prototype.getOverviewMap = function() { return this.ovmap_; }; goog.provide('ol.control.ScaleLineUnits'); /** * Units for the scale line. Supported values are `'degrees'`, `'imperial'`, * `'nautical'`, `'metric'`, `'us'`. * @enum {string} */ ol.control.ScaleLineUnits = { DEGREES: 'degrees', IMPERIAL: 'imperial', NAUTICAL: 'nautical', METRIC: 'metric', US: 'us' }; goog.provide('ol.control.ScaleLine'); goog.require('ol'); goog.require('ol.Object'); goog.require('ol.asserts'); goog.require('ol.control.Control'); goog.require('ol.control.ScaleLineUnits'); 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 */ 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.ScaleLineUnits} */ (options.units) || ol.control.ScaleLineUnits.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.ScaleLineUnits|undefined} The units to use in the scale * line. * @observable * @api */ ol.control.ScaleLine.prototype.getUnits = function() { return /** @type {ol.control.ScaleLineUnits|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.ScaleLineUnits} units The units to use in the scale line. * @observable * @api */ 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 units = this.getUnits(); var pointResolutionUnits = units == ol.control.ScaleLineUnits.DEGREES ? ol.proj.Units.DEGREES : ol.proj.Units.METERS; var pointResolution = ol.proj.getPointResolution(projection, viewState.resolution, center, pointResolutionUnits); if (units != ol.control.ScaleLineUnits.DEGREES) { pointResolution *= projection.getMetersPerUnit(); } var nominalCount = this.minWidth_ * pointResolution; var suffix = ''; if (units == ol.control.ScaleLineUnits.DEGREES) { var metersPerDegree = ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES]; if (projection.getUnits() == ol.proj.Units.DEGREES) { nominalCount *= metersPerDegree; } else { 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.ScaleLineUnits.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.ScaleLineUnits.NAUTICAL) { pointResolution /= 1852; suffix = 'nm'; } else if (units == ol.control.ScaleLineUnits.METRIC) { if (nominalCount < 0.001) { suffix = 'μm'; pointResolution *= 1000000; } else if (nominalCount < 1) { suffix = 'mm'; pointResolution *= 1000; } else if (nominalCount < 1000) { suffix = 'm'; } else { suffix = 'km'; pointResolution /= 1000; } } else if (units == ol.control.ScaleLineUnits.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} * @private */ ol.control.ScaleLine.Property_ = { UNITS: 'units' }; // FIXME should possibly show tooltip when dragging? goog.provide('ol.control.ZoomSlider'); goog.require('ol'); goog.require('ol.ViewHint'); 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 */ 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 {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} * @private */ 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.ViewHint.INTERACTING, 1); this.previousX_ = event.clientX; this.previousY_ = event.clientY; this.dragging_ = true; } }; /** * 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.ViewHint.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; } }; /** * 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 */ ol.control.ZoomToExtent = function(opt_options) { var options = opt_options ? opt_options : {}; /** * @type {ol.Extent} * @protected */ 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(); }; /** * @protected */ ol.control.ZoomToExtent.prototype.handleZoomToExtent = function() { var map = this.getMap(); var view = map.getView(); var extent = !this.extent ? view.getProjection().getExtent() : this.extent; view.fit(extent); }; 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/} * * @deprecated This class is deprecated and will removed in the next major release. * * @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} * @private */ ol.DeviceOrientation.Property_ = { ALPHA: 'alpha', BETA: 'beta', GAMMA: 'gamma', HEADING: 'heading', TRACKING: 'tracking' }; 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 * @abstract * @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.ImageState} 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.ImageState'); 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) { /** * @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|undefined} */ this.radius2_ = options.radius2; /** * @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.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.ImageState.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|undefined} 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 = function(listener, thisArg) {}; /** * @inheritDoc */ ol.style.RegularShape.prototype.load = function() {}; /** * @inheritDoc */ ol.style.RegularShape.prototype.unlistenImageChange = function(listener, thisArg) {}; /** * @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 lineDashOffset = 0; var strokeStyle; var strokeWidth = 0; if (this.stroke_) { strokeStyle = this.stroke_.getColor(); if (strokeStyle === null) { strokeStyle = ol.render.canvas.defaultStrokeStyle; } strokeStyle = ol.colorlike.asColorLike(strokeStyle); strokeWidth = this.stroke_.getWidth(); if (strokeWidth === undefined) { strokeWidth = ol.render.canvas.defaultLineWidth; } lineDash = this.stroke_.getLineDash(); lineDashOffset = this.stroke_.getLineDashOffset(); if (!ol.has.CANVAS_LINE_DASH) { lineDash = null; lineDashOffset = 0; } 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, lineDashOffset: lineDashOffset, 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); 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(); var points = this.points_; if (points === Infinity) { context.arc( renderOptions.size / 2, renderOptions.size / 2, this.radius_, 0, 2 * Math.PI, true); } else { var radius2 = (this.radius2_ !== undefined) ? this.radius2_ : this.radius_; if (radius2 !== this.radius_) { points = 2 * points; } for (i = 0; i <= points; i++) { angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; radiusC = i % 2 === 0 ? this.radius_ : radius2; context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), renderOptions.size / 2 + radiusC * Math.sin(angle0)); } } if (this.fill_) { var color = this.fill_.getColor(); if (color === null) { color = ol.render.canvas.defaultFillStyle; } context.fillStyle = ol.colorlike.asColorLike(color); context.fill(); } if (this.stroke_) { context.strokeStyle = renderOptions.strokeStyle; context.lineWidth = renderOptions.strokeWidth; if (renderOptions.lineDash) { context.setLineDash(renderOptions.lineDash); context.lineDashOffset = renderOptions.lineDashOffset; } 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(); var points = this.points_; if (points === Infinity) { context.arc( renderOptions.size / 2, renderOptions.size / 2, this.radius_, 0, 2 * Math.PI, true); } else { var radius2 = (this.radius2_ !== undefined) ? this.radius2_ : this.radius_; if (radius2 !== this.radius_) { points = 2 * points; } var i, radiusC, angle0; for (i = 0; i <= points; i++) { angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; radiusC = i % 2 === 0 ? this.radius_ : 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.lineDashOffset = renderOptions.lineDashOffset; } 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. * @override * @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 {number|undefined} */ this.lineDashOffset_ = options.lineDashOffset; /** * @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, lineDashOffset: this.getLineDashOffset(), 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 dash offset for the stroke. * @return {number|undefined} Line dash offset. * @api */ ol.style.Stroke.prototype.getLineDashOffset = function() { return this.lineDashOffset_; }; /** * 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 dash offset. * * @param {number|undefined} lineDashOffset Line dash offset. * @api */ ol.style.Stroke.prototype.setLineDashOffset = function(lineDashOffset) { this.lineDashOffset_ = lineDashOffset; 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.lineDashOffset_ !== undefined ? this.lineDashOffset_ : '-') + ',' + (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.StyleRenderFunction|null} */ this.renderer_ = options.renderer !== undefined ? options.renderer : 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 custom renderer function that was configured with * {@link #setRenderer} or the `renderer` constructor option. * @return {ol.StyleRenderFunction|null} Custom renderer function. * @api */ ol.style.Style.prototype.getRenderer = function() { return this.renderer_; }; /** * Sets a custom renderer function for this style. When set, `fill`, `stroke` * and `image` options of the style will be ignored. * @param {ol.StyleRenderFunction|null} renderer Custom renderer function. * @api */ ol.style.Style.prototype.setRenderer = function(renderer) { this.renderer_ = renderer; }; /** * 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.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 */ 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 */ 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 * @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 */ 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 */ 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|ol.StyleFunction} The feature style. * @api */ 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 */ 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 * @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|ol.StyleFunction} style Style for this feature. * @api * @fires ol.events.Event#event:change */ 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 * @fires ol.events.Event#event:change */ 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 */ 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') { if (obj.length == 2) { styleFunction = function(resolution) { return /** @type {ol.StyleFunction} */ (obj)(this, resolution); }; } else { 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.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) { var array = /** @type {Array.<*>} */ (objectStack[objectStack.length - 1]); 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]; 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) { 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) { 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; 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; var nodeName = fixedNodeName; if (nodeName === undefined) { nodeName = opt_nodeName; } var namespaceURI = opt_namespaceURI; if (opt_namespaceURI === undefined) { namespaceURI = node.namespaceURI; } 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, ol.Extent)|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), format.getLastExtent()); } else { failure.call(this); } } else { failure.call(this); } }.bind(this); /** * @private */ xhr.onerror = function() { 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 * @abstract * @api */ ol.format.Feature = function() { /** * @protected * @type {ol.proj.Projection} */ this.defaultDataProjection = null; /** * @protected * @type {ol.proj.Projection} */ this.defaultFeatureProjection = null; }; /** * 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); }; /** * Get the extent from the source of the last {@link readFeatures} call. * @return {ol.Extent} Tile extent. */ ol.format.Feature.prototype.getLastExtent = function() { return null; }; /** * @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( geometry, dataProjection, featureProjection); } } else { transformed = geometry; } if (write && opt_options && opt_options.decimals !== undefined) { 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 (transformed === geometry) { transformed = transformed.clone(); } 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 * @abstract * @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.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) { var pointX = NaN; var pointY = NaN; var n = (end - offset) / stride; 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 if (n !== 0) { 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]; 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); 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; } return null; }; 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 */ 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 */ ol.geom.LineString.prototype.appendCoordinate = function(coordinate) { 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. * @override * @api */ 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 */ 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. * @override * @api */ 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 */ 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 */ ol.geom.LineString.prototype.getType = function() { return ol.geom.GeometryType.LINE_STRING; }; /** * @inheritDoc * @api */ 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. * @override * @api */ 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 */ 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 */ ol.geom.MultiLineString.prototype.appendLineString = function(lineString) { 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. * @override * @api */ 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 */ 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. * @override * @api */ 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 */ ol.geom.MultiLineString.prototype.getLineString = function(index) { 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 */ 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 */ ol.geom.MultiLineString.prototype.getType = function() { return ol.geom.GeometryType.MULTI_LINE_STRING; }; /** * @inheritDoc * @api */ 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. * @override * @api */ 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) { 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(); } 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 */ 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 */ ol.geom.MultiPoint.prototype.appendPoint = function(point) { 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. * @override * @api */ 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. * @override * @api */ 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 */ ol.geom.MultiPoint.prototype.getPoint = function(index) { var n = !this.flatCoordinates ? 0 : this.flatCoordinates.length / this.stride; 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 */ 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 */ ol.geom.MultiPoint.prototype.getType = function() { return ol.geom.GeometryType.MULTI_POINT; }; /** * @inheritDoc * @api */ 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. * @override * @api */ 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 */ 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 */ ol.geom.MultiPolygon.prototype.appendPolygon = function(polygon) { /** @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. * @override * @api */ 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 */ 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. * @override * @api */ 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 as XYM coordinates, where M is * the length of the horizontal intersection that the point belongs to. * @api */ ol.geom.MultiPolygon.prototype.getInteriorPoints = function() { var interiorPoints = new ol.geom.MultiPoint(null); interiorPoints.setFlatCoordinates(ol.geom.GeometryLayout.XYM, 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 */ ol.geom.MultiPolygon.prototype.getPolygon = function(index) { 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 */ 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 */ ol.geom.MultiPolygon.prototype.getType = function() { return ol.geom.GeometryType.MULTI_POLYGON; }; /** * @inheritDoc * @api */ 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. * @override * @api */ 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) { 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(); } 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.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.deflate'); 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>>>} Transformed rings. */ ol.format.EsriJSON.convertRings_ = function(rings, layout) { var flatRing = []; var outerRings = []; var holes = []; var i, ii; for (i = 0, ii = rings.length; i < ii; ++i) { flatRing.length = 0; ol.geom.flat.deflate.coordinates(flatRing, 0, rings[i], layout.length); // 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]; var containsHole = ol.extent.containsExtent( new ol.geom.LinearRing(outerRing).getExtent(), new ol.geom.LinearRing(hole).getExtent() ); if (containsHole) { // 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) { 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) { 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) { 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) { 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) { 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); 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]) { 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. * @override * @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. * @override * @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); if (opt_options && opt_options.featureProjection) { object['geometry']['spatialReference'] = /** @type {EsriJSONCRS} */({ wkid: ol.proj.get( opt_options.featureProjection).getCode().split(':').pop() }); } } var properties = feature.getProperties(); delete properties[feature.getGeometryName()]; if (!ol.obj.isEmpty(properties)) { object['attributes'] = properties; } else { object['attributes'] = {}; } 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. * @override * @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'); /** * @classdesc * Abstract class; normally only used for creating subclasses and not instantiated in apps. * Base class for WFS GetFeature filters. * * deprecated: This class will no longer be exported starting from the next major version. * * @constructor * @abstract * @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.LogicalNary'); goog.require('ol'); goog.require('ol.asserts'); 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 n-ary logical filters. * * @constructor * @abstract * @param {!string} tagName The XML tag name for this filter. * @param {...ol.format.filter.Filter} conditions Conditions. * @extends {ol.format.filter.Filter} */ ol.format.filter.LogicalNary = function(tagName, conditions) { ol.format.filter.Filter.call(this, tagName); /** * @public * @type {Array.<ol.format.filter.Filter>} */ this.conditions = Array.prototype.slice.call(arguments, 1); ol.asserts.assert(this.conditions.length >= 2, 57); // At least 2 conditions are required. }; ol.inherits(ol.format.filter.LogicalNary, ol.format.filter.Filter); goog.provide('ol.format.filter.And'); goog.require('ol'); goog.require('ol.format.filter.LogicalNary'); /** * @classdesc * Represents a logical `<And>` operator between two or more filter conditions. * * deprecated: This class will no longer be exported starting from the next major version. * * @constructor * @abstract * @param {...ol.format.filter.Filter} conditions Conditions. * @extends {ol.format.filter.LogicalNary} * @api */ ol.format.filter.And = function(conditions) { var params = ['And'].concat(Array.prototype.slice.call(arguments)); ol.format.filter.LogicalNary.apply(this, params); }; ol.inherits(ol.format.filter.And, ol.format.filter.LogicalNary); 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.Spatial'); goog.require('ol'); goog.require('ol.format.filter.Filter'); /** * @classdesc * Abstract class; normally only used for creating subclasses and not instantiated in apps. * Represents a spatial operator to test whether a geometry-valued property * relates to a given geometry. * * deprecated: This class will no longer be exported starting from the next major version. * * @constructor * @abstract * @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.Contains'); goog.require('ol'); goog.require('ol.format.filter.Spatial'); /** * @classdesc * Represents a `<Contains>` operator to test whether a geometry-valued property * contains 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.Contains = function(geometryName, geometry, opt_srsName) { ol.format.filter.Spatial.call(this, 'Contains', geometryName, geometry, opt_srsName); }; ol.inherits(ol.format.filter.Contains, ol.format.filter.Spatial); 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. * * deprecated: This class will no longer be exported starting from the next major version. * * @constructor * @abstract * @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.During'); goog.require('ol'); goog.require('ol.format.filter.Comparison'); /** * @classdesc * Represents a `<During>` comparison operator. * * @constructor * @param {!string} propertyName Name of the context property to compare. * @param {!string} begin The begin date in ISO-8601 format. * @param {!string} end The end date in ISO-8601 format. * @extends {ol.format.filter.Comparison} * @api */ ol.format.filter.During = function(propertyName, begin, end) { ol.format.filter.Comparison.call(this, 'During', propertyName); /** * @public * @type {!string} */ this.begin = begin; /** * @public * @type {!string} */ this.end = end; }; ol.inherits(ol.format.filter.During, ol.format.filter.Comparison); 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. * * deprecated: This class will no longer be exported starting from the next major version. * * @constructor * @abstract * @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.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.Filter'); /** * @classdesc * Represents a logical `<Not>` operator for a filter condition. * * @constructor * @param {!ol.format.filter.Filter} condition Filter condition. * @extends {ol.format.filter.Filter} * @api */ ol.format.filter.Not = function(condition) { ol.format.filter.Filter.call(this, 'Not'); /** * @public * @type {!ol.format.filter.Filter} */ this.condition = condition; }; ol.inherits(ol.format.filter.Not, ol.format.filter.Filter); 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.LogicalNary'); /** * @classdesc * Represents a logical `<Or>` operator between two ore more filter conditions. * * @constructor * @param {...ol.format.filter.Filter} conditions Conditions. * @extends {ol.format.filter.LogicalNary} * @api */ ol.format.filter.Or = function(conditions) { var params = ['Or'].concat(Array.prototype.slice.call(arguments)); ol.format.filter.LogicalNary.apply(this, params); }; ol.inherits(ol.format.filter.Or, ol.format.filter.LogicalNary); 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.format.filter.And'); goog.require('ol.format.filter.Bbox'); goog.require('ol.format.filter.Contains'); goog.require('ol.format.filter.During'); 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 or more filter conditions. * * @param {...ol.format.filter.Filter} conditions Filter conditions. * @returns {!ol.format.filter.And} `<And>` operator. * @api */ ol.format.filter.and = function(conditions) { var params = [null].concat(Array.prototype.slice.call(arguments)); return new (Function.prototype.bind.apply(ol.format.filter.And, params)); }; /** * Create a logical `<Or>` operator between two or more filter conditions. * * @param {...ol.format.filter.Filter} conditions Filter conditions. * @returns {!ol.format.filter.Or} `<Or>` operator. * @api */ ol.format.filter.or = function(conditions) { var params = [null].concat(Array.prototype.slice.call(arguments)); return new (Function.prototype.bind.apply(ol.format.filter.Or, params)); }; /** * 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 `<Contains>` operator to test whether a geometry-valued property * contains 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.Contains} `<Contains>` operator. * @api */ ol.format.filter.contains = function(geometryName, geometry, opt_srsName) { return new ol.format.filter.Contains(geometryName, geometry, 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); }; /** * Create a `<During>` temporal operator. * * @param {!string} propertyName Name of the context property to compare. * @param {!string} begin The begin date in ISO-8601 format. * @param {!string} end The end date in ISO-8601 format. * @returns {!ol.format.filter.During} `<During>` operator. * @api */ ol.format.filter.during = function(propertyName, begin, end) { return new ol.format.filter.During(propertyName, begin, end); }; 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 */ 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. * @override * @api */ 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 */ 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 */ ol.geom.GeometryCollection.prototype.getType = function() { return ol.geom.GeometryType.GEOMETRY_COLLECTION; }; /** * @inheritDoc * @api */ 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 */ 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 */ 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. * @override * @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/openlayers/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 */ 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; /** * Look for the geometry name in the feature GeoJSON * @type {boolean|undefined} * @private */ this.extractGeometryName_ = options.extractGeometryName; }; ol.inherits(ol.format.GeoJSON, ol.format.JSONFeature); /** * @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) { 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) { return new ol.geom.Point(object.coordinates); }; /** * @param {GeoJSONGeometry} object Object. * @private * @return {ol.geom.LineString} LineString. */ ol.format.GeoJSON.readLineStringGeometry_ = function(object) { return new ol.geom.LineString(object.coordinates); }; /** * @param {GeoJSONGeometry} object Object. * @private * @return {ol.geom.MultiLineString} MultiLineString. */ ol.format.GeoJSON.readMultiLineStringGeometry_ = function(object) { return new ol.geom.MultiLineString(object.coordinates); }; /** * @param {GeoJSONGeometry} object Object. * @private * @return {ol.geom.MultiPoint} MultiPoint. */ ol.format.GeoJSON.readMultiPointGeometry_ = function(object) { return new ol.geom.MultiPoint(object.coordinates); }; /** * @param {GeoJSONGeometry} object Object. * @private * @return {ol.geom.MultiPolygon} MultiPolygon. */ ol.format.GeoJSON.readMultiPolygonGeometry_ = function(object) { return new ol.geom.MultiPolygon(object.coordinates); }; /** * @param {GeoJSONGeometry} object Object. * @private * @return {ol.geom.Polygon} Polygon. */ ol.format.GeoJSON.readPolygonGeometry_ = function(object) { 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_ }; /** * 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. If feature at source has an id, it will be used * as Feature id by calling {@link ol.Feature#setId} internally. * * @function * @param {Document|Node|Object|string} source Source. * @param {olx.format.ReadOptions=} opt_options Read options. * @return {ol.Feature} Feature. * @api */ 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 */ ol.format.GeoJSON.prototype.readFeatures; /** * @inheritDoc */ ol.format.GeoJSON.prototype.readFeatureFromObject = function( object, opt_options) { /** * @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_); } else if (this.extractGeometryName_ && geoJSONFeature.geometry_name !== undefined) { feature.setGeometryName(geoJSONFeature.geometry_name); } 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 */ 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 */ 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 { 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. * @override * @api */ 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. * @override * @api */ 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 */ 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. * @override * @api */ 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 */ 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. * @override * @api */ 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 * @abstract * @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; } }; /** * @param {Node} node Node. * @param {olx.format.ReadOptions=} opt_options Options. * @return {ol.Feature} Feature. */ ol.format.XMLFeature.prototype.readFeatureFromNode = function(node, opt_options) { return null; // not implemented }; /** * @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; } }; /** * @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) { return null; // not implemented }; /** * @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) { return null; // not implemented }; /** * @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); return this.xmlSerializer_.serializeToString(node); }; /** * @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) { return null; // not implemented }; /** * @inheritDoc */ ol.format.XMLFeature.prototype.writeFeatures = function(features, opt_options) { var node = this.writeFeaturesNode(features, opt_options); return this.xmlSerializer_.serializeToString(node); }; /** * @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) { return null; // not implemented }; /** * @inheritDoc */ ol.format.XMLFeature.prototype.writeGeometry = function(geometry, opt_options) { var node = this.writeGeometryNode(geometry, opt_options); return this.xmlSerializer_.serializeToString(node); }; /** * @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) { return null; // not implemented }; // 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 * @abstract * @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) { 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'); context['srsDimension'] = node.firstElementChild.getAttribute('srsDimension'); /** @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) { var flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); if (flatCoordinates) { var point = new ol.geom.Point(null); 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) { /** @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) { /** @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) { /** @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.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.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.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) { 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) { 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) { 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) { /** @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) { 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 */ 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.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) { 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_; /** * @private * @type {boolean} */ this.hasZ = options.hasZ !== undefined ? options.hasZ : false; }; 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) { /** @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) { /** @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.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.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) { 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) { 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) { 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) { 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) { /** @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]); flatLinearRings.push(flatLinearRing); } }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.GML3.prototype.exteriorParser_ = function(node, objectStack) { /** @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]); 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) { /** @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) { /** @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) { /** @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 contextDimension = context['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 (node.parentNode.getAttribute('srsDimension')) { dim = ol.format.XSD.readNonNegativeIntegerString( node.parentNode.getAttribute('srsDimension')); } else if (contextDimension) { dim = ol.format.XSD.readNonNegativeIntegerString(contextDimension); } 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 hasZ = context['hasZ']; var srsDimension = hasZ ? 3 : 2; node.setAttribute('srsDimension', srsDimension); 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]); } if (hasZ) { // For newly created points, Z can be undefined. var z = point[2] || 0; coords += ' ' + z; } ol.format.XSD.writeStringTextNode(node, coords); }; /** * @param {Array.<number>} point Point geometry. * @param {string=} opt_srsName Optional srsName * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. * @return {string} The coords string. * @private */ ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName, opt_hasZ) { var axisOrientation = 'enu'; if (opt_srsName) { axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); } var coords = ((axisOrientation.substr(0, 2) === 'en') ? point[0] + ' ' + point[1] : point[1] + ' ' + point[0]); if (opt_hasZ) { // For newly created points, Z can be undefined. var z = point[2] || 0; coords += ' ' + z; } return coords; }; /** * @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 hasZ = context['hasZ']; var srsDimension = hasZ ? 3 : 2; node.setAttribute('srsDimension', srsDimension); 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, hasZ); } 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) { 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 hasZ = context['hasZ']; 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, hasZ: hasZ, 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 hasZ = context['hasZ']; var srsName = context['srsName']; var surface = context['surface']; if (srsName) { node.setAttribute('srsName', srsName); } var polygons = geometry.getPolygons(); ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, 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']; var hasZ = context['hasZ']; if (srsName) { node.setAttribute('srsName', srsName); } var points = geometry.getPoints(); ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, 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 hasZ = context['hasZ']; var srsName = context['srsName']; var curve = context['curve']; if (srsName) { node.setAttribute('srsName', srsName); } var lines = geometry.getLineStrings(); ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, 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; 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 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. * @override * @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, hasZ: this.hasZ, 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 */ 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. * @override * @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, hasZ: this.hasZ, 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 */ 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 */ 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.Feature'); goog.require('ol.format.GMLBase'); 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 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 axisOrientation = 'enu'; if (containerSrs) { var proj = ol.proj.get(containerSrs); if (proj) { axisOrientation = proj.getAxisOrientation(); } } var coordsGroups = s.trim().split(/\s+/); var x, y, z; var flatCoordinates = []; for (var i = 0, ii = coordsGroups.length; i < ii; i++) { var coords = coordsGroups[i].split(/,+/); x = parseFloat(coords[0]); y = parseFloat(coords[1]); z = (coords.length === 3) ? parseFloat(coords[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) { /** @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) { /** @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]); flatLinearRings.push(flatLinearRing); } }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.GML2.prototype.outerBoundaryIsParser_ = function(node, objectStack) { /** @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]); 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_) } }; /** * @const * @param {*} value Value. * @param {Array.<*>} objectStack Object stack. * @param {string=} opt_nodeName Node name. * @return {Node|undefined} Node. * @private */ ol.format.GML2.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { var context = objectStack[objectStack.length - 1]; var multiSurface = context['multiSurface']; var surface = context['surface']; var multiCurve = context['multiCurve']; 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 === 'MultiLineString' && multiCurve === true) { nodeName = 'MultiCurve'; } } else { nodeName = 'Envelope'; } return ol.xml.createElementNS('http://www.opengis.net/gml', nodeName); }; /** * @param {Node} node Node. * @param {ol.Feature} feature Feature. * @param {Array.<*>} objectStack Node stack. */ ol.format.GML2.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 {ol.geom.Geometry|ol.Extent} geometry Geometry. * @param {Array.<*>} objectStack Node stack. */ ol.format.GML2.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.GML2.GEOMETRY_SERIALIZERS_, this.GEOMETRY_NODE_FACTORY_, [value], objectStack, undefined, this); }; /** * @param {Node} node Node. * @param {ol.geom.LineString} geometry LineString geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.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 coordinates = this.createCoordinatesNode_(node.namespaceURI); node.appendChild(coordinates); this.writeCoordinates_(coordinates, geometry, objectStack); } else if (node.nodeName === 'Curve') { var segments = ol.xml.createElementNS(node.namespaceURI, 'segments'); node.appendChild(segments); this.writeCurveSegments_(segments, geometry, objectStack); } }; /** * @param {string} namespaceURI XML namespace. * @returns {Node} coordinates node. * @private */ ol.format.GML2.prototype.createCoordinatesNode_ = function(namespaceURI) { var coordinates = ol.xml.createElementNS(namespaceURI, 'coordinates'); coordinates.setAttribute('decimal', '.'); coordinates.setAttribute('cs', ','); coordinates.setAttribute('ts', ' '); return coordinates; }; /** * @param {Node} node Node. * @param {ol.geom.LineString|ol.geom.LinearRing} value Geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeCoordinates_ = function(node, value, objectStack) { var context = objectStack[objectStack.length - 1]; var hasZ = context['hasZ']; 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, hasZ); } ol.format.XSD.writeStringTextNode(node, parts.join(' ')); }; /** * @param {Node} node Node. * @param {ol.geom.LineString} line LineString geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.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.Polygon} geometry Polygon geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var hasZ = context['hasZ']; 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, hasZ: hasZ, srsName: srsName}, ol.format.GML2.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 {*} value Value. * @param {Array.<*>} objectStack Object stack. * @param {string=} opt_nodeName Node name. * @return {Node} Node. * @private */ ol.format.GML2.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 ? 'innerBoundaryIs' : 'outerBoundaryIs'); }; /** * @param {Node} node Node. * @param {ol.geom.Polygon} polygon Polygon geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.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.LinearRing} ring LinearRing geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeRing_ = function(node, ring, objectStack) { var linearRing = ol.xml.createElementNS(node.namespaceURI, 'LinearRing'); node.appendChild(linearRing); this.writeLinearRing_(linearRing, ring, objectStack); }; /** * @param {Array.<number>} point Point geometry. * @param {string=} opt_srsName Optional srsName * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. * @return {string} The coords string. * @private */ ol.format.GML2.prototype.getCoords_ = function(point, opt_srsName, opt_hasZ) { var axisOrientation = 'enu'; if (opt_srsName) { axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); } var coords = ((axisOrientation.substr(0, 2) === 'en') ? point[0] + ',' + point[1] : point[1] + ',' + point[0]); if (opt_hasZ) { // For newly created points, Z can be undefined. var z = point[2] || 0; coords += ',' + z; } return coords; }; /** * @param {Node} node Node. * @param {ol.geom.MultiLineString} geometry MultiLineString geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var hasZ = context['hasZ']; var srsName = context['srsName']; var curve = context['curve']; if (srsName) { node.setAttribute('srsName', srsName); } var lines = geometry.getLineStrings(); ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, ol.format.GML2.LINESTRINGORCURVEMEMBER_SERIALIZERS_, this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, objectStack, undefined, this); }; /** * @param {Node} node Node. * @param {ol.geom.Point} geometry Point geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writePoint_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var hasZ = context['hasZ']; var srsName = context['srsName']; if (srsName) { node.setAttribute('srsName', srsName); } var coordinates = this.createCoordinatesNode_(node.namespaceURI); node.appendChild(coordinates); var point = geometry.getCoordinates(); var coord = this.getCoords_(point, srsName, hasZ); ol.format.XSD.writeStringTextNode(coordinates, coord); }; /** * @param {Node} node Node. * @param {ol.geom.MultiPoint} geometry MultiPoint geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeMultiPoint_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var hasZ = context['hasZ']; var srsName = context['srsName']; if (srsName) { node.setAttribute('srsName', srsName); } var points = geometry.getPoints(); ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, ol.format.GML2.POINTMEMBER_SERIALIZERS_, ol.xml.makeSimpleNodeFactory('pointMember'), points, objectStack, undefined, this); }; /** * @param {Node} node Node. * @param {ol.geom.Point} point Point geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.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.GML2.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.LinearRing} geometry LinearRing geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeLinearRing_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var srsName = context['srsName']; if (srsName) { node.setAttribute('srsName', srsName); } var coordinates = this.createCoordinatesNode_(node.namespaceURI); node.appendChild(coordinates); this.writeCoordinates_(coordinates, geometry, objectStack); }; /** * @param {Node} node Node. * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var hasZ = context['hasZ']; var srsName = context['srsName']; var surface = context['surface']; if (srsName) { node.setAttribute('srsName', srsName); } var polygons = geometry.getPolygons(); ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, ol.format.GML2.SURFACEORPOLYGONMEMBER_SERIALIZERS_, this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, objectStack, undefined, this); }; /** * @param {Node} node Node. * @param {ol.geom.Polygon} polygon Polygon geometry. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.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.Extent} extent Extent. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.GML2.prototype.writeEnvelope = function(node, extent, objectStack) { 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.GML2.ENVELOPE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, keys, this); }; /** * @const * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.GML2.GEOMETRY_SERIALIZERS_ = { 'http://www.opengis.net/gml': { 'Curve': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeCurveOrLineString_), 'MultiCurve': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeMultiCurveOrLineString_), 'Point': ol.xml.makeChildAppender(ol.format.GML2.prototype.writePoint_), 'MultiPoint': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeMultiPoint_), 'LineString': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeCurveOrLineString_), 'MultiLineString': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeMultiCurveOrLineString_), 'LinearRing': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeLinearRing_), 'Polygon': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeSurfaceOrPolygon_), 'MultiPolygon': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_), 'Surface': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeSurfaceOrPolygon_), 'MultiSurface': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_), 'Envelope': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeEnvelope) } }; /** * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.GML2.RING_SERIALIZERS_ = { 'http://www.opengis.net/gml': { 'outerBoundaryIs': ol.xml.makeChildAppender(ol.format.GML2.prototype.writeRing_), 'innerBoundaryIs': ol.xml.makeChildAppender(ol.format.GML2.prototype.writeRing_) } }; /** * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.GML2.POINTMEMBER_SERIALIZERS_ = { 'http://www.opengis.net/gml': { 'pointMember': ol.xml.makeChildAppender( ol.format.GML2.prototype.writePointMember_) } }; /** * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.GML2.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { 'http://www.opengis.net/gml': { 'lineStringMember': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeLineStringOrCurveMember_), 'curveMember': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeLineStringOrCurveMember_) } }; /** * @const * @param {*} value Value. * @param {Array.<*>} objectStack Object stack. * @param {string=} opt_nodeName Node name. * @return {Node|undefined} Node. * @private */ ol.format.GML2.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { var parentNode = objectStack[objectStack.length - 1].node; return ol.xml.createElementNS('http://www.opengis.net/gml', ol.format.GML2.MULTIGEOMETRY_TO_MEMBER_NODENAME_[parentNode.nodeName]); }; /** * @const * @type {Object.<string, string>} * @private */ ol.format.GML2.MULTIGEOMETRY_TO_MEMBER_NODENAME_ = { 'MultiLineString': 'lineStringMember', 'MultiCurve': 'curveMember', 'MultiPolygon': 'polygonMember', 'MultiSurface': 'surfaceMember' }; /** * @const * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.GML2.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { 'http://www.opengis.net/gml': { 'surfaceMember': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeSurfaceOrPolygonMember_), 'polygonMember': ol.xml.makeChildAppender( ol.format.GML2.prototype.writeSurfaceOrPolygonMember_) } }; /** * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.GML2.ENVELOPE_SERIALIZERS_ = { 'http://www.opengis.net/gml': { 'lowerCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), 'upperCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) } }; 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 */ 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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 */ ol.format.GPX.prototype.readFeature; /** * @inheritDoc */ ol.format.GPX.prototype.readFeatureFromNode = function(node, opt_options) { 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 */ ol.format.GPX.prototype.readFeatures; /** * @inheritDoc */ ol.format.GPX.prototype.readFeaturesFromNode = function(node, opt_options) { 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 */ 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; 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; 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 */ 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. * @override * @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'); 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.IGCZ'); /** * IGC altitude/z. One of 'barometric', 'gps', 'none'. * @enum {string} */ ol.format.IGCZ = { BAROMETRIC: 'barometric', GPS: 'gps', NONE: 'none' }; 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 * @abstract * @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.IGCZ'); 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.IGCZ} */ this.altitudeMode_ = options.altitudeMode ? options.altitudeMode : ol.format.IGCZ.NONE; }; ol.inherits(ol.format.IGC, ol.format.TextFeature); /** * @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/; /** * 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.IGCZ.NONE) { var z; if (altitudeMode == ol.format.IGCZ.GPS) { z = parseInt(m[11], 10); } else if (altitudeMode == ol.format.IGCZ.BAROMETRIC) { z = parseInt(m[12], 10); } else { 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.IGCZ.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; /** * Not implemented. * @inheritDoc */ ol.format.IGC.prototype.writeFeatureText = function(feature, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.IGC.prototype.writeFeaturesText = function(features, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.IGC.prototype.writeGeometryText = function(geometry, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.IGC.prototype.readGeometryFromText = function(text, opt_options) {}; goog.provide('ol.style.IconAnchorUnits'); /** * Icon anchor units. One of 'fraction', 'pixels'. * @enum {string} */ ol.style.IconAnchorUnits = { FRACTION: 'fraction', PIXELS: 'pixels' }; 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.ImageState'); 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.ImageState} 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.ImageState} */ 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.ImageState.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.ImageState} 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.ImageState.ERROR; this.unlistenImage_(); this.dispatchChangeEvent_(); }; /** * @private */ ol.style.IconImage.prototype.handleImageLoad_ = function() { this.imageState_ = ol.ImageState.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.ImageState} 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.ImageState.IDLE) { this.imageState_ = ol.ImageState.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.IconOrigin'); /** * Icon origin. One of 'bottom-left', 'bottom-right', 'top-left', 'top-right'. * @enum {string} */ ol.style.IconOrigin = { BOTTOM_LEFT: 'bottom-left', BOTTOM_RIGHT: 'bottom-right', TOP_LEFT: 'top-left', TOP_RIGHT: 'top-right' }; goog.provide('ol.style.Icon'); goog.require('ol'); goog.require('ol.ImageState'); goog.require('ol.asserts'); goog.require('ol.color'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.style.IconAnchorUnits'); goog.require('ol.style.IconImage'); goog.require('ol.style.IconOrigin'); 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.IconOrigin} */ this.anchorOrigin_ = options.anchorOrigin !== undefined ? options.anchorOrigin : ol.style.IconOrigin.TOP_LEFT; /** * @private * @type {ol.style.IconAnchorUnits} */ this.anchorXUnits_ = options.anchorXUnits !== undefined ? options.anchorXUnits : ol.style.IconAnchorUnits.FRACTION; /** * @private * @type {ol.style.IconAnchorUnits} */ this.anchorYUnits_ = options.anchorYUnits !== undefined ? options.anchorYUnits : ol.style.IconAnchorUnits.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.ImageState} */ var imageState = options.src !== undefined ? ol.ImageState.IDLE : ol.ImageState.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.IconOrigin} */ this.offsetOrigin_ = options.offsetOrigin !== undefined ? options.offsetOrigin : ol.style.IconOrigin.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. The underlying Image/HTMLCanvasElement is not cloned. * @return {ol.style.Icon} The cloned style. * @api */ ol.style.Icon.prototype.clone = function() { 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, src: 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.IconAnchorUnits.FRACTION || this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { if (!size) { return null; } anchor = this.anchor_.slice(); if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION) { anchor[0] *= size[0]; } if (this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { anchor[1] *= size[1]; } } if (this.anchorOrigin_ != ol.style.IconOrigin.TOP_LEFT) { if (!size) { return null; } if (anchor === this.anchor_) { anchor = this.anchor_.slice(); } if (this.anchorOrigin_ == ol.style.IconOrigin.TOP_RIGHT || this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { anchor[0] = -anchor[0] + size[0]; } if (this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT || this.anchorOrigin_ == ol.style.IconOrigin.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. * @override * @api */ ol.style.Icon.prototype.getImage = function(pixelRatio) { return this.iconImage_.getImage(pixelRatio); }; /** * @override */ ol.style.Icon.prototype.getImageSize = function() { return this.iconImage_.getSize(); }; /** * @override */ ol.style.Icon.prototype.getHitDetectionImageSize = function() { return this.getImageSize(); }; /** * @override */ ol.style.Icon.prototype.getImageState = function() { return this.iconImage_.getImageState(); }; /** * @override */ 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.IconOrigin.TOP_LEFT) { var size = this.getSize(); var iconImageSize = this.iconImage_.getSize(); if (!size || !iconImageSize) { return null; } offset = offset.slice(); if (this.offsetOrigin_ == ol.style.IconOrigin.TOP_RIGHT || this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { offset[0] = iconImageSize[0] - size[0] - offset[0]; } if (this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT || this.offsetOrigin_ == ol.style.IconOrigin.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_; }; /** * @override */ 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. * @override * @api */ ol.style.Icon.prototype.load = function() { this.iconImage_.load(); }; /** * @override */ ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) { ol.events.unlisten(this.iconImage_, ol.events.EventType.CHANGE, listener, thisArg); }; goog.provide('ol.style.Text'); goog.require('ol.style.Fill'); goog.require('ol.style.TextPlacement'); /** * @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 {number} */ this.maxAngle_ = options.maxAngle !== undefined ? options.maxAngle : Math.PI / 4; /** * @private * @type {ol.style.TextPlacement|string} */ this.placement_ = options.placement !== undefined ? options.placement : ol.style.TextPlacement.POINT; //TODO Use options.overflow directly after removing @deprecated exceedLength var overflow = options.overflow === undefined ? options.exceedLength : options.overflow; /** * @private * @type {boolean} */ this.overflow_ = overflow !== undefined ? overflow : false; /** * @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; /** * @private * @type {ol.style.Fill} */ this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null; /** * @private * @type {ol.style.Stroke} */ this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null; /** * @private * @type {Array.<number>} */ this.padding_ = options.padding === undefined ? null : options.padding; }; /** * 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(), placement: this.getPlacement(), maxAngle: this.getMaxAngle(), overflow: this.getOverflow(), 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 `overflow` configuration. * @return {boolean} Let text overflow the length of the path they follow. * @api */ ol.style.Text.prototype.getOverflow = function() { return this.overflow_; }; /** * Get the font name. * @return {string|undefined} Font. * @api */ ol.style.Text.prototype.getFont = function() { return this.font_; }; /** * Get the maximum angle between adjacent characters. * @return {number} Angle in radians. * @api */ ol.style.Text.prototype.getMaxAngle = function() { return this.maxAngle_; }; /** * Get the label placement. * @return {ol.style.TextPlacement|string} Text placement. * @api */ ol.style.Text.prototype.getPlacement = function() { return this.placement_; }; /** * 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_; }; /** * Get the background fill style for the text. * @return {ol.style.Fill} Fill style. * @api */ ol.style.Text.prototype.getBackgroundFill = function() { return this.backgroundFill_; }; /** * Get the background stroke style for the text. * @return {ol.style.Stroke} Stroke style. * @api */ ol.style.Text.prototype.getBackgroundStroke = function() { return this.backgroundStroke_; }; /** * Get the padding for the text. * @return {Array.<number>} Padding. * @api */ ol.style.Text.prototype.getPadding = function() { return this.padding_; }; /** * Set the `overflow` property. * * @param {boolean} overflow Let text overflow the path that it follows. * @api */ ol.style.Text.prototype.setOverflow = function(overflow) { this.overflow_ = overflow; }; /** * Set the font. * * @param {string|undefined} font Font. * @api */ ol.style.Text.prototype.setFont = function(font) { this.font_ = font; }; /** * Set the maximum angle between adjacent characters. * * @param {number} maxAngle Angle in radians. * @api */ ol.style.Text.prototype.setMaxAngle = function(maxAngle) { this.maxAngle_ = maxAngle; }; /** * 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 text placement. * * @param {ol.style.TextPlacement|string} placement Placement. * @api */ ol.style.Text.prototype.setPlacement = function(placement) { this.placement_ = placement; }; /** * 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; }; /** * Set the background fill. * * @param {ol.style.Fill} fill Fill style. * @api */ ol.style.Text.prototype.setBackgroundFill = function(fill) { this.backgroundFill_ = fill; }; /** * Set the background stroke. * * @param {ol.style.Stroke} stroke Stroke style. * @api */ ol.style.Text.prototype.setBackgroundStroke = function(stroke) { this.backgroundStroke_ = stroke; }; /** * Set the padding (`[top, right, bottom, left]`). * * @param {!Array.<number>} padding Padding. * @api */ ol.style.Text.prototype.setPadding = function(padding) { this.padding_ = padding; }; // 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.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.IconAnchorUnits'); goog.require('ol.style.IconOrigin'); 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 */ 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.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.IconAnchorUnits} * @private */ ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_ = ol.style.IconAnchorUnits.PIXELS; /** * @const * @type {ol.style.IconAnchorUnits} * @private */ ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_ = ol.style.IconAnchorUnits.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.IconOrigin.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.IconAnchorUnits>} * @private */ ol.format.KML.ICON_ANCHOR_UNITS_MAP_ = { 'fraction': ol.style.IconAnchorUnits.FRACTION, 'pixels': ol.style.IconAnchorUnits.PIXELS, 'insetPixels': ol.style.IconAnchorUnits.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(); var baseURI = node.baseURI; if (!baseURI || baseURI == 'about:blank') { baseURI = window.location.href; } if (baseURI) { var url = new URL(s, 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'); var origin; if (xunits !== 'insetPixels') { if (yunits !== 'insetPixels') { origin = ol.style.IconOrigin.BOTTOM_LEFT; } else { origin = ol.style.IconOrigin.TOP_LEFT; } } else { if (yunits !== 'insetPixels') { origin = ol.style.IconOrigin.BOTTOM_RIGHT; } else { origin = ol.style.IconOrigin.TOP_RIGHT; } } 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], origin: origin }; }; /** * @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) { // 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 anchorOrigin = ol.style.IconOrigin.BOTTOM_LEFT; var hotSpot = /** @type {ol.KMLVec2_|undefined} */ (object['hotSpot']); if (hotSpot) { anchor = [hotSpot.x, hotSpot.y]; anchorXUnits = hotSpot.xunits; anchorYUnits = hotSpot.yunits; anchorOrigin = hotSpot.origin; } 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.IconAnchorUnits.FRACTION; anchorYUnits = ol.style.IconAnchorUnits.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: anchorOrigin, anchorXUnits: anchorXUnits, anchorYUnits: anchorYUnits, crossOrigin: 'anonymous', // FIXME should this be configurable? offset: offset, offsetOrigin: ol.style.IconOrigin.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) { // 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) { // 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) { // 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) { 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) { 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) { 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) { 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; 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) { 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) { 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) { 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) { 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) { 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.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) { 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); 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) { 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) { 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 tessellates = new Array(geometries.length); var altitudeModes = new Array(geometries.length); var geometry, i, hasExtrude, hasTessellate, hasAltitudeMode; hasExtrude = hasTessellate = hasAltitudeMode = false; for (i = 0; i < ii; ++i) { geometry = geometries[i]; extrudes[i] = geometry.get('extrude'); tessellates[i] = geometry.get('tessellate'); altitudeModes[i] = geometry.get('altitudeMode'); hasExtrude = hasExtrude || extrudes[i] !== undefined; hasTessellate = hasTessellate || tessellates[i] !== undefined; hasAltitudeMode = hasAltitudeMode || altitudeModes[i]; } if (hasExtrude) { multiGeometry.set('extrude', extrudes); } if (hasTessellate) { multiGeometry.set('tessellate', tessellates); } if (hasAltitudeMode) { multiGeometry.set('altitudeMode', altitudeModes); } }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.KML.DataParser_ = function(node, objectStack) { 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; } delete featureObject['value']; }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.KML.ExtendedDataParser_ = function(node, objectStack) { 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.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) { 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) { 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.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) { 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) { 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) { 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) { /** @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]); flatLinearRings.push(flatLinearRing); } }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.KML.outerBoundaryIsParser_ = function(node, objectStack) { /** @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]); flatLinearRings[0] = flatLinearRing; } }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.KML.LinkParser_ = function(node, objectStack) { 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) { 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), 'tessellate': 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_ }); /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private * @return {Array.<ol.Feature>|undefined} Features. */ ol.format.KML.prototype.readDocumentOrFolder_ = function(node, objectStack) { // 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) { 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) { var id = node.getAttribute('id'); if (id !== null) { var style = ol.format.KML.readStyle_(node, objectStack); if (style) { var styleUri; var baseURI = node.baseURI; if (!baseURI || baseURI == 'about:blank') { baseURI = window.location.href; } if (baseURI) { var url = new URL('#' + id, 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) { var id = node.getAttribute('id'); if (id === null) { return; } var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack); if (!styleMapValue) { return; } var styleUri; var baseURI = node.baseURI; if (!baseURI || baseURI == 'about:blank') { baseURI = window.location.href; } if (baseURI) { var url = new URL('#' + id, 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 */ ol.format.KML.prototype.readFeature; /** * @inheritDoc */ ol.format.KML.prototype.readFeatureFromNode = function(node, opt_options) { if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) { return null; } 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 */ ol.format.KML.prototype.readFeatures; /** * @inheritDoc */ ol.format.KML.prototype.readFeaturesFromNode = function(node, opt_options) { 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 */ 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 */ 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] !== size[0] / 2 || anchor[1] !== size[1] / 2)) { var /** @type {ol.KMLVec2_} */ hotSpot = { x: anchor[0], xunits: ol.style.IconAnchorUnits.PIXELS, y: size[1] - anchor[1], yunits: ol.style.IconAnchorUnits.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) { var flatCoordinates = geometry.getFlatCoordinates(); var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; context['layout'] = geometry.getLayout(); context['stride'] = geometry.getStride(); // serialize properties (properties unknown to KML are not serialized) var properties = geometry.getProperties(); properties.coordinates = flatCoordinates; var parentNode = objectStack[objectStack.length - 1].node; var orderedKeys = ol.format.KML.PRIMITIVE_GEOMETRY_SEQUENCE_[parentNode.namespaceURI]; var values = ol.xml.makeSequence(properties, orderedKeys); ol.xml.pushSerializeAndPop(context, ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); }; /** * @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(); 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, Array.<string>>} * @private */ ol.format.KML.PRIMITIVE_GEOMETRY_SEQUENCE_ = ol.xml.makeStructureNS( ol.format.KML.NAMESPACE_URIS_, [ 'extrude', 'tessellate', 'altitudeMode', 'coordinates' ]); /** * @const * @type {Object.<string, Object.<string, ol.XmlSerializer>>} * @private */ ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( ol.format.KML.NAMESPACE_URIS_, { 'extrude': ol.xml.makeChildAppender(ol.format.XSD.writeBooleanTextNode), 'tessellate': ol.xml.makeChildAppender(ol.format.XSD.writeBooleanTextNode), 'altitudeMode': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), '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; 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; 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 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 */ 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. * @override * @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; }; /** * @fileoverview * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} */ goog.provide('ol.ext.PBF'); /** @typedef {function(*)} */ ol.ext.PBF = function() {}; (function() {(function (exports) { 'use strict'; var 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) }; var 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; }; var ieee754 = { read: read, write: write }; var pbf = Pbf; 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; Pbf.Fixed64 = 1; Pbf.Bytes = 2; Pbf.Fixed32 = 5; var SHIFT_LEFT_32 = (1 << 16) * (1 << 16); var SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; Pbf.prototype = { destroy: function() { this.buf = null; }, 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; }, 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() { return this.readVarint(true); }, readSVarint: function() { var num = this.readVarint(); return num % 2 === 1 ? (num + 1) / -2 : num / 2; }, 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; }, 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); }, 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++; var startPos = this.pos; this.pos = writeUtf8(this.buf, str, this.pos); var len = this.pos - startPos; if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); 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++; var startPos = this.pos; fn(obj, this); var len = this.pos - startPos; if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); 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)); 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]); } 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; 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); 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; } exports['default'] = pbf; }((this.PBF = this.PBF || {})));}).call(ol.ext); ol.ext.PBF = ol.ext.PBF.default; goog.provide('ol.render.Feature'); goog.require('ol'); goog.require('ol.array'); goog.require('ol.extent'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.flat.center'); goog.require('ol.geom.flat.interiorpoint'); goog.require('ol.geom.flat.interpolate'); goog.require('ol.geom.flat.transform'); goog.require('ol.transform'); /** * Lightweight, read-only, {@link ol.Feature} and {@link ol.geom.Geometry} like * structure, optimized for vector tile 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. * @param {number|string|undefined} id Feature id. */ ol.render.Feature = function(type, flatCoordinates, ends, properties, id) { /** * @private * @type {ol.Extent|undefined} */ this.extent_; /** * @private * @type {number|string|undefined} */ this.id_ = id; /** * @private * @type {ol.geom.GeometryType} */ this.type_ = type; /** * @private * @type {Array.<number>} */ this.flatCoordinates_ = flatCoordinates; /** * @private * @type {Array.<number>} */ this.flatInteriorPoints_ = null; /** * @private * @type {Array.<number>} */ this.flatMidpoints_ = null; /** * @private * @type {Array.<number>|Array.<Array.<number>>} */ this.ends_ = ends; /** * @private * @type {Object.<string, *>} */ this.properties_ = properties; /** * @private * @type {ol.Transform} */ this.tmpTransform_ = ol.transform.create(); }; /** * 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 = ol.render.Feature.prototype.getEndss = 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 interior points. */ ol.render.Feature.prototype.getFlatInteriorPoint = function() { if (!this.flatInteriorPoints_) { var flatCenter = ol.extent.getCenter(this.getExtent()); this.flatInteriorPoints_ = ol.geom.flat.interiorpoint.linearRings( this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0); } return this.flatInteriorPoints_; }; /** * @return {Array.<number>} Flat interior points. */ ol.render.Feature.prototype.getFlatInteriorPoints = function() { if (!this.flatInteriorPoints_) { var flatCenters = ol.geom.flat.center.linearRingss( this.flatCoordinates_, 0, this.ends_, 2); this.flatInteriorPoints_ = ol.geom.flat.interiorpoint.linearRingss( this.flatCoordinates_, 0, this.ends_, 2, flatCenters); } return this.flatInteriorPoints_; }; /** * @return {Array.<number>} Flat midpoint. */ ol.render.Feature.prototype.getFlatMidpoint = function() { if (!this.flatMidpoints_) { this.flatMidpoints_ = ol.geom.flat.interpolate.lineString( this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5); } return this.flatMidpoints_; }; /** * @return {Array.<number>} Flat midpoints. */ ol.render.Feature.prototype.getFlatMidpoints = function() { if (!this.flatMidpoints_) { this.flatMidpoints_ = []; var flatCoordinates = this.flatCoordinates_; var offset = 0; var ends = this.ends_; for (var i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var midpoint = ol.geom.flat.interpolate.lineString( flatCoordinates, offset, end, 2, 0.5); ol.array.extend(this.flatMidpoints_, midpoint); offset = end; } } return this.flatMidpoints_; }; /** * Get the feature identifier. This is a stable identifier for the feature and * is set when reading data from a remote source. * @return {number|string|undefined} Id. * @api */ ol.render.Feature.prototype.getId = function() { return this.id_; }; /** * @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; /** * For API compatibility with {@link ol.Feature}, this method is useful when * determining the geometry type in style function (see {@link #getType}). * @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_; }; /** * Transform geometry coordinates from tile pixel space to projected. * The SRS of the source and destination are expected to be the same. * * @param {ol.ProjectionLike} source The current projection * @param {ol.ProjectionLike} destination The desired projection. */ ol.render.Feature.prototype.transform = function(source, destination) { var pixelExtent = source.getExtent(); var projectedExtent = source.getWorldExtent(); var scale = ol.extent.getHeight(projectedExtent) / ol.extent.getHeight(pixelExtent); var transform = this.tmpTransform_; ol.transform.compose(transform, projectedExtent[0], projectedExtent[3], scale, -scale, 0, 0, 0); ol.geom.flat.transform.transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, transform, this.flatCoordinates_); }; //FIXME Implement projection handling goog.provide('ol.format.MVT'); goog.require('ol'); goog.require('ol.asserts'); goog.require('ol.ext.PBF'); 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.MultiPolygon'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.geom.flat.orient'); 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: 'EPSG:3857', 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,*>,number)} */ this.featureClass_ = options.featureClass ? options.featureClass : ol.render.Feature; /** * @private * @type {string|undefined} */ this.geometryName_ = options.geometryName; /** * @private * @type {string} */ this.layerName_ = options.layerName ? options.layerName : 'layer'; /** * @private * @type {Array.<string>} */ this.layers_ = options.layers ? options.layers : null; /** * @private * @type {ol.Extent} */ this.extent_ = null; }; ol.inherits(ol.format.MVT, ol.format.Feature); /** * Reader callbacks for parsing the PBF. * @type {Object.<string, function(number, Object, ol.ext.PBF)>} */ ol.format.MVT.pbfReaders_ = { layers: function(tag, layers, pbf) { if (tag === 3) { var layer = { keys: [], values: [], features: [] }; var end = pbf.readVarint() + pbf.pos; pbf.readFields(ol.format.MVT.pbfReaders_.layer, layer, end); layer.length = layer.features.length; if (layer.length) { layers[layer.name] = layer; } } }, layer: function(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) { var value = null; var end = pbf.readVarint() + pbf.pos; while (pbf.pos < end) { 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; } layer.values.push(value); } }, feature: function(tag, feature, pbf) { if (tag == 1) { feature.id = pbf.readVarint(); } else if (tag == 2) { var end = pbf.readVarint() + pbf.pos; while (pbf.pos < end) { var key = feature.layer.keys[pbf.readVarint()]; var value = feature.layer.values[pbf.readVarint()]; feature.properties[key] = value; } } else if (tag == 3) { feature.type = pbf.readVarint(); } else if (tag == 4) { feature.geometry = pbf.pos; } } }; /** * Read a raw feature from the pbf offset stored at index `i` in the raw layer. * @suppress {missingProperties} * @private * @param {ol.ext.PBF} pbf PBF. * @param {Object} layer Raw layer. * @param {number} i Index of the feature in the raw layer's `features` array. * @return {Object} Raw feature. */ ol.format.MVT.readRawFeature_ = function(pbf, layer, i) { pbf.pos = layer.features[i]; var end = pbf.readVarint() + pbf.pos; var feature = { layer: layer, type: 0, properties: {} }; pbf.readFields(ol.format.MVT.pbfReaders_.feature, feature, end); return feature; }; /** * Read the raw geometry from the pbf offset stored in a raw feature's geometry * proeprty. * @suppress {missingProperties} * @private * @param {ol.ext.PBF} pbf PBF. * @param {Object} feature Raw feature. * @param {Array.<number>} flatCoordinates Array to store flat coordinates in. * @param {Array.<number>} ends Array to store ends in. */ ol.format.MVT.readRawGeometry_ = function(pbf, feature, flatCoordinates, ends) { pbf.pos = feature.geometry; var end = pbf.readVarint() + pbf.pos; var cmd = 1; var length = 0; var x = 0; var y = 0; var coordsLen = 0; var currentEnd = 0; 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 (coordsLen > currentEnd) { ends.push(coordsLen); currentEnd = coordsLen; } } flatCoordinates.push(x, y); coordsLen += 2; } else if (cmd === 7) { if (coordsLen > currentEnd) { // close polygon flatCoordinates.push( flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]); coordsLen += 2; } } else { ol.asserts.assert(false, 59); // Invalid command found in the PBF } } if (coordsLen > currentEnd) { ends.push(coordsLen); currentEnd = coordsLen; } }; /** * @suppress {missingProperties} * @private * @param {number} type The raw feature's geometry type * @param {number} numEnds Number of ends of the flat coordinates of the * geometry. * @return {ol.geom.GeometryType} The geometry type. */ ol.format.MVT.getGeometryType_ = function(type, numEnds) { /** @type {ol.geom.GeometryType} */ var geometryType; if (type === 1) { geometryType = numEnds === 1 ? ol.geom.GeometryType.POINT : ol.geom.GeometryType.MULTI_POINT; } else if (type === 2) { geometryType = numEnds === 1 ? ol.geom.GeometryType.LINE_STRING : ol.geom.GeometryType.MULTI_LINE_STRING; } else if (type === 3) { geometryType = ol.geom.GeometryType.POLYGON; // MultiPolygon not relevant for rendering - winding order determines // outer rings of polygons. } return geometryType; }; /** * @private * @param {ol.ext.PBF} pbf PBF * @param {Object} rawFeature Raw Mapbox feature. * @param {olx.format.ReadOptions=} opt_options Read options. * @return {ol.Feature|ol.render.Feature} Feature. */ ol.format.MVT.prototype.createFeature_ = function(pbf, rawFeature, opt_options) { var type = rawFeature.type; if (type === 0) { return null; } var feature; var id = rawFeature.id; var values = rawFeature.properties; values[this.layerName_] = rawFeature.layer.name; var flatCoordinates = []; var ends = []; ol.format.MVT.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends); var geometryType = ol.format.MVT.getGeometryType_(type, ends.length); if (this.featureClass_ === ol.render.Feature) { feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id); } else { var geom; if (geometryType == ol.geom.GeometryType.POLYGON) { var endss = []; var offset = 0; var prevEndIndex = 0; for (var i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; if (!ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates, offset, end, 2)) { endss.push(ends.slice(prevEndIndex, i)); prevEndIndex = i; } offset = end; } if (endss.length > 1) { ends = endss; geom = new ol.geom.MultiPolygon(null); } else { geom = new ol.geom.Polygon(null); } } else { geom = geometryType === ol.geom.GeometryType.POINT ? new ol.geom.Point(null) : geometryType === ol.geom.GeometryType.LINE_STRING ? new ol.geom.LineString(null) : geometryType === ol.geom.GeometryType.POLYGON ? new ol.geom.Polygon(null) : geometryType === ol.geom.GeometryType.MULTI_POINT ? new ol.geom.MultiPoint (null) : geometryType === ol.geom.GeometryType.MULTI_LINE_STRING ? new ol.geom.MultiLineString(null) : null; } geom.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates, ends); feature = new this.featureClass_(); if (this.geometryName_) { feature.setGeometryName(this.geometryName_); } var geometry = ol.format.Feature.transformWithOptions(geom, false, this.adaptOptions(opt_options)); feature.setGeometry(geometry); feature.setId(id); feature.setProperties(values); } return feature; }; /** * @inheritDoc * @api */ ol.format.MVT.prototype.getLastExtent = function() { return this.extent_; }; /** * @inheritDoc */ ol.format.MVT.prototype.getType = function() { return ol.format.FormatType.ARRAY_BUFFER; }; /** * @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 pbfLayers = pbf.readFields(ol.format.MVT.pbfReaders_.layers, {}); /** @type {Array.<ol.Feature|ol.render.Feature>} */ var features = []; var pbfLayer; for (var name in pbfLayers) { if (layers && layers.indexOf(name) == -1) { continue; } pbfLayer = pbfLayers[name]; var rawFeature; for (var i = 0, ii = pbfLayer.length; i < ii; ++i) { rawFeature = ol.format.MVT.readRawFeature_(pbf, pbfLayer, i); features.push(this.createFeature_(pbf, rawFeature)); } this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null; } 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; }; /** * Not implemented. * @override */ ol.format.MVT.prototype.readFeature = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.readGeometry = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.writeFeature = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.writeGeometry = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.writeFeatures = function() {}; // 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 */ 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); /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.OSMXML.readNode_ = function(node, objectStack) { 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) { var id = node.getAttribute('id'); var values = ol.xml.pushParseAndPop({ id: id, ndrefs: [], tags: {} }, ol.format.OSMXML.WAY_PARSERS_, node, objectStack); var state = /** @type {Object} */ (objectStack[objectStack.length - 1]); state.ways.push(values); }; /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. * @private */ ol.format.OSMXML.readNd_ = function(node, objectStack) { 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) { 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 */ ol.format.OSMXML.prototype.readFeatures; /** * @inheritDoc */ ol.format.OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) { var options = this.getReadOptions(node, opt_options); if (node.localName == 'osm') { var state = ol.xml.pushParseAndPop({ nodes: {}, ways: [], features: [] }, ol.format.OSMXML.PARSERS_, node, [options]); // parse nodes in ways for (var j = 0; j < state.ways.length; j++) { var values = /** @type {Object} */ (state.ways[j]); /** @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(values.id); feature.setProperties(values.tags); state.features.push(feature); } 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 */ ol.format.OSMXML.prototype.readProjection; /** * Not implemented. * @inheritDoc */ ol.format.OSMXML.prototype.writeFeatureNode = function(feature, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.OSMXML.prototype.writeFeaturesNode = function(features, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.OSMXML.prototype.writeGeometryNode = function(geometry, opt_options) {}; 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 * @abstract * @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); /** * @inheritDoc */ ol.format.OWS.prototype.readFromDocument = function(doc) { for (var n = doc.firstChild; n; n = n.nextSibling) { if (n.nodeType == Node.ELEMENT_NODE) { return this.readFromNode(n); } } return null; }; /** * @inheritDoc */ ol.format.OWS.prototype.readFromNode = function(node) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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_, { 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), 'AccessConstraints': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), 'Fees': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), '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 */ 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 */ 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 */ 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 */ 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 */ 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) { 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 */ 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.proj'); /** * @classdesc * Feature format for reading data in the TopoJSON format. * * @constructor * @extends {ol.format.JSONFeature} * @param {olx.format.TopoJSONOptions=} opt_options Options. * @api */ ol.format.TopoJSON = function(opt_options) { var options = opt_options ? opt_options : {}; ol.format.JSONFeature.call(this); /** * @private * @type {string|undefined} */ this.layerName_ = options.layerName; /** * @private * @type {Array.<string>} */ this.layers_ = options.layers ? options.layers : null; /** * @inheritDoc */ this.defaultDataProjection = ol.proj.get( options.defaultDataProjection ? options.defaultDataProjection : 'EPSG:4326'); }; ol.inherits(ol.format.TopoJSON, ol.format.JSONFeature); /** * 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); }; /** * 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 {string|undefined} property Property to set the `GeometryCollection`'s parent * object to. * @param {string} name Name of the `Topology`'s child object. * @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, property, name, 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, property, name, 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 {string|undefined} property Property to set the `GeometryCollection`'s parent * object to. * @param {string} name Name of the `Topology`'s child object. * @param {olx.format.ReadOptions=} opt_options Read options. * @return {ol.Feature} Feature. * @private */ ol.format.TopoJSON.readFeatureFromGeometry_ = function(object, arcs, scale, translate, property, name, 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); } var properties = object.properties; if (property) { if (!properties) { properties = {}; } properties[property] = name; } if (properties) { feature.setProperties(properties); } return feature; }; /** * Read all features from a TopoJSON source. * * @function * @param {Document|Node|Object|string} source Source. * @return {Array.<ol.Feature>} Features. * @api */ 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 = topoJSONTopology.objects; var property = this.layerName_; var objectName, feature; for (objectName in topoJSONFeatures) { if (this.layers_ && this.layers_.indexOf(objectName) == -1) { continue; } if (topoJSONFeatures[objectName].type === 'GeometryCollection') { feature = /** @type {TopoJSONGeometryCollection} */ (topoJSONFeatures[objectName]); features.push.apply(features, ol.format.TopoJSON.readFeaturesFromGeometryCollection_( feature, arcs, scale, translate, property, objectName, opt_options)); } else { feature = /** @type {TopoJSONGeometry} */ (topoJSONFeatures[objectName]); features.push(ol.format.TopoJSON.readFeatureFromGeometry_( feature, arcs, scale, translate, property, objectName, 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. * * @param {Document|Node|Object|string} object Source. * @return {ol.proj.Projection} Projection. * @override * @api */ ol.format.TopoJSON.prototype.readProjection; /** * @inheritDoc */ ol.format.TopoJSON.prototype.readProjectionFromObject = 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_ }; /** * Not implemented. * @inheritDoc */ ol.format.TopoJSON.prototype.writeFeatureObject = function(feature, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.TopoJSON.prototype.writeFeaturesObject = function(features, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.TopoJSON.prototype.writeGeometryObject = function(geometry, opt_options) {}; /** * Not implemented. * @override */ ol.format.TopoJSON.prototype.readGeometryFromObject = function() {}; /** * Not implemented. * @override */ ol.format.TopoJSON.prototype.readFeatureFromObject = function() {}; goog.provide('ol.format.WFS'); goog.require('ol'); goog.require('ol.asserts'); goog.require('ol.format.GML2'); 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 */ 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_LOCATIONS[ol.format.WFS.DEFAULT_VERSION]; 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.FESNS = 'http://www.opengis.net/fes'; /** * @const * @type {Object.<string, string>} */ ol.format.WFS.SCHEMA_LOCATIONS = { '1.1.0': 'http://www.opengis.net/wfs ' + 'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd', '1.0.0': 'http://www.opengis.net/wfs ' + 'http://schemas.opengis.net/wfs/1.0.0/wfs.xsd' }; /** * @const * @type {string} */ ol.format.WFS.DEFAULT_VERSION = '1.1.0'; /** * @return {Array.<string>|string|undefined} featureType */ ol.format.WFS.prototype.getFeatureType = function() { return this.featureType_; }; /** * @param {Array.<string>|string|undefined} featureType Feature type(s) to parse. */ ol.format.WFS.prototype.setFeatureType = function(featureType) { this.featureType_ = featureType; }; /** * 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 */ 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 */ 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 */ 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) { 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) { 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) { 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) { 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 gmlVersion = context['gmlVersion']; var child = ol.xml.createElementNS(featureNS, featureType); node.appendChild(child); if (gmlVersion === 2) { ol.format.GML2.prototype.writeFeatureElement(child, feature, objectStack); } else { 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 {string|undefined} featurePrefix The prefix of the feature. * @param {string} featureType The type of the feature. * @returns {string} The value of the typeName property. * @private */ ol.format.WFS.getTypeName_ = function(featurePrefix, featureType) { featurePrefix = featurePrefix ? featurePrefix : ol.format.WFS.FEATURE_PREFIX; var prefix = featurePrefix + ':'; // The featureType already contains the prefix. if (featureType.indexOf(prefix) === 0) { return featureType; } else { return prefix + featureType; } }; /** * @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']; var featureNS = context['featureNS']; var typeName = ol.format.WFS.getTypeName_(featurePrefix, featureType); node.setAttribute('typeName', typeName); 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']; var featureNS = context['featureNS']; var typeName = ol.format.WFS.getTypeName_(featurePrefix, featureType); var geometryName = feature.getGeometryName(); node.setAttribute('typeName', typeName); 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) { var name = keys[i]; if (value instanceof ol.geom.Geometry) { name = geometryName; } values.push({name: name, value: value}); } } ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ( {'gmlVersion': context['gmlVersion'], node: node, 'hasZ': context['hasZ'], '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'); var context = objectStack[objectStack.length - 1]; var gmlVersion = context['gmlVersion']; 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) { if (gmlVersion === 2) { ol.format.GML2.prototype.writeGeometryElement(value, pair.value, objectStack); } else { 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 typeName; // If feature prefix is not defined, we must not use the default prefix. if (featurePrefix) { typeName = ol.format.WFS.getTypeName_(featurePrefix, featureType); } else { typeName = featureType; } node.setAttribute('typeName', typeName); 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.Contains} filter Filter. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.WFS.writeContainsFilter_ = 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.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.During} filter Filter. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.WFS.writeDuringFilter_ = function(node, filter, objectStack) { var valueReference = ol.xml.createElementNS(ol.format.WFS.FESNS, 'ValueReference'); ol.format.XSD.writeStringTextNode(valueReference, filter.propertyName); node.appendChild(valueReference); var timePeriod = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'TimePeriod'); node.appendChild(timePeriod); var begin = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'begin'); timePeriod.appendChild(begin); ol.format.WFS.writeTimeInstant_(begin, filter.begin); var end = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'end'); timePeriod.appendChild(end); ol.format.WFS.writeTimeInstant_(end, filter.end); }; /** * @param {Node} node Node. * @param {ol.format.filter.LogicalNary} filter Filter. * @param {Array.<*>} objectStack Node stack. * @private */ ol.format.WFS.writeLogicalFilter_ = function(node, filter, objectStack) { /** @type {ol.XmlNodeStackItem} */ var item = {node: node}; var conditions = filter.conditions; for (var i = 0, ii = conditions.length; i < ii; ++i) { var condition = conditions[i]; ol.xml.pushSerializeAndPop(item, ol.format.WFS.GETFEATURE_SERIALIZERS_, ol.xml.makeSimpleNodeFactory(condition.getTagName()), [condition], 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); }; /** * @param {Node} node Node. * @param {string} time PropertyName value. * @private */ ol.format.WFS.writeTimeInstant_ = function(node, time) { var timeInstant = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'TimeInstant'); node.appendChild(timeInstant); var timePosition = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'timePosition'); timeInstant.appendChild(timePosition); ol.format.XSD.writeStringTextNode(timePosition, time); }; /** * @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': { 'During': ol.xml.makeChildAppender(ol.format.WFS.writeDuringFilter_), '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_), 'Contains': ol.xml.makeChildAppender(ol.format.WFS.writeContainsFilter_), '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_) } }; /** * Encode filter as WFS `Filter` and return the Node. * * @param {ol.format.filter.Filter} filter Filter. * @return {Node} Result. * @api */ ol.format.WFS.writeFilter = function(filter) { var child = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'Filter'); ol.format.WFS.writeFilterCondition_(child, filter, []); return child; }; /** * @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 */ 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 */ ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes, options) { var objectStack = []; var node = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Transaction'); var version = options.version ? options.version : ol.format.WFS.DEFAULT_VERSION; var gmlVersion = version === '1.0.0' ? 2 : 3; node.setAttribute('service', 'WFS'); node.setAttribute('version', version); var baseObj; /** @type {ol.XmlNodeStackItem} */ var obj; if (options) { baseObj = options.gmlOptions ? options.gmlOptions : {}; if (options.handle) { node.setAttribute('handle', options.handle); } } var schemaLocation = ol.format.WFS.SCHEMA_LOCATIONS[version]; ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation', schemaLocation); var featurePrefix = options.featurePrefix ? options.featurePrefix : ol.format.WFS.FEATURE_PREFIX; if (inserts) { obj = {node: node, 'featureNS': options.featureNS, 'featureType': options.featureType, 'featurePrefix': featurePrefix, 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, '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': featurePrefix, 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, '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': featurePrefix, 'gmlVersion': gmlVersion, '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': featurePrefix, 'gmlVersion': gmlVersion, '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 */ ol.format.WFS.prototype.readProjection; /** * @inheritDoc */ ol.format.WFS.prototype.readProjectionFromDocument = function(doc) { 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) { 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 */ 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]; 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 */ 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 */ 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 */ 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 */ 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 */ 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. * @param {olx.format.WriteOptions=} opt_options Write options. * @return {string} WKT string. * @api */ 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} * @private */ 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_(); 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; /** * @inheritDoc */ ol.format.WMSCapabilities.prototype.readFromDocument = function(doc) { for (var n = doc.firstChild; n; n = n.nextSibling) { if (n.nodeType == Node.ELEMENT_NODE) { return this.readFromNode(n); } } return null; }; /** * @inheritDoc */ ol.format.WMSCapabilities.prototype.readFromNode = function(node) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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'; /** * @return {Array.<string>} layers */ ol.format.WMSGetFeatureInfo.prototype.getLayers = function() { return this.layers_; }; /** * @param {Array.<string>} layers Layers to parse. */ ol.format.WMSGetFeatureInfo.prototype.setLayers = function(layers) { this.layers_ = layers; }; /** * @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_); 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]; 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 */ 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]); }; /** * Not implemented. * @inheritDoc */ ol.format.WMSGetFeatureInfo.prototype.writeFeatureNode = function(feature, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.WMSGetFeatureInfo.prototype.writeFeaturesNode = function(features, opt_options) {}; /** * Not implemented. * @inheritDoc */ ol.format.WMSGetFeatureInfo.prototype.writeGeometryNode = function(geometry, opt_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; /** * @inheritDoc */ ol.format.WMTSCapabilities.prototype.readFromDocument = function(doc) { for (var n = doc.firstChild; n; n = n.nextSibling) { if (n.nodeType == Node.ELEMENT_NODE) { return this.readFromNode(n); } } return null; }; /** * @inheritDoc */ ol.format.WMTSCapabilities.prototype.readFromNode = function(node) { 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) { 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) { 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) })); goog.provide('ol.GeolocationProperty'); /** * @enum {string} */ ol.GeolocationProperty = { ACCURACY: 'accuracy', ACCURACY_GEOMETRY: 'accuracyGeometry', ALTITUDE: 'altitude', ALTITUDE_ACCURACY: 'altitudeAccuracy', HEADING: 'heading', POSITION: 'position', PROJECTION: 'projection', SPEED: 'speed', TRACKING: 'tracking', TRACKING_OPTIONS: 'trackingOptions' }; // FIXME handle geolocation not supported goog.provide('ol.Geolocation'); goog.require('ol'); goog.require('ol.GeolocationProperty'); goog.require('ol.Object'); goog.require('ol.Sphere'); 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.proj.EPSG4326'); /** * @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 */ 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 {ol.Sphere} */ this.sphere_ = new ol.Sphere(ol.proj.EPSG4326.RADIUS); /** * @private * @type {number|undefined} */ this.watchId_ = undefined; ol.events.listen( this, ol.Object.getChangeEventType(ol.GeolocationProperty.PROJECTION), this.handleProjectionChanged_, this); ol.events.listen( this, ol.Object.getChangeEventType(ol.GeolocationProperty.TRACKING), this.handleTrackingChanged_, this); if (options.projection !== undefined) { this.setProjection(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.GeolocationProperty.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.GeolocationProperty.ACCURACY, coords.accuracy); this.set(ol.GeolocationProperty.ALTITUDE, coords.altitude === null ? undefined : coords.altitude); this.set(ol.GeolocationProperty.ALTITUDE_ACCURACY, coords.altitudeAccuracy === null ? undefined : coords.altitudeAccuracy); this.set(ol.GeolocationProperty.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.GeolocationProperty.POSITION, projectedPosition); this.set(ol.GeolocationProperty.SPEED, coords.speed === null ? undefined : coords.speed); var geometry = ol.geom.Polygon.circular( this.sphere_, this.position_, coords.accuracy); geometry.applyTransform(this.transform_); this.set(ol.GeolocationProperty.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 */ ol.Geolocation.prototype.getAccuracy = function() { return /** @type {number|undefined} */ ( this.get(ol.GeolocationProperty.ACCURACY)); }; /** * Get a geometry of the position accuracy. * @return {?ol.geom.Polygon} A geometry of the position accuracy. * @observable * @api */ ol.Geolocation.prototype.getAccuracyGeometry = function() { return /** @type {?ol.geom.Polygon} */ ( this.get(ol.GeolocationProperty.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 */ ol.Geolocation.prototype.getAltitude = function() { return /** @type {number|undefined} */ ( this.get(ol.GeolocationProperty.ALTITUDE)); }; /** * Get the altitude accuracy of the position. * @return {number|undefined} The accuracy of the altitude measurement in * meters. * @observable * @api */ ol.Geolocation.prototype.getAltitudeAccuracy = function() { return /** @type {number|undefined} */ ( this.get(ol.GeolocationProperty.ALTITUDE_ACCURACY)); }; /** * Get the heading as radians clockwise from North. * @return {number|undefined} The heading of the device in radians from north. * @observable * @api */ ol.Geolocation.prototype.getHeading = function() { return /** @type {number|undefined} */ ( this.get(ol.GeolocationProperty.HEADING)); }; /** * Get the position of the device. * @return {ol.Coordinate|undefined} The current position of the device reported * in the current projection. * @observable * @api */ ol.Geolocation.prototype.getPosition = function() { return /** @type {ol.Coordinate|undefined} */ ( this.get(ol.GeolocationProperty.POSITION)); }; /** * Get the projection associated with the position. * @return {ol.proj.Projection|undefined} The projection the position is * reported in. * @observable * @api */ ol.Geolocation.prototype.getProjection = function() { return /** @type {ol.proj.Projection|undefined} */ ( this.get(ol.GeolocationProperty.PROJECTION)); }; /** * Get the speed in meters per second. * @return {number|undefined} The instantaneous speed of the device in meters * per second. * @observable * @api */ ol.Geolocation.prototype.getSpeed = function() { return /** @type {number|undefined} */ ( this.get(ol.GeolocationProperty.SPEED)); }; /** * Determine if the device location is being tracked. * @return {boolean} The device location is being tracked. * @observable * @api */ ol.Geolocation.prototype.getTracking = function() { return /** @type {boolean} */ ( this.get(ol.GeolocationProperty.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 */ ol.Geolocation.prototype.getTrackingOptions = function() { return /** @type {GeolocationPositionOptions|undefined} */ ( this.get(ol.GeolocationProperty.TRACKING_OPTIONS)); }; /** * Set the projection to use for transforming the coordinates. * @param {ol.ProjectionLike} projection The projection the position is * reported in. * @observable * @api */ ol.Geolocation.prototype.setProjection = function(projection) { this.set(ol.GeolocationProperty.PROJECTION, ol.proj.get(projection)); }; /** * Enable or disable tracking. * @param {boolean} tracking Enable tracking. * @observable * @api */ ol.Geolocation.prototype.setTracking = function(tracking) { this.set(ol.GeolocationProperty.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 */ ol.Geolocation.prototype.setTrackingOptions = function(options) { this.set(ol.GeolocationProperty.TRACKING_OPTIONS, options); }; 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. * @override * @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 */ 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; 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(); } }; /** * @inheritDoc */ ol.geom.Circle.prototype.getCoordinates = function() {}; /** * @inheritDoc */ ol.geom.Circle.prototype.setCoordinates = function(coordinates, opt_layout) {}; /** * @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 */ ol.geom.Circle.prototype.transform; goog.provide('ol.geom.flat.geodesic'); 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(); 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); } } 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.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; }; goog.provide('ol.Graticule'); goog.require('ol.coordinate'); goog.require('ol.extent'); goog.require('ol.geom.GeometryLayout'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.geom.flat.geodesic'); goog.require('ol.math'); goog.require('ol.proj'); goog.require('ol.render.EventType'); goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Text'); /** * 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.PluggableMap} * @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; /** * @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; /** * @type {Array.<ol.GraticuleLabelDataType>} * @private */ this.meridiansLabels_ = null; /** * @type {Array.<ol.GraticuleLabelDataType>} * @private */ this.parallelsLabels_ = null; if (options.showLabels == true) { var degreesToString = ol.coordinate.degreesToStringHDMS; /** * @type {null|function(number):string} * @private */ this.lonLabelFormatter_ = options.lonLabelFormatter == undefined ? degreesToString.bind(this, 'EW') : options.lonLabelFormatter; /** * @type {function(number):string} * @private */ this.latLabelFormatter_ = options.latLabelFormatter == undefined ? degreesToString.bind(this, 'NS') : options.latLabelFormatter; /** * Longitude label position in fractions (0..1) of view extent. 0 means * bottom, 1 means top. * @type {number} * @private */ this.lonLabelPosition_ = options.lonLabelPosition == undefined ? 0 : options.lonLabelPosition; /** * Latitude Label position in fractions (0..1) of view extent. 0 means left, 1 * means right. * @type {number} * @private */ this.latLabelPosition_ = options.latLabelPosition == undefined ? 1 : options.latLabelPosition; /** * @type {ol.style.Text} * @private */ this.lonLabelStyle_ = options.lonLabelStyle !== undefined ? options.lonLabelStyle : new ol.style.Text({ font: '12px Calibri,sans-serif', textBaseline: 'bottom', fill: new ol.style.Fill({ color: 'rgba(0,0,0,1)' }), stroke: new ol.style.Stroke({ color: 'rgba(255,255,255,1)', width: 3 }) }); /** * @type {ol.style.Text} * @private */ this.latLabelStyle_ = options.latLabelStyle !== undefined ? options.latLabelStyle : new ol.style.Text({ font: '12px Calibri,sans-serif', textAlign: 'end', fill: new ol.style.Fill({ color: 'rgba(0,0,0,1)' }), stroke: new ol.style.Stroke({ color: 'rgba(255,255,255,1)', width: 3 }) }); this.meridiansLabels_ = []; this.parallelsLabels_ = []; } 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)) { if (this.meridiansLabels_) { var textPoint = this.getMeridianPoint_(lineString, extent, index); this.meridiansLabels_[index] = { geom: textPoint, text: this.lonLabelFormatter_(lon) }; } this.meridians_[index++] = lineString; } return index; }; /** * @param {ol.geom.LineString} lineString Meridian * @param {ol.Extent} extent Extent. * @param {number} index Index. * @return {ol.geom.Point} Meridian point. * @private */ ol.Graticule.prototype.getMeridianPoint_ = function(lineString, extent, index) { var flatCoordinates = lineString.getFlatCoordinates(); var clampedBottom = Math.max(extent[1], flatCoordinates[1]); var clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]); var lat = ol.math.clamp( extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_, clampedBottom, clampedTop); var coordinate = [flatCoordinates[0], lat]; var point = this.meridiansLabels_[index] !== undefined ? this.meridiansLabels_[index].geom : new ol.geom.Point(null); point.setCoordinates(coordinate); return point; }; /** * @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)) { if (this.parallelsLabels_) { var textPoint = this.getParallelPoint_(lineString, extent, index); this.parallelsLabels_[index] = { geom: textPoint, text: this.latLabelFormatter_(lat) }; } this.parallels_[index++] = lineString; } return index; }; /** * @param {ol.geom.LineString} lineString Parallels. * @param {ol.Extent} extent Extent. * @param {number} index Index. * @return {ol.geom.Point} Parallel point. * @private */ ol.Graticule.prototype.getParallelPoint_ = function(lineString, extent, index) { var flatCoordinates = lineString.getFlatCoordinates(); var clampedLeft = Math.max(extent[0], flatCoordinates[0]); var clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]); var lon = ol.math.clamp( extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_, clampedLeft, clampedRight); var coordinate = [lon, flatCoordinates[1]]; var point = this.parallelsLabels_[index] !== undefined ? this.parallelsLabels_[index].geom : new ol.geom.Point(null); point.setCoordinates(coordinate); return point; }; /** * @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; if (this.meridiansLabels_) { this.meridiansLabels_.length = 0; } if (this.parallelsLabels_) { this.parallelsLabels_.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; if (this.meridiansLabels_) { this.meridiansLabels_.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; if (this.parallelsLabels_) { this.parallelsLabels_.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.PluggableMap} 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) { var flatCoordinates = ol.geom.flat.geodesic.meridian(lon, minLat, maxLat, this.projection_, squaredTolerance); 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) { var flatCoordinates = ol.geom.flat.geodesic.parallel(lat, minLon, maxLon, this.projection_, squaredTolerance); 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. Parallels 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); } 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.drawGeometry(line); } for (i = 0, l = this.parallels_.length; i < l; ++i) { line = this.parallels_[i]; vectorContext.drawGeometry(line); } var labelData; if (this.meridiansLabels_) { for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) { labelData = this.meridiansLabels_[i]; this.lonLabelStyle_.setText(labelData.text); vectorContext.setTextStyle(this.lonLabelStyle_); vectorContext.drawGeometry(labelData.geom); } } if (this.parallelsLabels_) { for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) { labelData = this.parallelsLabels_[i]; this.latLabelStyle_.setText(labelData.text); vectorContext.setTextStyle(this.latLabelStyle_); vectorContext.drawGeometry(labelData.geom); } } }; /** * @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]; 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.PluggableMap} map Map. * @api */ ol.Graticule.prototype.setMap = function(map) { if (this.map_) { this.map_.un(ol.render.EventType.POSTCOMPOSE, this.handlePostCompose_, this); this.map_.render(); } if (map) { map.on(ol.render.EventType.POSTCOMPOSE, this.handlePostCompose_, this); map.render(); } this.map_ = map; }; goog.provide('ol.Image'); goog.require('ol'); goog.require('ol.ImageBase'); goog.require('ol.ImageState'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.extent'); /** * @constructor * @extends {ol.ImageBase} * @param {ol.Extent} extent Extent. * @param {number|undefined} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {string} src Image source URI. * @param {?string} crossOrigin Cross origin. * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. */ ol.Image = function(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { ol.ImageBase.call(this, extent, resolution, pixelRatio, ol.ImageState.IDLE); /** * @private * @type {string} */ this.src_ = src; /** * @private * @type {HTMLCanvasElement|Image|HTMLVideoElement} */ this.image_ = new Image(); if (crossOrigin !== null) { this.image_.crossOrigin = crossOrigin; } /** * @private * @type {Array.<ol.EventsKey>} */ this.imageListenerKeys_ = null; /** * @protected * @type {ol.ImageState} */ this.state = ol.ImageState.IDLE; /** * @private * @type {ol.ImageLoadFunctionType} */ this.imageLoadFunction_ = imageLoadFunction; }; ol.inherits(ol.Image, ol.ImageBase); /** * @inheritDoc * @api */ ol.Image.prototype.getImage = function() { return this.image_; }; /** * Tracks loading or read errors. * * @private */ ol.Image.prototype.handleImageError_ = function() { this.state = ol.ImageState.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.ImageState.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. * @override * @api */ ol.Image.prototype.load = function() { if (this.state == ol.ImageState.IDLE || this.state == ol.ImageState.ERROR) { this.state = ol.ImageState.LOADING; this.changed(); 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; }; goog.provide('ol.Tile'); goog.require('ol'); goog.require('ol.TileState'); goog.require('ol.easing'); goog.require('ol.events.EventTarget'); goog.require('ol.events.EventType'); /** * @classdesc * Base class for tiles. * * @constructor * @abstract * @extends {ol.events.EventTarget} * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileState} state State. * @param {olx.TileOptions=} opt_options Tile options. */ ol.Tile = function(tileCoord, state, opt_options) { ol.events.EventTarget.call(this); var options = opt_options ? opt_options : {}; /** * @type {ol.TileCoord} */ this.tileCoord = tileCoord; /** * @protected * @type {ol.TileState} */ 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 = ''; /** * The duration for the opacity transition. * @type {number} */ this.transition_ = options.transition === undefined ? 250 : options.transition; /** * Lookup of start times for rendering transitions. If the start time is * equal to -1, the transition is complete. * @type {Object.<number, number>} */ this.transitionStarts_ = {}; }; ol.inherits(ol.Tile, ol.events.EventTarget); /** * @protected */ ol.Tile.prototype.changed = function() { this.dispatchEvent(ol.events.EventType.CHANGE); }; /** * @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.TileState.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.TileState.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.TileState.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.TileState.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.TileState} State. */ ol.Tile.prototype.getState = function() { return this.state; }; /** * @param {ol.TileState} state State. */ ol.Tile.prototype.setState = function(state) { this.state = state; 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. * @abstract * @api */ ol.Tile.prototype.load = function() {}; /** * Get the alpha value for rendering. * @param {number} id An id for the renderer. * @param {number} time The render frame time. * @return {number} A number between 0 and 1. */ ol.Tile.prototype.getAlpha = function(id, time) { if (!this.transition_) { return 1; } var start = this.transitionStarts_[id]; if (!start) { start = time; this.transitionStarts_[id] = start; } else if (start === -1) { return 1; } var delta = time - start + (1000 / 60); // avoid rendering at 0 if (delta >= this.transition_) { return 1; } return ol.easing.easeIn(delta / this.transition_); }; /** * Determine if a tile is in an alpha transition. A tile is considered in * transition if tile.getAlpha() has not yet been called or has been called * and returned 1. * @param {number} id An id for the renderer. * @return {boolean} The tile is in transition. */ ol.Tile.prototype.inTransition = function(id) { if (!this.transition_) { return false; } return this.transitionStarts_[id] !== -1; }; /** * Mark a transition as complete. * @param {number} id An id for the renderer. */ ol.Tile.prototype.endTransition = function(id) { if (this.transition_) { this.transitionStarts_[id] = -1; } }; goog.provide('ol.ImageTile'); goog.require('ol'); goog.require('ol.Tile'); goog.require('ol.TileState'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); /** * @constructor * @extends {ol.Tile} * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileState} state State. * @param {string} src Image source URI. * @param {?string} crossOrigin Cross origin. * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. * @param {olx.TileOptions=} opt_options Tile options. */ ol.ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { ol.Tile.call(this, tileCoord, state, opt_options); /** * @private * @type {?string} */ this.crossOrigin_ = crossOrigin; /** * Image URI * * @private * @type {string} */ this.src_ = src; /** * @private * @type {Image|HTMLCanvasElement} */ 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.TileState.LOADING) { this.unlistenImage_(); this.image_ = ol.ImageTile.getBlankImage(); } if (this.interimTile) { this.interimTile.dispose(); } this.state = ol.TileState.ABORT; this.changed(); ol.Tile.prototype.disposeInternal.call(this); }; /** * Get the HTML image element for this tile (may be a Canvas, Image, or Video). * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. * @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.TileState.ERROR; this.unlistenImage_(); this.image_ = ol.ImageTile.getBlankImage(); this.changed(); }; /** * Tracks successful image load. * * @private */ ol.ImageTile.prototype.handleImageLoad_ = function() { if (this.image_.naturalWidth && this.image_.naturalHeight) { this.state = ol.TileState.LOADED; } else { this.state = ol.TileState.EMPTY; } this.unlistenImage_(); this.changed(); }; /** * @inheritDoc * @api */ ol.ImageTile.prototype.load = function() { if (this.state == ol.TileState.ERROR) { this.state = ol.TileState.IDLE; this.image_ = new Image(); if (this.crossOrigin_ !== null) { this.image_.crossOrigin = this.crossOrigin_; } } if (this.state == ol.TileState.IDLE) { this.state = ol.TileState.LOADING; this.changed(); 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; }; /** * Get a 1-pixel blank image. * @return {HTMLCanvasElement} Blank image. */ ol.ImageTile.getBlankImage = function() { var ctx = ol.dom.createCanvasContext2D(1, 1); ctx.fillStyle = 'rgba(0,0,0,0)'; ctx.fillRect(0, 0, 1, 1); return ctx.canvas; }; // 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 */ 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 {ol.source.Vector} */ this.source_ = options.source || 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(); } var formatConstructors = this.formatConstructors_; var features = []; var i, ii; for (i = 0, ii = formatConstructors.length; i < ii; ++i) { /** * Avoid "cannot instantiate abstract class" error. * @type {Function} */ var formatConstructor = formatConstructors[i]; /** * @type {ol.format.Feature} */ var format = new formatConstructor(); features = this.tryReadFeatures_(format, result, { featureProjection: projection }); if (features && features.length > 0) { break; } } if (this.source_) { this.source_.clear(); this.source_.addFeatures(features); } 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; /** * @private */ ol.interaction.DragAndDrop.prototype.registerListeners_ = function() { var map = this.getMap(); 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) ]; } }; /** * @inheritDoc */ ol.interaction.DragAndDrop.prototype.setActive = function(active) { ol.interaction.Interaction.prototype.setActive.call(this, active); if (active) { this.registerListeners_(); } else { this.unregisterListeners_(); } }; /** * @inheritDoc */ ol.interaction.DragAndDrop.prototype.setMap = function(map) { this.unregisterListeners_(); ol.interaction.Interaction.prototype.setMap.call(this, map); if (this.getActive()) { this.registerListeners_(); } }; /** * @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; } }; /** * @private */ ol.interaction.DragAndDrop.prototype.unregisterListeners_ = function() { if (this.dropListenKeys_) { this.dropListenKeys_.forEach(ol.events.unlistenByKey); this.dropListenKeys_ = null; } }; /** * @enum {string} * @private */ ol.interaction.DragAndDrop.EventType_ = { /** * Triggered when features are added * @event ol.interaction.DragAndDrop.Event#addfeatures * @api */ 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 */ this.features = opt_features; /** * The dropped file. * @type {File} * @api */ 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.RotationConstraint'); goog.require('ol.ViewHint'); 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 */ 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 (view.getConstraints().rotation !== ol.RotationConstraint.disable && this.lastAngle_ !== undefined) { var angleDelta = theta - this.lastAngle_; ol.interaction.Interaction.rotateWithoutConstraints( view, view.getRotation() - angleDelta); } this.lastAngle_ = theta; if (this.lastMagnitude_ !== undefined) { var resolution = this.lastMagnitude_ * (view.getResolution() / magnitude); ol.interaction.Interaction.zoomWithoutConstraints(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.ViewHint.INTERACTING, -1); var direction = this.lastScaleDelta_ - 1; ol.interaction.Interaction.rotate(view, view.getRotation()); ol.interaction.Interaction.zoom(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.ViewHint.INTERACTING, 1); this.lastAngle_ = undefined; this.lastMagnitude_ = undefined; return true; } else { return false; } }; goog.provide('ol.interaction.DrawEventType'); /** * @enum {string} */ ol.interaction.DrawEventType = { /** * Triggered upon feature draw start * @event ol.interaction.Draw.Event#drawstart * @api */ DRAWSTART: 'drawstart', /** * Triggered upon feature draw end * @event ol.interaction.Draw.Event#drawend * @api */ DRAWEND: 'drawend' }; goog.provide('ol.layer.Vector'); goog.require('ol'); goog.require('ol.LayerType'); goog.require('ol.layer.Layer'); goog.require('ol.layer.VectorRenderType'); 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 */ ol.layer.Vector = function(opt_options) { var options = opt_options ? opt_options : /** @type {olx.layer.VectorOptions} */ ({}); 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)); /** * @private * @type {boolean} */ this.declutter_ = options.declutter !== undefined ? options.declutter : false; /** * @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; /** * @private * @type {ol.layer.VectorTileRenderType|string} */ this.renderMode_ = options.renderMode || ol.layer.VectorRenderType.VECTOR; /** * The layer type. * @protected * @type {ol.LayerType} */ this.type = ol.LayerType.VECTOR; }; ol.inherits(ol.layer.Vector, ol.layer.Layer); /** * @return {boolean} Declutter. */ ol.layer.Vector.prototype.getDeclutter = function() { return this.declutter_; }; /** * @param {boolean} declutter Declutter. */ ol.layer.Vector.prototype.setDeclutter = function(declutter) { this.declutter_ = declutter; }; /** * @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 {ol.RenderOrderFunction|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 */ 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 */ ol.layer.Vector.prototype.getStyle = function() { return this.style_; }; /** * Get the style function. * @return {ol.StyleFunction|undefined} Layer style function. * @api */ 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 {ol.RenderOrderFunction|null|undefined} renderOrder * Render order. */ ol.layer.Vector.prototype.setRenderOrder = function(renderOrder) { 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 */ 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(); }; /** * @return {ol.layer.VectorRenderType|string} The render mode. */ ol.layer.Vector.prototype.getRenderMode = function() { return this.renderMode_; }; /** * @enum {string} * @private */ ol.layer.Vector.Property_ = { RENDER_ORDER: 'renderOrder' }; 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; }); }; 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 * @abstract * @extends {ol.Object} * @param {ol.SourceSourceOptions} options Source options. * @api */ 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_ = null; /** * @private * @type {?ol.Attribution2} */ this.attributions2_ = this.adaptAttributions_(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 the attributions option into an attributions function. * @suppress {deprecated} * @param {ol.AttributionLike|undefined} attributionLike The attribution option. * @return {?ol.Attribution2} An attribution function (or null). */ ol.source.Source.prototype.adaptAttributions_ = function(attributionLike) { if (!attributionLike) { return null; } if (attributionLike instanceof ol.Attribution) { // TODO: remove attributions_ in next major release this.attributions_ = [attributionLike]; return function(frameState) { return [attributionLike.getHTML()]; }; } if (Array.isArray(attributionLike)) { if (attributionLike[0] instanceof ol.Attribution) { // TODO: remove attributions_ in next major release this.attributions_ = attributionLike; var attributions = attributionLike.map(function(attribution) { return attribution.getHTML(); }); return function(frameState) { return attributions; }; } // TODO: remove attributions_ in next major release this.attributions_ = attributionLike.map(function(attribution) { return new ol.Attribution({html: attribution}); }); return function(frameState) { return attributionLike; }; } if (typeof attributionLike === 'function') { return attributionLike; } // TODO: remove attributions_ in next major release this.attributions_ = [ new ol.Attribution({html: attributionLike}) ]; return function(frameState) { return [attributionLike]; }; }; /** * @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 */ ol.source.Source.prototype.getAttributions = function() { return this.attributions_; }; /** * Get the attribution function for the source. * @return {?ol.Attribution2} Attribution function. */ ol.source.Source.prototype.getAttributions2 = function() { return this.attributions2_; }; /** * Get the logo of the source. * @return {string|olx.LogoOptions|undefined} Logo. * @api */ 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.Attribution2}`, * or `undefined`. * @api */ ol.source.Source.prototype.setAttributions = function(attributions) { this.attributions2_ = this.adaptAttributions_(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.VectorEventType'); /** * @enum {string} */ ol.source.VectorEventType = { /** * Triggered when a feature is added to the source. * @event ol.source.Vector.Event#addfeature * @api */ 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 */ REMOVEFEATURE: 'removefeature' }; 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_ = {}; }; /** * Insert a value into the RBush. * @param {ol.Extent} extent Extent. * @param {T} value Value. */ ol.structs.RBush.prototype.insert = function(extent, value) { /** @type {ol.RBushEntry} */ var item = { minX: extent[0], minY: extent[1], maxX: extent[2], maxY: extent[3], value: value }; this.rbush_.insert(item); 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) { 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; 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) { var uid = ol.getUid(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) { var item = this.items_[ol.getUid(value)]; var bbox = [item.minX, item.minY, item.maxX, item.maxY]; if (!ol.extent.equals(bbox, extent)) { 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) { 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) { 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 ol.extent.createOrUpdate(data.minX, data.minY, data.maxX, data.maxY, opt_extent); }; /** * @param {ol.structs.RBush} rbush R-Tree. */ ol.structs.RBush.prototype.concat = function(rbush) { this.rbush_.load(rbush.rbush_.all()); for (var i in rbush.items_) { this.items_[i | 0] = rbush.items_[i | 0]; } }; // 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.CollectionEventType'); goog.require('ol.ObjectEventType'); 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.source.VectorEventType'); 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 */ 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. A feature will not be added to the source if feature with * the same id is already there. The reason for this behavior is to avoid * feature duplication when using bbox or tile loading strategies. * @param {ol.Feature} feature Feature to add. * @api */ 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.VectorEventType.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) { this.featureChangeKeys_[featureKey] = [ ol.events.listen(feature, ol.events.EventType.CHANGE, this.handleFeatureChange_, this), ol.events.listen(feature, ol.ObjectEventType.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 */ 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.VectorEventType.ADDFEATURE, newFeatures[i])); } }; /** * @param {!ol.Collection.<ol.Feature>} collection Collection. * @private */ ol.source.Vector.prototype.bindFeaturesCollection_ = function(collection) { var modifyingCollection = false; ol.events.listen(this, ol.source.VectorEventType.ADDFEATURE, function(evt) { if (!modifyingCollection) { modifyingCollection = true; collection.push(evt.feature); modifyingCollection = false; } }); ol.events.listen(this, ol.source.VectorEventType.REMOVEFEATURE, function(evt) { if (!modifyingCollection) { modifyingCollection = true; collection.remove(evt.feature); modifyingCollection = false; } }); ol.events.listen(collection, ol.CollectionEventType.ADD, function(evt) { if (!modifyingCollection) { modifyingCollection = true; this.addFeature(/** @type {ol.Feature} */ (evt.element)); modifyingCollection = false; } }, this); ol.events.listen(collection, ol.CollectionEventType.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 */ 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(); } if (this.featuresRtree_) { this.featuresRtree_.clear(); } this.loadedExtentsRtree_.clear(); this.nullGeometryFeatures_ = {}; var clearEvent = new ol.source.Vector.Event(ol.source.VectorEventType.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 */ 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(); 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(); 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 */ 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 */ 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) { 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 */ 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]; 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(); 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`. * @param {ol.Extent=} opt_extent Destination extent. If provided, no new extent * will be created. Instead, that extent's coordinates will be overwritten. * @return {ol.Extent} Extent. * @api */ ol.source.Vector.prototype.getExtent = function(opt_extent) { return this.featuresRtree_.getExtent(opt_extent); }; /** * 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 */ 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_; }; /** * @override */ ol.source.Vector.prototype.getResolutions = function() {}; /** * 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(); 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) { this.removeFromIdIndex_(feature); this.idIndex_[sid] = feature; } } } else { if (!(featureKey in this.undefIdIndex_)) { this.removeFromIdIndex_(feature); this.undefIdIndex_[featureKey] = feature; } } this.changed(); this.dispatchEvent(new ol.source.Vector.Event( ol.source.VectorEventType.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 an extent from the list of loaded extents. * @param {ol.Extent} extent Extent. * @api */ ol.source.Vector.prototype.removeLoadedExtent = function(extent) { var loadedExtentsRtree = this.loadedExtentsRtree_; var obj; loadedExtentsRtree.forEachInExtent(extent, function(object) { if (ol.extent.equals(object.extent, extent)) { obj = object; return true; } }); if (obj) { loadedExtentsRtree.remove(obj); } }; /** * 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 */ 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(); 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.VectorEventType.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; }; /** * Set the new loader of the source. The next loadFeatures call will use the * new loader. * @param {ol.FeatureLoader} loader The loader to set. * @api */ ol.source.Vector.prototype.setLoader = function(loader) { this.loader_ = loader; }; /** * @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 */ this.feature = opt_feature; }; ol.inherits(ol.source.Vector.Event, ol.events.Event); goog.provide('ol.interaction.Draw'); goog.require('ol'); goog.require('ol.Feature'); goog.require('ol.MapBrowserEventType'); goog.require('ol.Object'); goog.require('ol.coordinate'); goog.require('ol.events'); goog.require('ol.events.Event'); goog.require('ol.events.condition'); goog.require('ol.extent'); goog.require('ol.functions'); 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.DrawEventType'); goog.require('ol.interaction.Pointer'); goog.require('ol.interaction.Property'); 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 */ 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 {boolean} * @private */ this.shouldHandle_ = false; /** * @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_ = /** @type {ol.geom.GeometryType} */ (options.type); /** * Drawing mode (derived from geometry type. * @type {ol.interaction.Draw.Mode_} * @private */ this.mode_ = ol.interaction.Draw.getMode_(this.type_); /** * Stop click, singleclick, and doubleclick events from firing during drawing. * Default is `false`. * @type {boolean} * @private */ this.stopClick_ = !!options.stopClick; /** * 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 permissible * @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 {!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 {!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) { if (coordinates[0].length) { // Add a closing coordinate to match the first geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])]); } else { geometry.setCoordinates([]); } } 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.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 = true; if (this.freehand_ && event.type === ol.MapBrowserEventType.POINTERDRAG && this.sketchFeature_ !== null) { this.addToDrawing_(event); pass = false; } else if (this.freehand_ && event.type === ol.MapBrowserEventType.POINTERDOWN) { pass = false; } else if (event.type === ol.MapBrowserEventType.POINTERMOVE) { pass = this.handlePointerMove_(event); } else if (event.type === ol.MapBrowserEventType.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) { this.shouldHandle_ = !this.freehand_; 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 pass = true; this.handlePointerMove_(event); var circleMode = this.mode_ === ol.interaction.Draw.Mode_.CIRCLE; if (this.shouldHandle_) { 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 (this.freehand_) { this.finishCoordinate_ = null; this.abortDrawing_(); } if (!pass && this.stopClick_) { event.stopPropagation(); } 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.downPx_ && ((!this.freehand_ && this.shouldHandle_) || (this.freehand_ && !this.shouldHandle_))) { 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; this.shouldHandle_ = this.freehand_ ? squaredDistance > this.squaredClickTolerance_ : squaredDistance <= this.squaredClickTolerance_; } 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_); 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.DrawEventType.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]; this.geometryFunction_(/** @type {!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() { if (!this.sketchFeature_) { return; } 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); if (coordinates.length >= 2) { this.finishCoordinate_ = coordinates[coordinates.length - 2].slice(); } } 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.DrawEventType.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.DrawEventType.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(); 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.DrawEventType.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 {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} * @private */ 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.DrawEventType} 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 */ this.feature = feature; }; ol.inherits(ol.interaction.Draw.Event, ol.events.Event); goog.provide('ol.interaction.ExtentEventType'); /** * @enum {string} */ ol.interaction.ExtentEventType = { /** * Triggered after the extent is changed * @event ol.interaction.Extent.Event#extentchanged * @api */ EXTENTCHANGED: 'extentchanged' }; goog.provide('ol.interaction.Extent'); goog.require('ol'); goog.require('ol.Feature'); goog.require('ol.MapBrowserEventType'); 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.ExtentEventType'); 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) { var options = 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_ = options.pixelTolerance !== undefined ? options.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 = {}; } /* 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 }); if (opt_options.extent) { this.setExtent(opt_options.extent); } }; 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.MapBrowserEventType.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.PluggableMap} 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 (ol.coordinate.distance(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 * @implements {oli.ExtentEvent} * @param {ol.Extent} extent the new extent * @extends {ol.events.Event} */ ol.interaction.Extent.Event = function(extent) { ol.events.Event.call(this, ol.interaction.ExtentEventType.EXTENTCHANGED); /** * The current extent. * @type {ol.Extent} * @api */ this.extent = extent; }; ol.inherits(ol.interaction.Extent.Event, ol.events.Event); goog.provide('ol.interaction.ModifyEventType'); /** * @enum {string} */ ol.interaction.ModifyEventType = { /** * 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.Modify'); goog.require('ol'); goog.require('ol.Collection'); goog.require('ol.CollectionEventType'); goog.require('ol.Feature'); goog.require('ol.MapBrowserEventType'); goog.require('ol.MapBrowserPointerEvent'); 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.ModifyEventType'); goog.require('ol.interaction.Pointer'); goog.require('ol.layer.Vector'); goog.require('ol.source.Vector'); goog.require('ol.source.VectorEventType'); goog.require('ol.structs.RBush'); goog.require('ol.style.Style'); /** * @classdesc * Interaction for modifying feature geometries. To modify features that have * been added to an existing source, construct the modify interaction with the * `source` option. If you want to modify features in a collection (for example, * the collection used by a select interaction), construct the interaction with * the `features` option. The interaction must be constructed with either a * `source` or `features` option. * * By default, the interaction will allow deletion of vertices when the `alt` * key is pressed. To configure the interaction with a different condition * for deletion, use the `deleteCondition` option. * * @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.altKeyOnly(mapBrowserEvent) && ol.events.condition.singleClick(mapBrowserEvent); }; /** * @type {ol.EventsConditionType} * @private */ this.deleteCondition_ = options.deleteCondition ? options.deleteCondition : this.defaultDeleteCondition_; /** * @type {ol.EventsConditionType} * @private */ this.insertVertexCondition_ = options.insertVertexCondition ? options.insertVertexCondition : ol.events.condition.always; /** * 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_, 'Circle': this.writeCircleGeometry_, 'GeometryCollection': this.writeGeometryCollectionGeometry_ }; /** * @type {ol.source.Vector} * @private */ this.source_ = null; var features; if (options.source) { this.source_ = options.source; features = new ol.Collection(this.source_.getFeatures()); ol.events.listen(this.source_, ol.source.VectorEventType.ADDFEATURE, this.handleSourceAdd_, this); ol.events.listen(this.source_, ol.source.VectorEventType.REMOVEFEATURE, this.handleSourceRemove_, this); } else { features = options.features; } if (!features) { throw new Error('The modify interaction requires features or a source'); } /** * @type {ol.Collection.<ol.Feature>} * @private */ this.features_ = features; this.features_.forEach(this.addFeature_, this); ol.events.listen(this.features_, ol.CollectionEventType.ADD, this.handleFeatureAdd_, this); ol.events.listen(this.features_, ol.CollectionEventType.REMOVE, this.handleFeatureRemove_, this); /** * @type {ol.MapBrowserPointerEvent} * @private */ this.lastPointerEvent_ = null; }; ol.inherits(ol.interaction.Modify, ol.interaction.Pointer); /** * @define {number} The segment index assigned to a circle's center when * breaking up a cicrle into ModifySegmentDataType segments. */ ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX = 0; /** * @define {number} The segment index assigned to a circle's circumference when * breaking up a circle into ModifySegmentDataType segments. */ ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX = 1; /** * @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.getActive()) { 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.ModifyEventType.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.source.Vector.Event} event Event. * @private */ ol.interaction.Modify.prototype.handleSourceAdd_ = function(event) { if (event.feature) { this.features_.push(event.feature); } }; /** * @param {ol.source.Vector.Event} event Event. * @private */ ol.interaction.Modify.prototype.handleSourceRemove_ = function(event) { if (event.feature) { this.features_.remove(event.feature); } }; /** * @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); } } } }; /** * We convert a circle into two segments. The segment at index * {@link ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX} is the * circle's center (a point). The segment at index * {@link ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX} is * the circumference, and is not a line segment. * * @param {ol.Feature} feature Feature. * @param {ol.geom.Circle} geometry Geometry. * @private */ ol.interaction.Modify.prototype.writeCircleGeometry_ = function(feature, geometry) { var coordinates = geometry.getCenter(); var centerSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ feature: feature, geometry: geometry, index: ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX, segment: [coordinates, coordinates] }); var circumferenceSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ feature: feature, geometry: geometry, index: ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX, segment: [coordinates, coordinates] }); var featureSegments = [centerSegmentData, circumferenceSegmentData]; centerSegmentData.featureSegments = circumferenceSegmentData.featureSegments = featureSegments; this.rBush_.insert(ol.extent.createOrUpdateFromCoordinate(coordinates), centerSegmentData); this.rBush_.insert(geometry.getExtent(), circumferenceSegmentData); }; /** * @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); var pixelCoordinate = evt.map.getCoordinateFromPixel(evt.pixel); 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 (segmentDataMatch.geometry.getType() === ol.geom.GeometryType.CIRCLE && segmentDataMatch.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { var closestVertex = ol.interaction.Modify.closestOnSegmentData_(pixelCoordinate, segmentDataMatch); if (ol.coordinate.equals(closestVertex, vertex) && !componentSegments[uid][0]) { this.dragSegments_.push([segmentDataMatch, 0]); componentSegments[uid][0] = segmentDataMatch; } } else 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 (this.insertVertexCondition_(evt) && 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; 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 = geometry.getCoordinates(); coordinates[segmentData.index] = vertex; segment[0] = segment[1] = vertex; break; case ol.geom.GeometryType.LINE_STRING: coordinates = geometry.getCoordinates(); coordinates[segmentData.index + index] = vertex; segment[index] = vertex; break; case ol.geom.GeometryType.MULTI_LINE_STRING: coordinates = geometry.getCoordinates(); coordinates[depth[0]][segmentData.index + index] = vertex; segment[index] = vertex; break; case ol.geom.GeometryType.POLYGON: coordinates = geometry.getCoordinates(); coordinates[depth[0]][segmentData.index + index] = vertex; segment[index] = vertex; break; case ol.geom.GeometryType.MULTI_POLYGON: coordinates = geometry.getCoordinates(); coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex; segment[index] = vertex; break; case ol.geom.GeometryType.CIRCLE: segment[0] = segment[1] = vertex; if (segmentData.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX) { this.changingFeature_ = true; geometry.setCenter(vertex); this.changingFeature_ = false; } else { // We're dragging the circle's circumference: this.changingFeature_ = true; geometry.setRadius(ol.coordinate.distance(geometry.getCenter(), vertex)); this.changingFeature_ = false; } break; default: // pass } if (coordinates) { 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; var geometry; for (var i = this.dragSegments_.length - 1; i >= 0; --i) { segmentData = this.dragSegments_[i][0]; geometry = segmentData.geometry; if (geometry.getType() === ol.geom.GeometryType.CIRCLE) { // Update a circle object in the R* bush: var coordinates = geometry.getCenter(); var centerSegmentData = segmentData.featureSegments[0]; var circumferenceSegmentData = segmentData.featureSegments[1]; centerSegmentData.segment[0] = centerSegmentData.segment[1] = coordinates; circumferenceSegmentData.segment[0] = circumferenceSegmentData.segment[1] = coordinates; this.rBush_.update(ol.extent.createOrUpdateFromCoordinate(coordinates), centerSegmentData); this.rBush_.update(geometry.getExtent(), circumferenceSegmentData); } else { this.rBush_.update(ol.extent.boundingExtent(segmentData.segment), segmentData); } } if (this.modified_) { this.dispatchEvent(new ol.interaction.Modify.Event( ol.interaction.ModifyEventType.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().getInteracting() && mapBrowserEvent.type == ol.MapBrowserEventType.POINTERMOVE && !this.handlingDownUpSequence) { this.handlePointerMove_(mapBrowserEvent); } if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) { if (mapBrowserEvent.type != ol.MapBrowserEventType.SINGLECLICK || !this.ignoreNextSingleClick_) { handled = this.removePoint(); } else { handled = true; } } if (mapBrowserEvent.type == ol.MapBrowserEventType.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.PluggableMap} map Map. * @private */ ol.interaction.Modify.prototype.handlePointerAtPixel_ = function(pixel, map) { var pixelCoordinate = map.getCoordinateFromPixel(pixel); var sortByDistance = function(a, b) { return ol.interaction.Modify.pointDistanceToSegmentDataSquared_(pixelCoordinate, a) - ol.interaction.Modify.pointDistanceToSegmentDataSquared_(pixelCoordinate, b); }; 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.interaction.Modify.closestOnSegmentData_(pixelCoordinate, node); var vertexPixel = map.getPixelFromCoordinate(vertex); var dist = ol.coordinate.distance(pixel, vertexPixel); if (dist <= this.pixelTolerance_) { var vertexSegments = {}; if (node.geometry.getType() === ol.geom.GeometryType.CIRCLE && node.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { this.snappedToVertex_ = true; this.createOrUpdateVertexFeature_(vertex); } else { 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); 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 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; } } } vertexSegments[ol.getUid(closestSegment)] = true; this.vertexSegments_ = vertexSegments; return; } } if (this.vertexFeature_) { this.overlay_.getSource().removeFeature(this.vertexFeature_); this.vertexFeature_ = null; } }; /** * Returns the distance from a point to a line segment. * * @param {ol.Coordinate} pointCoordinates The coordinates of the point from * which to calculate the distance. * @param {ol.ModifySegmentDataType} segmentData The object describing the line * segment we are calculating the distance to. * @return {number} The square of the distance between a point and a line segment. */ ol.interaction.Modify.pointDistanceToSegmentDataSquared_ = function(pointCoordinates, segmentData) { var geometry = segmentData.geometry; if (geometry.getType() === ol.geom.GeometryType.CIRCLE) { var circleGeometry = /** @type {ol.geom.Circle} */ (geometry); if (segmentData.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { var distanceToCenterSquared = ol.coordinate.squaredDistance(circleGeometry.getCenter(), pointCoordinates); var distanceToCircumference = Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius(); return distanceToCircumference * distanceToCircumference; } } return ol.coordinate.squaredDistanceToSegment(pointCoordinates, segmentData.segment); }; /** * Returns the point closest to a given line segment. * * @param {ol.Coordinate} pointCoordinates The point to which a closest point * should be found. * @param {ol.ModifySegmentDataType} segmentData The object describing the line * segment which should contain the closest point. * @return {ol.Coordinate} The point closest to the specified line segment. */ ol.interaction.Modify.closestOnSegmentData_ = function(pointCoordinates, segmentData) { var geometry = segmentData.geometry; if (geometry.getType() === ol.geom.GeometryType.CIRCLE && segmentData.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { return geometry.getClosestPoint(pointCoordinates); } return ol.coordinate.closestOnSegment(pointCoordinates, segmentData.segment); }; /** * @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.MapBrowserEventType.POINTERDRAG) { var evt = this.lastPointerEvent_; this.willModifyFeatures_(evt); this.removeVertex_(); this.dispatchEvent(new ol.interaction.Modify.Event( ol.interaction.ModifyEventType.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) { 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; } dragSegments.length = 0; } } 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.ModifyEventType} 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); goog.provide('ol.interaction.Select'); goog.require('ol'); goog.require('ol.CollectionEventType'); goog.require('ol.array'); goog.require('ol.events'); goog.require('ol.events.Event'); goog.require('ol.events.condition'); goog.require('ol.functions'); 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 */ 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.CollectionEventType.ADD, this.addFeature_, this); ol.events.listen(features, ol.CollectionEventType.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 */ 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.PluggableMap} map Map. * @override * @api */ 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} * @private */ 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.CollectionEventType'); goog.require('ol.coordinate'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.extent'); goog.require('ol.functions'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Polygon'); goog.require('ol.interaction.Pointer'); goog.require('ol.obj'); goog.require('ol.source.Vector'); goog.require('ol.source.VectorEventType'); 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.featureChangeListenerKeys_ = {}; /** * 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_, 'Circle': this.writeCircleGeometry_ }; }; 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 feature 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.featureChangeListenerKeys_[feature_uid] = ol.events.listen( feature, ol.events.EventType.CHANGE, this.handleFeatureChange_, 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.handleFeatureChange_ = function(evt) { var feature = /** @type {ol.Feature} */ (evt.target); 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 feature 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.events.unlistenByKey(this.featureChangeListenerKeys_[feature_uid]); delete this.featureChangeListenerKeys_[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.events.unlistenByKey); 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.CollectionEventType.ADD, this.handleFeatureAdd_, this), ol.events.listen(this.features_, ol.CollectionEventType.REMOVE, this.handleFeatureRemove_, this) ); } else if (this.source_) { keys.push( ol.events.listen(this.source_, ol.source.VectorEventType.ADDFEATURE, this.handleFeatureAdd_, this), ol.events.listen(this.source_, ol.source.VectorEventType.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.PluggableMap} 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); // If snapping on vertices only, don't consider circles if (this.vertex_ && !this.edge_) { segments = segments.filter(function(segment) { return segment.feature.getGeometry().getType() !== ol.geom.GeometryType.CIRCLE; }); } 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; var isCircle = segments[0].feature.getGeometry().getType() === ol.geom.GeometryType.CIRCLE; 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_) { if (isCircle) { vertex = ol.coordinate.closestOnCircle(pixelCoordinate, /** @type {ol.geom.Circle} */ (segments[0].feature.getGeometry())); } else { vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, closestSegment)); } vertexPixel = map.getPixelFromCoordinate(vertex); if (ol.coordinate.distance(pixel, vertexPixel) <= this.pixelTolerance_) { snapped = true; if (this.vertex_ && !isCircle) { 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.Circle} geometry Geometry. * @private */ ol.interaction.Snap.prototype.writeCircleGeometry_ = function(feature, geometry) { var polygon = ol.geom.Polygon.fromCircle(geometry); var coordinates = polygon.getCoordinates()[0]; 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.GeometryCollection} geometry Geometry. * @private */ ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { var i, geometries = geometry.getGeometriesArray(); for (i = 0; i < geometries.length; ++i) { var segmentWriter = this.SEGMENT_WRITERS_[geometries[i].getType()]; if (segmentWriter) { segmentWriter.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.TranslateEventType'); /** * @enum {string} */ ol.interaction.TranslateEventType = { /** * 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.interaction.Translate'); goog.require('ol'); goog.require('ol.Collection'); goog.require('ol.Object'); goog.require('ol.events'); goog.require('ol.events.Event'); goog.require('ol.functions'); goog.require('ol.array'); goog.require('ol.interaction.Pointer'); goog.require('ol.interaction.Property'); goog.require('ol.interaction.TranslateEventType'); /** * @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 : {}; /** * 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.events.listen(this, ol.Object.getChangeEventType(ol.interaction.Property.ACTIVE), this.handleActiveChanged_, this); }; 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.TranslateEventType.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.TranslateEventType.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.TranslateEventType.TRANSLATING, features, newCoordinate)); } }; /** * @param {ol.MapBrowserEvent} event Event. * @this {ol.interaction.Translate} * @private */ ol.interaction.Translate.handleMoveEvent_ = function(event) { var elem = event.map.getViewport(); // Change the cursor to grab/grabbing if hovering any of the features managed // by the interaction if (this.featuresAtPixel_(event.pixel, event.map)) { elem.classList.remove(this.lastCoordinate_ ? 'ol-grab' : 'ol-grabbing'); elem.classList.add(this.lastCoordinate_ ? 'ol-grabbing' : 'ol-grab'); } else { elem.classList.remove('ol-grab', 'ol-grabbing'); } }; /** * 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.PluggableMap} 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; }; /** * @inheritDoc */ ol.interaction.Translate.prototype.setMap = function(map) { var oldMap = this.getMap(); ol.interaction.Pointer.prototype.setMap.call(this, map); this.updateState_(oldMap); }; /** * @private */ ol.interaction.Translate.prototype.handleActiveChanged_ = function() { this.updateState_(null); }; /** * @param {ol.PluggableMap} oldMap Old map. * @private */ ol.interaction.Translate.prototype.updateState_ = function(oldMap) { var map = this.getMap(); var active = this.getActive(); if (!map || !active) { map = map || oldMap; if (map) { var elem = map.getViewport(); elem.classList.remove('ol-grab', 'ol-grabbing'); } } }; /** * @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.TranslateEventType} 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); 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.EventType'); 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; } this.setStyle(function(feature, resolution) { 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.EventType.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(); 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) { 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} * @private */ ol.layer.Heatmap.Property_ = { BLUR: 'blur', GRADIENT: 'gradient', RADIUS: 'radius' }; goog.provide('ol.layer.Image'); goog.require('ol'); goog.require('ol.LayerType'); 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 */ ol.layer.Image = function(opt_options) { var options = opt_options ? opt_options : {}; ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (options)); /** * The layer type. * @protected * @type {ol.LayerType} */ this.type = ol.LayerType.IMAGE; }; 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 */ ol.layer.Image.prototype.getSource; goog.provide('ol.layer.TileProperty'); /** * @enum {string} */ ol.layer.TileProperty = { PRELOAD: 'preload', USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError' }; goog.provide('ol.layer.Tile'); goog.require('ol'); goog.require('ol.LayerType'); goog.require('ol.layer.Layer'); goog.require('ol.layer.TileProperty'); 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 */ 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); /** * The layer type. * @protected * @type {ol.LayerType} */ this.type = ol.LayerType.TILE; }; 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.TileProperty.PRELOAD)); }; /** * Return the associated {@link ol.source.Tile tilesource} of the layer. * @function * @return {ol.source.Tile} Source. * @api */ 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.TileProperty.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.TileProperty.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.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); }; goog.provide('ol.layer.VectorTile'); goog.require('ol'); goog.require('ol.LayerType'); goog.require('ol.asserts'); goog.require('ol.layer.TileProperty'); goog.require('ol.layer.Vector'); goog.require('ol.layer.VectorTileRenderType'); 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 renderMode = options.renderMode || ol.layer.VectorTileRenderType.HYBRID; ol.asserts.assert(renderMode == undefined || renderMode == ol.layer.VectorTileRenderType.IMAGE || renderMode == ol.layer.VectorTileRenderType.HYBRID || renderMode == ol.layer.VectorTileRenderType.VECTOR, 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` if (options.declutter && renderMode == ol.layer.VectorTileRenderType.IMAGE) { renderMode = ol.layer.VectorTileRenderType.HYBRID; } options.renderMode = renderMode; 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); /** * The layer type. * @protected * @type {ol.LayerType} */ this.type = ol.LayerType.VECTOR_TILE; }; 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.TileProperty.PRELOAD)); }; /** * 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.TileProperty.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.TileProperty.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.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); }; /** * Return the associated {@link ol.source.VectorTile vectortilesource} of the layer. * @function * @return {ol.source.VectorTile} Source. * @api */ ol.layer.VectorTile.prototype.getSource; goog.provide('ol.webgl.Shader'); goog.require('ol.functions'); /** * @constructor * @abstract * @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'); ol.render.webgl.circlereplay.defaultshader.fragment = new ol.webgl.Fragment(ol.DEBUG_WEBGL ? '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' : '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;}}'); ol.render.webgl.circlereplay.defaultshader.vertex = new ol.webgl.Vertex(ol.DEBUG_WEBGL ? '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' : '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;//Until we get gl_VertexID in WebGL,we store an instruction.if(f==0.0){//Offsetting the edges of the triangle by lineWidth/2 is necessary,however//we should also leave some space for the antialiasing,thus we offset by lineWidth.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);}}'); // This file is automatically generated, do not edit goog.provide('ol.render.webgl.circlereplay.defaultshader.Locations'); goog.require('ol'); /** * @constructor * @param {WebGLRenderingContext} gl GL. * @param {WebGLProgram} program Program. * @struct */ ol.render.webgl.circlereplay.defaultshader.Locations = function(gl, program) { /** * @type {WebGLUniformLocation} */ this.u_projectionMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); /** * @type {WebGLUniformLocation} */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); /** * @type {WebGLUniformLocation} */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); /** * @type {WebGLUniformLocation} */ this.u_lineWidth = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_lineWidth' : 'k'); /** * @type {WebGLUniformLocation} */ this.u_pixelRatio = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_pixelRatio' : 'l'); /** * @type {WebGLUniformLocation} */ this.u_opacity = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_opacity' : 'm'); /** * @type {WebGLUniformLocation} */ this.u_fillColor = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_fillColor' : 'n'); /** * @type {WebGLUniformLocation} */ this.u_strokeColor = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_strokeColor' : 'o'); /** * @type {WebGLUniformLocation} */ this.u_size = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_size' : 'p'); /** * @type {number} */ this.a_position = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_position' : 'e'); /** * @type {number} */ this.a_instruction = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_instruction' : 'f'); /** * @type {number} */ this.a_radius = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? '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 * @abstract * @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.linestringreplay.defaultshader.Locations| ol.render.webgl.polygonreplay.defaultshader.Locations| ol.render.webgl.texturereplay.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.linestringreplay.defaultshader.Locations| ol.render.webgl.polygonreplay.defaultshader.Locations| ol.render.webgl.texturereplay.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); } context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.verticesBuffer); 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.render.webgl'); /** * @const * @type {string} */ ol.render.webgl.defaultFont = '10px sans-serif'; /** * @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 {number} */ ol.render.webgl.defaultLineDashOffset = 0; /** * @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.defaultTextAlign = 0.5; /** * @const * @type {number} */ ol.render.webgl.defaultTextBaseline = 0.5; /** * @const * @type {number} */ ol.render.webgl.defaultLineWidth = 1; /** * 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.Buffer'); 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} * @private */ 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.circlereplay.defaultshader.Locations'); 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>, * lineDashOffset: (number|undefined), * lineWidth: (number|undefined), * changed: boolean}|null} */ this.state_ = { fillColor: null, strokeColor: null, lineDash: null, lineDashOffset: undefined, 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. 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 { //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) { 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) { 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) { var strokeStyleColor, strokeStyleWidth; if (strokeStyle) { var strokeStyleLineDash = strokeStyle.getLineDash(); this.state_.lineDash = strokeStyleLineDash ? strokeStyleLineDash : ol.render.webgl.defaultLineDash; var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); this.state_.lineDashOffset = strokeStyleLineDashOffset ? strokeStyleLineDashOffset : ol.render.webgl.defaultLineDashOffset; 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.texturereplay.defaultshader'); goog.require('ol'); goog.require('ol.webgl.Fragment'); goog.require('ol.webgl.Vertex'); ol.render.webgl.texturereplay.defaultshader.fragment = new ol.webgl.Fragment(ol.DEBUG_WEBGL ? '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' : '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;}'); ol.render.webgl.texturereplay.defaultshader.vertex = new ol.webgl.Vertex(ol.DEBUG_WEBGL ? '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' : '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;}'); // This file is automatically generated, do not edit goog.provide('ol.render.webgl.texturereplay.defaultshader.Locations'); goog.require('ol'); /** * @constructor * @param {WebGLRenderingContext} gl GL. * @param {WebGLProgram} program Program. * @struct */ ol.render.webgl.texturereplay.defaultshader.Locations = function(gl, program) { /** * @type {WebGLUniformLocation} */ this.u_projectionMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); /** * @type {WebGLUniformLocation} */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); /** * @type {WebGLUniformLocation} */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); /** * @type {WebGLUniformLocation} */ this.u_opacity = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_opacity' : 'k'); /** * @type {WebGLUniformLocation} */ this.u_image = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_image' : 'l'); /** * @type {number} */ this.a_position = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_position' : 'c'); /** * @type {number} */ this.a_texCoord = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'd'); /** * @type {number} */ this.a_offsets = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_offsets' : 'e'); /** * @type {number} */ this.a_opacity = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_opacity' : 'f'); /** * @type {number} */ this.a_rotateWithView = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); }; 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) { gl.getExtension('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); 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)); 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); 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); 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.TextureReplay'); goog.require('ol'); goog.require('ol.extent'); goog.require('ol.obj'); goog.require('ol.render.webgl.texturereplay.defaultshader'); goog.require('ol.render.webgl.texturereplay.defaultshader.Locations'); goog.require('ol.render.webgl.Replay'); goog.require('ol.webgl'); goog.require('ol.webgl.Context'); /** * @constructor * @abstract * @extends {ol.render.webgl.Replay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Max extent. * @struct */ ol.render.webgl.TextureReplay = function(tolerance, maxExtent) { ol.render.webgl.Replay.call(this, tolerance, maxExtent); /** * @type {number|undefined} * @protected */ this.anchorX = undefined; /** * @type {number|undefined} * @protected */ this.anchorY = undefined; /** * @type {Array.<number>} * @protected */ this.groupIndices = []; /** * @type {Array.<number>} * @protected */ this.hitDetectionGroupIndices = []; /** * @type {number|undefined} * @protected */ this.height = undefined; /** * @type {number|undefined} * @protected */ this.imageHeight = undefined; /** * @type {number|undefined} * @protected */ this.imageWidth = undefined; /** * @protected * @type {ol.render.webgl.texturereplay.defaultshader.Locations} */ this.defaultLocations = null; /** * @protected * @type {number|undefined} */ this.opacity = undefined; /** * @type {number|undefined} * @protected */ this.originX = undefined; /** * @type {number|undefined} * @protected */ this.originY = undefined; /** * @protected * @type {boolean|undefined} */ this.rotateWithView = undefined; /** * @protected * @type {number|undefined} */ this.rotation = undefined; /** * @protected * @type {number|undefined} */ this.scale = undefined; /** * @type {number|undefined} * @protected */ this.width = undefined; }; ol.inherits(ol.render.webgl.TextureReplay, ol.render.webgl.Replay); /** * @inheritDoc */ ol.render.webgl.TextureReplay.prototype.getDeleteResourcesFunction = function(context) { var verticesBuffer = this.verticesBuffer; var indicesBuffer = this.indicesBuffer; var textures = this.getTextures(true); 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]); } } 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. * @protected */ ol.render.webgl.TextureReplay.prototype.drawCoordinates = function(flatCoordinates, offset, end, stride) { 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; }; /** * @protected * @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.TextureReplay.prototype.createTextures = function(textures, images, texturePerImage, gl) { 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.TextureReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { // get the program var fragmentShader = ol.render.webgl.texturereplay.defaultshader.fragment; var vertexShader = ol.render.webgl.texturereplay.defaultshader.vertex; var program = context.getProgram(fragmentShader, vertexShader); // get the locations var locations; if (!this.defaultLocations) { locations = new ol.render.webgl.texturereplay.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.TextureReplay.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.TextureReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { var textures = hitDetection ? this.getHitDetectionTextures() : this.getTextures(); var groupIndices = hitDetection ? this.hitDetectionGroupIndices : this.groupIndices; 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 * * @protected * @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.TextureReplay.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.TextureReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) { var i, groupStart, start, end, feature, featureUid; var featureIndex = this.startIndices.length - 1; var hitDetectionTextures = this.getHitDetectionTextures(); for (i = hitDetectionTextures.length - 1; i >= 0; --i) { gl.bindTexture(ol.webgl.TEXTURE_2D, 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.TextureReplay.prototype.finish = function(context) { this.anchorX = undefined; this.anchorY = undefined; this.height = undefined; 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; }; /** * @abstract * @protected * @param {boolean=} opt_all Return hit detection textures with regular ones. * @returns {Array.<WebGLTexture>} Textures. */ ol.render.webgl.TextureReplay.prototype.getTextures = function(opt_all) {}; /** * @abstract * @protected * @returns {Array.<WebGLTexture>} Textures. */ ol.render.webgl.TextureReplay.prototype.getHitDetectionTextures = function() {}; goog.provide('ol.render.webgl.ImageReplay'); goog.require('ol'); goog.require('ol.render.webgl.TextureReplay'); goog.require('ol.webgl.Buffer'); /** * @constructor * @extends {ol.render.webgl.TextureReplay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Max extent. * @struct */ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { ol.render.webgl.TextureReplay.call(this, tolerance, maxExtent); /** * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} * @protected */ this.images_ = []; /** * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} * @protected */ this.hitDetectionImages_ = []; /** * @type {Array.<WebGLTexture>} * @private */ this.textures_ = []; /** * @type {Array.<WebGLTexture>} * @private */ this.hitDetectionTextures_ = []; }; ol.inherits(ol.render.webgl.ImageReplay, ol.render.webgl.TextureReplay); /** * @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); this.hitDetectionGroupIndices.push(this.indices.length); // create, bind, and populate the vertices buffer this.verticesBuffer = new ol.webgl.Buffer(this.vertices); var indices = this.indices; // 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); this.createTextures(this.hitDetectionTextures_, this.hitDetectionImages_, texturePerImage, gl); this.images_ = null; this.hitDetectionImages_ = null; ol.render.webgl.TextureReplay.prototype.finish.call(this, context); }; /** * @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 opacity = imageStyle.getOpacity(); var origin = imageStyle.getOrigin(); var rotateWithView = imageStyle.getRotateWithView(); var rotation = imageStyle.getRotation(); var size = imageStyle.getSize(); var scale = imageStyle.getScale(); 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); 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); 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]; }; /** * @inheritDoc */ ol.render.webgl.ImageReplay.prototype.getTextures = function(opt_all) { return opt_all ? this.textures_.concat(this.hitDetectionTextures_) : this.textures_; }; /** * @inheritDoc */ ol.render.webgl.ImageReplay.prototype.getHitDetectionTextures = function() { return this.hitDetectionTextures_; }; // 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'); ol.render.webgl.linestringreplay.defaultshader.fragment = new ol.webgl.Fragment(ol.DEBUG_WEBGL ? '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' : 'precision mediump float;varying float a;varying vec2 aVertex;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((aVertex.x+1.0)/2.0*o.x*p,(aVertex.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;}'); ol.render.webgl.linestringreplay.defaultshader.vertex = new ol.webgl.Vertex(ol.DEBUG_WEBGL ? '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' : 'varying float a;varying vec2 aVertex;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;aVertex=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);}}'); // This file is automatically generated, do not edit goog.provide('ol.render.webgl.linestringreplay.defaultshader.Locations'); goog.require('ol'); /** * @constructor * @param {WebGLRenderingContext} gl GL. * @param {WebGLProgram} program Program. * @struct */ ol.render.webgl.linestringreplay.defaultshader.Locations = function(gl, program) { /** * @type {WebGLUniformLocation} */ this.u_projectionMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); /** * @type {WebGLUniformLocation} */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); /** * @type {WebGLUniformLocation} */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); /** * @type {WebGLUniformLocation} */ this.u_lineWidth = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_lineWidth' : 'k'); /** * @type {WebGLUniformLocation} */ this.u_miterLimit = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_miterLimit' : 'l'); /** * @type {WebGLUniformLocation} */ this.u_opacity = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_opacity' : 'm'); /** * @type {WebGLUniformLocation} */ this.u_color = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_color' : 'n'); /** * @type {WebGLUniformLocation} */ this.u_size = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_size' : 'o'); /** * @type {WebGLUniformLocation} */ this.u_pixelRatio = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_pixelRatio' : 'p'); /** * @type {number} */ this.a_lastPos = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_lastPos' : 'd'); /** * @type {number} */ this.a_position = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_position' : 'e'); /** * @type {number} */ this.a_nextPos = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_nextPos' : 'f'); /** * @type {number} */ this.a_direction = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_direction' : 'g'); }; 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.render.webgl.linestringreplay.defaultshader.Locations'); 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>, * lineDashOffset: (number|undefined), * lineJoin: (string|undefined), * lineWidth: (number|undefined), * miterLimit: (number|undefined), * changed: boolean}|null} */ this.state_ = { strokeColor: null, lineCap: undefined, lineDash: null, lineDashOffset: undefined, 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.LineStringReplay.Instruction_, 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.LineStringReplay.Instruction_.BEGIN_LINE_CAP * lineCap, numVertices); numVertices = this.addVertices_([0, 0], p1, p2, -lastSign * ol.render.webgl.LineStringReplay.Instruction_.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.LineStringReplay.Instruction_.BEGIN_LINE * (lineCap || 1), numVertices); numVertices = this.addVertices_([0, 0], p1, p2, -lastSign * ol.render.webgl.LineStringReplay.Instruction_.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 { p0 = p0 || [0, 0]; numVertices = this.addVertices_(p0, p1, [0, 0], lastSign * ol.render.webgl.LineStringReplay.Instruction_.END_LINE * (lineCap || 1), numVertices); numVertices = this.addVertices_(p0, p1, [0, 0], -lastSign * ol.render.webgl.LineStringReplay.Instruction_.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.LineStringReplay.Instruction_.END_LINE_CAP * lineCap, numVertices); numVertices = this.addVertices_(p0, p1, [0, 0], -lastSign * ol.render.webgl.LineStringReplay.Instruction_.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.LineStringReplay.Instruction_.BEVEL_FIRST * (lineJoin || 1), numVertices); numVertices = this.addVertices_(p0, p1, p2, sign * ol.render.webgl.LineStringReplay.Instruction_.BEVEL_SECOND * (lineJoin || 1), numVertices); numVertices = this.addVertices_(p0, p1, p2, -sign * ol.render.webgl.LineStringReplay.Instruction_.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.LineStringReplay.Instruction_.MITER_TOP * lineJoin, numVertices); this.indices[numIndices++] = n + 1; this.indices[numIndices++] = n + 3; this.indices[numIndices++] = n; } } if (closed) { 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.LineStringReplay.Instruction_.BEVEL_FIRST * (lineJoin || 1), numVertices); numVertices = this.addVertices_(p0, p1, p2, -sign * ol.render.webgl.LineStringReplay.Instruction_.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 ends = multiLineStringGeometry.getEnds(); ends.unshift(0); var flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); var stride = multiLineStringGeometry.getStride(); var i, ii; if (ends.length > 1) { for (i = 1, ii = ends.length; i < ii; ++i) { if (this.isValid_(flatCoordinates, ends[i - 1], ends[i], stride)) { var lineString = ol.geom.flat.transform.translate(flatCoordinates, ends[i - 1], ends[i], stride, -this.origin[0], -this.origin[1]); this.drawCoordinates_( lineString, 0, lineString.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) { 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 { //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) { 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) { 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) { 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 strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); this.state_.lineDashOffset = strokeStyleLineDashOffset ? strokeStyleLineDashOffset : ol.render.webgl.defaultLineDashOffset; 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]); } }; /** * @enum {number} * @private */ ol.render.webgl.LineStringReplay.Instruction_ = { 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 }; // 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'); ol.render.webgl.polygonreplay.defaultshader.fragment = new ol.webgl.Fragment(ol.DEBUG_WEBGL ? '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' : '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;}'); ol.render.webgl.polygonreplay.defaultshader.vertex = new ol.webgl.Vertex(ol.DEBUG_WEBGL ? '\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' : 'attribute vec2 a;uniform mat4 b;uniform mat4 c;uniform mat4 d;void main(void){gl_Position=b*vec4(a,0.0,1.0);}'); // This file is automatically generated, do not edit goog.provide('ol.render.webgl.polygonreplay.defaultshader.Locations'); goog.require('ol'); /** * @constructor * @param {WebGLRenderingContext} gl GL. * @param {WebGLProgram} program Program. * @struct */ ol.render.webgl.polygonreplay.defaultshader.Locations = function(gl, program) { /** * @type {WebGLUniformLocation} */ this.u_projectionMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'b'); /** * @type {WebGLUniformLocation} */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'c'); /** * @type {WebGLUniformLocation} */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'd'); /** * @type {WebGLUniformLocation} */ this.u_color = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_color' : 'e'); /** * @type {WebGLUniformLocation} */ this.u_opacity = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_opacity' : 'f'); /** * @type {number} */ this.a_position = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? '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.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.polygonreplay.defaultshader.Locations'); 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 this.processFlatCoordinates_(flatCoordinates, stride, outerRing, rtree, true); var maxCoords = this.getMaxCoords_(outerRing); // 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(), maxCoords: undefined, rtree: new ol.structs.RBush() }; holeLists.push(holeList); this.processFlatCoordinates_(holeFlatCoordinates[i], stride, holeList.list, holeList.rtree, false); this.classifyPoints_(holeList.list, holeList.rtree, true); holeList.maxCoords = this.getMaxCoords_(holeList.list); } holeLists.sort(function(a, b) { return b.maxCoords[0] === a.maxCoords[0] ? a.maxCoords[1] - b.maxCoords[1] : b.maxCoords[0] - a.maxCoords[0]; }); for (i = 0; i < holeLists.length; ++i) { var currList = holeLists[i].list; var start = currList.firstItem(); var currItem = start; var intersection; do { //TODO: Triangulate holes when they intersect the outer ring. if (this.getIntersections_(currItem, rtree).length) { intersection = true; break; } currItem = currList.nextItem(); } while (start !== currItem); if (!intersection) { if (this.bridgeHole_(currList, holeLists[i].maxCoords[0], outerRing, maxCoords[0], rtree)) { rtree.concat(holeLists[i].rtree); this.classifyPoints_(outerRing, rtree, false); } } } } else { 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. */ 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; 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; 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)]); 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; 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)]); 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); }; /** * Returns the rightmost coordinates of a polygon on the X axis. * @private * @param {ol.structs.LinkedList} list Polygons ring. * @return {Array.<number>} Max X coordinates. */ ol.render.webgl.PolygonReplay.prototype.getMaxCoords_ = function(list) { var start = list.firstItem(); var seg = start; var maxCoords = [seg.p0.x, seg.p0.y]; do { seg = list.nextItem(); if (seg.p0.x > maxCoords[0]) { maxCoords = [seg.p0.x, seg.p0.y]; } } while (seg !== start); return maxCoords; }; /** * 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. * @return {boolean} Bridging was successful. */ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, list, listMaxX, rtree) { 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]; var intersection = this.calculateIntersection_(p1, p2, currSeg.p0, currSeg.p1, true); var dist = Math.abs(p1.x - intersection[0]); if (dist < minDist && ol.render.webgl.triangleIsCounterClockwise(p1.x, p1.y, currSeg.p0.x, currSeg.p0.y, currSeg.p1.x, currSeg.p1.y) !== undefined) { minDist = dist; p5 = {x: intersection[0], y: intersection[1], i: -1}; seg = currSeg; } } if (minDist === Infinity) { return false; } 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.x !== bestPoint.x || seg.p1.y !== bestPoint.y) { 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); return true; }; /** * @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.resolveSelfIntersections_(list, rtree, true)) { 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.resolveSelfIntersections_(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 variableCriterion; if (simple) { variableCriterion = this.getPointsInTriangle_(p0, p1, p2, rtree, true).length === 0; } else { variableCriterion = 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) && variableCriterion) { //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.resolveSelfIntersections_ = 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 endss = multiPolygonGeometry.getEndss(); var stride = multiPolygonGeometry.getStride(); var currIndex = this.indices.length; var currLineIndex = this.lineStringReplay.getCurrentIndex(); var flatCoordinates = multiPolygonGeometry.getFlatCoordinates(); var i, ii, j, jj; var start = 0; for (i = 0, ii = endss.length; i < ii; ++i) { var ends = endss[i]; if (ends.length > 0) { var outerRing = ol.geom.flat.transform.translate(flatCoordinates, start, ends[0], stride, -this.origin[0], -this.origin[1]); if (outerRing.length) { var holes = []; var holeFlatCoords; for (j = 1, jj = ends.length; j < jj; ++j) { if (ends[j] !== ends[j - 1]) { holeFlatCoords = ol.geom.flat.transform.translate(flatCoordinates, ends[j - 1], ends[j], stride, -this.origin[0], -this.origin[1]); holes.push(holeFlatCoords); } } this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); this.drawCoordinates_(outerRing, holes, stride); } } start = ends[ends.length - 1]; } 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 ends = polygonGeometry.getEnds(); var stride = polygonGeometry.getStride(); if (ends.length > 0) { var flatCoordinates = polygonGeometry.getFlatCoordinates().map(Number); var outerRing = ol.geom.flat.transform.translate(flatCoordinates, 0, ends[0], stride, -this.origin[0], -this.origin[1]); if (outerRing.length) { var holes = []; var i, ii, holeFlatCoords; for (i = 1, ii = ends.length; i < ii; ++i) { if (ends[i] !== ends[i - 1]) { holeFlatCoords = ol.geom.flat.transform.translate(flatCoordinates, ends[i - 1], ends[i], stride, -this.origin[0], -this.origin[1]); holes.push(holeFlatCoords); } } 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); this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); this.drawCoordinates_(outerRing, 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) { 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 { //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) { 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) { 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) { 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.style.Atlas'); 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) { 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; } } return null; }; goog.provide('ol.render.webgl.TextReplay'); goog.require('ol'); goog.require('ol.colorlike'); goog.require('ol.dom'); goog.require('ol.geom.GeometryType'); goog.require('ol.has'); goog.require('ol.render.replay'); goog.require('ol.render.webgl'); goog.require('ol.render.webgl.TextureReplay'); goog.require('ol.style.AtlasManager'); goog.require('ol.webgl.Buffer'); /** * @constructor * @extends {ol.render.webgl.TextureReplay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Max extent. * @struct */ ol.render.webgl.TextReplay = function(tolerance, maxExtent) { ol.render.webgl.TextureReplay.call(this, tolerance, maxExtent); /** * @private * @type {Array.<HTMLCanvasElement>} */ this.images_ = []; /** * @private * @type {Array.<WebGLTexture>} */ this.textures_ = []; /** * @private * @type {HTMLCanvasElement} */ this.measureCanvas_ = ol.dom.createCanvasContext2D(0, 0).canvas; /** * @private * @type {{strokeColor: (ol.ColorLike|null), * lineCap: (string|undefined), * lineDash: Array.<number>, * lineDashOffset: (number|undefined), * lineJoin: (string|undefined), * lineWidth: number, * miterLimit: (number|undefined), * fillColor: (ol.ColorLike|null), * font: (string|undefined), * scale: (number|undefined)}} */ this.state_ = { strokeColor: null, lineCap: undefined, lineDash: null, lineDashOffset: undefined, lineJoin: undefined, lineWidth: 0, miterLimit: undefined, fillColor: null, font: undefined, scale: undefined }; /** * @private * @type {string} */ this.text_ = ''; /** * @private * @type {number|undefined} */ this.textAlign_ = undefined; /** * @private * @type {number|undefined} */ this.textBaseline_ = undefined; /** * @private * @type {number|undefined} */ this.offsetX_ = undefined; /** * @private * @type {number|undefined} */ this.offsetY_ = undefined; /** * @private * @type {Object.<string, ol.WebglGlyphAtlas>} */ this.atlases_ = {}; /** * @private * @type {ol.WebglGlyphAtlas|undefined} */ this.currAtlas_ = undefined; this.scale = 1; this.opacity = 1; }; ol.inherits(ol.render.webgl.TextReplay, ol.render.webgl.TextureReplay); /** * @inheritDoc */ ol.render.webgl.TextReplay.prototype.drawText = function(geometry, feature) { if (this.text_) { var flatCoordinates = null; var offset = 0; var end = 2; var stride = 2; switch (geometry.getType()) { case ol.geom.GeometryType.POINT: case ol.geom.GeometryType.MULTI_POINT: flatCoordinates = geometry.getFlatCoordinates(); end = flatCoordinates.length; stride = geometry.getStride(); break; case ol.geom.GeometryType.CIRCLE: flatCoordinates = /** @type {ol.geom.Circle} */ (geometry).getCenter(); break; case ol.geom.GeometryType.LINE_STRING: flatCoordinates = /** @type {ol.geom.LineString} */ (geometry).getFlatMidpoint(); break; case ol.geom.GeometryType.MULTI_LINE_STRING: flatCoordinates = /** @type {ol.geom.MultiLineString} */ (geometry).getFlatMidpoints(); end = flatCoordinates.length; break; case ol.geom.GeometryType.POLYGON: flatCoordinates = /** @type {ol.geom.Polygon} */ (geometry).getFlatInteriorPoint(); break; case ol.geom.GeometryType.MULTI_POLYGON: flatCoordinates = /** @type {ol.geom.MultiPolygon} */ (geometry).getFlatInteriorPoints(); end = flatCoordinates.length; break; default: } this.startIndices.push(this.indices.length); this.startIndicesFeature.push(feature); var glyphAtlas = this.currAtlas_; var lines = this.text_.split('\n'); var textSize = this.getTextSize_(lines); var i, ii, j, jj, currX, currY, charArr, charInfo; var anchorX = Math.round(textSize[0] * this.textAlign_ - this.offsetX_); var anchorY = Math.round(textSize[1] * this.textBaseline_ - this.offsetY_); var lineWidth = (this.state_.lineWidth / 2) * this.state_.scale; for (i = 0, ii = lines.length; i < ii; ++i) { currX = 0; currY = glyphAtlas.height * i; charArr = lines[i].split(''); for (j = 0, jj = charArr.length; j < jj; ++j) { charInfo = glyphAtlas.atlas.getInfo(charArr[j]); if (charInfo) { var image = charInfo.image; this.anchorX = anchorX - currX; this.anchorY = anchorY - currY; this.originX = j === 0 ? charInfo.offsetX - lineWidth : charInfo.offsetX; this.originY = charInfo.offsetY; this.height = glyphAtlas.height; this.width = j === 0 || j === charArr.length - 1 ? glyphAtlas.width[charArr[j]] + lineWidth : glyphAtlas.width[charArr[j]]; this.imageHeight = image.height; this.imageWidth = image.width; 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); this.images_.push(image); } } this.drawText_(flatCoordinates, offset, end, stride); } currX += this.width; } } } }; /** * @private * @param {Array.<string>} lines Label to draw split to lines. * @return {Array.<number>} Size of the label in pixels. */ ol.render.webgl.TextReplay.prototype.getTextSize_ = function(lines) { var self = this; var glyphAtlas = this.currAtlas_; var textHeight = lines.length * glyphAtlas.height; //Split every line to an array of chars, sum up their width, and select the longest. var textWidth = lines.map(function(str) { var sum = 0; var i, ii; for (i = 0, ii = str.length; i < ii; ++i) { var curr = str[i]; if (!glyphAtlas.width[curr]) { self.addCharToAtlas_(curr); } sum += glyphAtlas.width[curr] ? glyphAtlas.width[curr] : 0; } return sum; }).reduce(function(max, curr) { return Math.max(max, curr); }); return [textWidth, textHeight]; }; /** * @private * @param {Array.<number>} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {number} end End. * @param {number} stride Stride. */ ol.render.webgl.TextReplay.prototype.drawText_ = function(flatCoordinates, offset, end, stride) { var i, ii; for (i = offset, ii = end; i < ii; i += stride) { this.drawCoordinates(flatCoordinates, offset, end, stride); } }; /** * @private * @param {string} char Character. */ ol.render.webgl.TextReplay.prototype.addCharToAtlas_ = function(char) { if (char.length === 1) { var glyphAtlas = this.currAtlas_; var state = this.state_; var mCtx = this.measureCanvas_.getContext('2d'); mCtx.font = state.font; var width = Math.ceil(mCtx.measureText(char).width * state.scale); var info = glyphAtlas.atlas.add(char, width, glyphAtlas.height, function(ctx, x, y) { //Parameterize the canvas ctx.font = /** @type {string} */ (state.font); ctx.fillStyle = state.fillColor; ctx.strokeStyle = state.strokeColor; ctx.lineWidth = state.lineWidth; ctx.lineCap = /*** @type {string} */ (state.lineCap); ctx.lineJoin = /** @type {string} */ (state.lineJoin); ctx.miterLimit = /** @type {number} */ (state.miterLimit); ctx.textAlign = 'left'; ctx.textBaseline = 'top'; if (ol.has.CANVAS_LINE_DASH && state.lineDash) { //FIXME: use pixelRatio ctx.setLineDash(state.lineDash); ctx.lineDashOffset = /** @type {number} */ (state.lineDashOffset); } if (state.scale !== 1) { //FIXME: use pixelRatio ctx.setTransform(/** @type {number} */ (state.scale), 0, 0, /** @type {number} */ (state.scale), 0, 0); } //Draw the character on the canvas if (state.strokeColor) { ctx.strokeText(char, x, y); } if (state.fillColor) { ctx.fillText(char, x, y); } }); if (info) { glyphAtlas.width[char] = width; } } }; /** * @inheritDoc */ ol.render.webgl.TextReplay.prototype.finish = function(context) { var gl = context.getGL(); this.groupIndices.push(this.indices.length); this.hitDetectionGroupIndices = this.groupIndices; // 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); // create textures /** @type {Object.<string, WebGLTexture>} */ var texturePerImage = {}; this.createTextures(this.textures_, this.images_, texturePerImage, gl); this.state_ = { strokeColor: null, lineCap: undefined, lineDash: null, lineDashOffset: undefined, lineJoin: undefined, lineWidth: 0, miterLimit: undefined, fillColor: null, font: undefined, scale: undefined }; this.text_ = ''; this.textAlign_ = undefined; this.textBaseline_ = undefined; this.offsetX_ = undefined; this.offsetY_ = undefined; this.images_ = null; this.atlases_ = {}; this.currAtlas_ = undefined; ol.render.webgl.TextureReplay.prototype.finish.call(this, context); }; /** * @inheritDoc */ ol.render.webgl.TextReplay.prototype.setTextStyle = function(textStyle) { var state = this.state_; var textFillStyle = textStyle.getFill(); var textStrokeStyle = textStyle.getStroke(); if (!textStyle || !textStyle.getText() || (!textFillStyle && !textStrokeStyle)) { this.text_ = ''; } else { if (!textFillStyle) { state.fillColor = null; } else { var textFillStyleColor = textFillStyle.getColor(); state.fillColor = ol.colorlike.asColorLike(textFillStyleColor ? textFillStyleColor : ol.render.webgl.defaultFillStyle); } if (!textStrokeStyle) { state.strokeColor = null; state.lineWidth = 0; } else { var textStrokeStyleColor = textStrokeStyle.getColor(); state.strokeColor = ol.colorlike.asColorLike(textStrokeStyleColor ? textStrokeStyleColor : ol.render.webgl.defaultStrokeStyle); state.lineWidth = textStrokeStyle.getWidth() || ol.render.webgl.defaultLineWidth; state.lineCap = textStrokeStyle.getLineCap() || ol.render.webgl.defaultLineCap; state.lineDashOffset = textStrokeStyle.getLineDashOffset() || ol.render.webgl.defaultLineDashOffset; state.lineJoin = textStrokeStyle.getLineJoin() || ol.render.webgl.defaultLineJoin; state.miterLimit = textStrokeStyle.getMiterLimit() || ol.render.webgl.defaultMiterLimit; var lineDash = textStrokeStyle.getLineDash(); state.lineDash = lineDash ? lineDash.slice() : ol.render.webgl.defaultLineDash; } state.font = textStyle.getFont() || ol.render.webgl.defaultFont; state.scale = textStyle.getScale() || 1; this.text_ = /** @type {string} */ (textStyle.getText()); var textAlign = ol.render.replay.TEXT_ALIGN[textStyle.getTextAlign()]; var textBaseline = ol.render.replay.TEXT_ALIGN[textStyle.getTextBaseline()]; this.textAlign_ = textAlign === undefined ? ol.render.webgl.defaultTextAlign : textAlign; this.textBaseline_ = textBaseline === undefined ? ol.render.webgl.defaultTextBaseline : textBaseline; this.offsetX_ = textStyle.getOffsetX() || 0; this.offsetY_ = textStyle.getOffsetY() || 0; this.rotateWithView = !!textStyle.getRotateWithView(); this.rotation = textStyle.getRotation() || 0; this.currAtlas_ = this.getAtlas_(state); } }; /** * @private * @param {Object} state Font attributes. * @return {ol.WebglGlyphAtlas} Glyph atlas. */ ol.render.webgl.TextReplay.prototype.getAtlas_ = function(state) { var params = []; var i; for (i in state) { if (state[i] || state[i] === 0) { if (Array.isArray(state[i])) { params = params.concat(state[i]); } else { params.push(state[i]); } } } var hash = this.calculateHash_(params); if (!this.atlases_[hash]) { var mCtx = this.measureCanvas_.getContext('2d'); mCtx.font = state.font; var height = Math.ceil((mCtx.measureText('M').width * 1.5 + state.lineWidth / 2) * state.scale); this.atlases_[hash] = { atlas: new ol.style.AtlasManager({ space: state.lineWidth + 1 }), width: {}, height: height }; } return this.atlases_[hash]; }; /** * @private * @param {Array.<string|number>} params Array of parameters. * @return {string} Hash string. */ ol.render.webgl.TextReplay.prototype.calculateHash_ = function(params) { //TODO: Create a more performant, reliable, general hash function. var i, ii; var hash = ''; for (i = 0, ii = params.length; i < ii; ++i) { hash += params[i]; } return hash; }; /** * @inheritDoc */ ol.render.webgl.TextReplay.prototype.getTextures = function(opt_all) { return this.textures_; }; /** * @inheritDoc */ ol.render.webgl.TextReplay.prototype.getHitDetectionTextures = function() { return this.textures_; }; 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.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.style.Style} style Style. * @param {boolean} group Group with previous replay. */ ol.render.webgl.ReplayGroup.prototype.addDeclutter = function(style, group) {}; /** * @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) { /** * @type {Function} */ var Constructor = ol.render.webgl.ReplayGroup.BATCH_CONSTRUCTORS_[replayType]; 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'); /** * @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; /** * @private * @type {ol.style.Text} */ this.textStyle_ = null; }; ol.inherits(ol.render.webgl.Immediate, ol.render.VectorContext); /** * @param {ol.render.webgl.ReplayGroup} replayGroup Replay group. * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. * @private */ ol.render.webgl.Immediate.prototype.drawText_ = function(replayGroup, geometry) { var context = this.context_; var replay = /** @type {ol.render.webgl.TextReplay} */ ( replayGroup.getReplay(0, ol.render.ReplayType.TEXT)); replay.setTextStyle(this.textStyle_); replay.drawText(geometry, null); 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)(); }; /** * 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. * @override * @api */ ol.render.webgl.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.webgl.Immediate#setStyle} first to set the rendering style. * * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render. * @override * @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: // pass } }; /** * @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); 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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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)(); if (this.textStyle_) { this.drawText_(replayGroup, geometry); } }; /** * @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; }; /** * @inheritDoc */ ol.render.webgl.Immediate.prototype.setTextStyle = function(textStyle) { this.textStyle_ = textStyle; }; // 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'); ol.renderer.webgl.defaultmapshader.fragment = new ol.webgl.Fragment(ol.DEBUG_WEBGL ? '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' : '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;}'); ol.renderer.webgl.defaultmapshader.vertex = new ol.webgl.Vertex(ol.DEBUG_WEBGL ? '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' : '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;}'); // This file is automatically generated, do not edit goog.provide('ol.renderer.webgl.defaultmapshader.Locations'); goog.require('ol'); /** * @constructor * @param {WebGLRenderingContext} gl GL. * @param {WebGLProgram} program Program. * @struct */ ol.renderer.webgl.defaultmapshader.Locations = function(gl, program) { /** * @type {WebGLUniformLocation} */ this.u_texCoordMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_texCoordMatrix' : 'd'); /** * @type {WebGLUniformLocation} */ this.u_projectionMatrix = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'e'); /** * @type {WebGLUniformLocation} */ this.u_opacity = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_opacity' : 'f'); /** * @type {WebGLUniformLocation} */ this.u_texture = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_texture' : 'g'); /** * @type {number} */ this.a_position = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_position' : 'b'); /** * @type {number} */ this.a_texCoord = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'c'); }; goog.provide('ol.renderer.webgl.Layer'); goog.require('ol'); goog.require('ol.render.Event'); goog.require('ol.render.EventType'); goog.require('ol.render.webgl.Immediate'); goog.require('ol.renderer.Layer'); goog.require('ol.renderer.webgl.defaultmapshader'); goog.require('ol.renderer.webgl.defaultmapshader.Locations'); goog.require('ol.transform'); goog.require('ol.vec.Mat4'); goog.require('ol.webgl'); goog.require('ol.webgl.Buffer'); goog.require('ol.webgl.Context'); /** * @constructor * @abstract * @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.EventType.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.EventType.POSTCOMPOSE, context, frameState); }; /** * @param {ol.render.EventType} 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.renderer.webgl.ImageLayer'); goog.require('ol'); goog.require('ol.LayerType'); goog.require('ol.ViewHint'); goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.functions'); goog.require('ol.renderer.Type'); goog.require('ol.renderer.webgl.Layer'); 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. * @api */ 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); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.webgl.ImageLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.WEBGL && layer.getType() === ol.LayerType.IMAGE; }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.webgl.ImageLayer} The layer renderer. */ ol.renderer.webgl.ImageLayer['create'] = function(mapRenderer, layer) { return new ol.renderer.webgl.ImageLayer( /** @type {ol.renderer.webgl.Map} */ (mapRenderer), /** @type {ol.layer.Image} */ (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.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && !ol.extent.isEmpty(renderedExtent)) { var projection = viewState.projection; if (!ol.ENABLE_RASTER_REPROJECTION) { var sourceProjection = imageSource.getProjection(); if (sourceProjection) { 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) { 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.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().forEachFeatureAtCoordinate !== ol.nullFunction) { // for ImageCanvas 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; }; // 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.has'); goog.require('ol.layer.Layer'); goog.require('ol.render.Event'); goog.require('ol.render.EventType'); goog.require('ol.render.webgl.Immediate'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.Type'); 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.PluggableMap} map Map. * @api */ 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_.style.display = 'block'; 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 }); /** * @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.PluggableMap} 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); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @return {boolean} The renderer can render the layer. */ ol.renderer.webgl.Map['handles'] = function(type) { return ol.has.WEBGL && type === ol.renderer.Type.WEBGL; }; /** * Create the map renderer. * @param {Element} container Container. * @param {ol.PluggableMap} map Map. * @return {ol.renderer.webgl.Map} The map renderer. */ ol.renderer.webgl.Map['create'] = function(container, map) { return new ol.renderer.webgl.Map(container, 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 }); } }; /** * @param {ol.render.EventType} 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.PluggableMap} 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.EventType.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.EventType.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; }; // 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'); ol.renderer.webgl.tilelayershader.fragment = new ol.webgl.Fragment(ol.DEBUG_WEBGL ? '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' : 'precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}'); ol.renderer.webgl.tilelayershader.vertex = new ol.webgl.Vertex(ol.DEBUG_WEBGL ? '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' : '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;}'); // This file is automatically generated, do not edit goog.provide('ol.renderer.webgl.tilelayershader.Locations'); goog.require('ol'); /** * @constructor * @param {WebGLRenderingContext} gl GL. * @param {WebGLProgram} program Program. * @struct */ ol.renderer.webgl.tilelayershader.Locations = function(gl, program) { /** * @type {WebGLUniformLocation} */ this.u_tileOffset = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_tileOffset' : 'd'); /** * @type {WebGLUniformLocation} */ this.u_texture = gl.getUniformLocation( program, ol.DEBUG_WEBGL ? 'u_texture' : 'e'); /** * @type {number} */ this.a_position = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? 'a_position' : 'b'); /** * @type {number} */ this.a_texCoord = gl.getAttribLocation( program, ol.DEBUG_WEBGL ? '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.LayerType'); goog.require('ol.TileRange'); goog.require('ol.TileState'); goog.require('ol.array'); goog.require('ol.extent'); goog.require('ol.math'); goog.require('ol.renderer.Type'); goog.require('ol.renderer.webgl.Layer'); goog.require('ol.renderer.webgl.tilelayershader'); goog.require('ol.renderer.webgl.tilelayershader.Locations'); 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. * @api */ 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); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.webgl.TileLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.WEBGL && layer.getType() === ol.LayerType.TILE; }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.webgl.TileLayer} The layer renderer. */ ol.renderer.webgl.TileLayer['create'] = function(mapRenderer, layer) { return new ol.renderer.webgl.TileLayer( /** @type {ol.renderer.webgl.Map} */ (mapRenderer), /** @type {ol.layer.Tile} */ (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); }; /** * @inheritDoc */ 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.getTileRangeForExtentAndZ(extent, z); 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.TileState.LOADED || tileState == ol.TileState.EMPTY || tileState == ol.TileState.ERROR && !useInterimTilesOnError; if (!drawable) { tile = tile.getInterimTile(); } tileState = tile.getState(); if (tileState == ol.TileState.LOADED) { if (mapRenderer.isTileTextureLoaded(tile)) { tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; continue; } } else if (tileState == ol.TileState.EMPTY || (tileState == ol.TileState.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.TileState.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.LayerType'); goog.require('ol.ViewHint'); goog.require('ol.extent'); goog.require('ol.render.webgl.ReplayGroup'); goog.require('ol.renderer.Type'); 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. * @api */ 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); /** * Determine if this renderer handles the provided layer. * @param {ol.renderer.Type} type The renderer type. * @param {ol.layer.Layer} layer The candidate layer. * @return {boolean} The renderer can render the layer. */ ol.renderer.webgl.VectorLayer['handles'] = function(type, layer) { return type === ol.renderer.Type.WEBGL && layer.getType() === ol.LayerType.VECTOR; }; /** * Create a layer renderer. * @param {ol.renderer.Map} mapRenderer The map renderer. * @param {ol.layer.Layer} layer The layer to be rendererd. * @return {ol.renderer.webgl.VectorLayer} The layer renderer. */ ol.renderer.webgl.VectorLayer['create'] = function(mapRenderer, layer) { return new ol.renderer.webgl.VectorLayer( /** @type {ol.renderer.webgl.Map} */ (mapRenderer), /** @type {ol.layer.Vector} */ (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.updateLogos(frameState, vectorSource); var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; var interacting = frameState.viewHints[ol.ViewHint.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.Map'); goog.require('ol'); goog.require('ol.PluggableMap'); goog.require('ol.PluginType'); goog.require('ol.control'); goog.require('ol.interaction'); goog.require('ol.obj'); goog.require('ol.plugins'); goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.Map'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); goog.require('ol.renderer.canvas.VectorTileLayer'); goog.require('ol.renderer.webgl.ImageLayer'); goog.require('ol.renderer.webgl.Map'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VectorLayer'); if (ol.ENABLE_CANVAS) { ol.plugins.register(ol.PluginType.MAP_RENDERER, ol.renderer.canvas.Map); ol.plugins.registerMultiple(ol.PluginType.LAYER_RENDERER, [ ol.renderer.canvas.ImageLayer, ol.renderer.canvas.TileLayer, ol.renderer.canvas.VectorLayer, ol.renderer.canvas.VectorTileLayer ]); } if (ol.ENABLE_WEBGL) { ol.plugins.register(ol.PluginType.MAP_RENDERER, ol.renderer.webgl.Map); ol.plugins.registerMultiple(ol.PluginType.LAYER_RENDERER, [ ol.renderer.webgl.ImageLayer, ol.renderer.webgl.TileLayer, ol.renderer.webgl.VectorLayer ]); } /** * @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.PluggableMap} * @param {olx.MapOptions} options Map options. * @fires ol.MapBrowserEvent * @fires ol.MapEvent * @fires ol.render.Event#postcompose * @fires ol.render.Event#precompose * @api */ ol.Map = function(options) { options = ol.obj.assign({}, options); if (!options.controls) { options.controls = ol.control.defaults(); } if (!options.interactions) { options.interactions = ol.interaction.defaults(); } ol.PluggableMap.call(this, options); }; ol.inherits(ol.Map, ol.PluggableMap); 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.proj.common'); goog.require('ol.proj'); /** * Deprecated. Transforms between EPSG:4326 and EPSG:3857 are now included by * default. There is no need to call this function in application code and it * will be removed in a future major release. * @deprecated This function is no longer necessary. * @api */ ol.proj.common.add = ol.proj.addCommon; 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'); goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.math'); goog.require('ol.proj'); /** * 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 sourceExtent = sourceProj.getExtent(); if (!sourceExtent || ol.extent.containsCoordinate(sourceExtent, sourceCenter)) { 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(); 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]); 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_) { 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.ImageBase'); goog.require('ol.ImageState'); 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.ImageState.LOADED; if (this.sourceImage_) { state = ol.ImageState.IDLE; } ol.ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state); }; ol.inherits(ol.reproj.Image, ol.ImageBase); /** * @inheritDoc */ ol.reproj.Image.prototype.disposeInternal = function() { if (this.state == ol.ImageState.LOADING) { this.unlistenSource_(); } ol.ImageBase.prototype.disposeInternal.call(this); }; /** * @inheritDoc */ ol.reproj.Image.prototype.getImage = function() { 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.ImageState.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.ImageState.IDLE) { this.state = ol.ImageState.LOADING; this.changed(); var sourceState = this.sourceImage_.getState(); if (sourceState == ol.ImageState.LOADED || sourceState == ol.ImageState.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.ImageState.LOADED || sourceState == ol.ImageState.ERROR) { this.unlistenSource_(); this.reproject_(); } }, this); this.sourceImage_.load(); } } }; /** * @private */ ol.reproj.Image.prototype.unlistenSource_ = function() { ol.events.unlistenByKey(/** @type {!ol.EventsKey} */ (this.sourceListenerKey_)); this.sourceListenerKey_ = null; }; goog.provide('ol.reproj.Tile'); goog.require('ol'); goog.require('ol.Tile'); goog.require('ol.TileState'); 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.TileState.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.TileState.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.TileState.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.TileState.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.TileState.EMPTY; } else { var sourceRange = sourceTileGrid.getTileRangeForExtentAndZ( sourceExtent, this.sourceZ_); 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.TileState.EMPTY; } } }; ol.inherits(ol.reproj.Tile, ol.Tile); /** * @inheritDoc */ ol.reproj.Tile.prototype.disposeInternal = function() { if (this.state == ol.TileState.LOADING) { this.unlistenSources_(); } ol.Tile.prototype.disposeInternal.call(this); }; /** * Get the HTML Canvas element for this tile. * @return {HTMLCanvasElement} Canvas. */ 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.TileState.LOADED) { sources.push({ extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord), image: tile.getImage() }); } }, this); this.sourceTiles_.length = 0; if (sources.length === 0) { this.state = ol.TileState.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.TileState.LOADED; } this.changed(); }; /** * @inheritDoc */ ol.reproj.Tile.prototype.load = function() { if (this.state == ol.TileState.IDLE) { this.state = ol.TileState.LOADING; this.changed(); var leftToLoad = 0; this.sourcesListenerKeys_ = []; this.sourceTiles_.forEach(function(tile, i, arr) { var state = tile.getState(); if (state == ol.TileState.IDLE || state == ol.TileState.LOADING) { leftToLoad++; var sourceListenKey; sourceListenKey = ol.events.listen(tile, ol.events.EventType.CHANGE, function(e) { var state = tile.getState(); if (state == ol.TileState.LOADED || state == ol.TileState.ERROR || state == ol.TileState.EMPTY) { ol.events.unlistenByKey(sourceListenKey); leftToLoad--; 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.TileState.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.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) { 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'); goog.require('ol.tilecoord'); /** * @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, opt_highWaterMark); }; ol.inherits(ol.TileCache, ol.structs.LRUCache); /** * @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(); } } }; /** * Prune all tiles from the cache that don't have the same z as the newest tile. */ ol.TileCache.prototype.pruneExceptNewestZ = function() { if (this.getCount() === 0) { return; } var key = this.peekFirstKey(); var tileCoord = ol.tilecoord.fromKey(key); var z = tileCoord[0]; this.forEach(function(tile) { if (tile.tileCoord[0] !== z) { this.remove(ol.tilecoord.getKey(tile.tileCoord)); tile.dispose(); } }, this); }; goog.provide('ol.source.Tile'); goog.require('ol'); goog.require('ol.TileCache'); goog.require('ol.TileState'); 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 * @abstract * @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_ = ''; /** * @protected * @type {olx.TileOptions} */ this.tileOptions = {transition: options.transition}; }; 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 = ol.tilecoord.getKeyZXY(z, x, y); loaded = false; if (tileCache.containsKey(tileCoordKey)) { tile = /** @type {!ol.Tile} */ (tileCache.get(tileCoordKey)); loaded = tile.getState() === ol.TileState.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 {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 */ 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 `pixelRatio` as close as possible. * @param {number} pixelRatio Pixel ratio. * @return {number} Tile pixel ratio. */ ol.source.Tile.prototype.getTilePixelRatio = function(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); goog.provide('ol.source.TileEventType'); /** * @enum {string} */ ol.source.TileEventType = { /** * Triggered when a tile starts loading. * @event ol.source.Tile.Event#tileloadstart * @api */ TILELOADSTART: 'tileloadstart', /** * Triggered when a tile finishes loading, either when its data is loaded, * or when loading was aborted because the tile is no longer needed. * @event ol.source.Tile.Event#tileloadend * @api */ TILELOADEND: 'tileloadend', /** * Triggered if tile loading results in an error. * @event ol.source.Tile.Event#tileloaderror * @api */ TILELOADERROR: 'tileloaderror' }; goog.provide('ol.source.UrlTile'); goog.require('ol'); goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); goog.require('ol.source.Tile'); goog.require('ol.source.TileEventType'); goog.require('ol.tilecoord'); /** * @classdesc * Base class for sources providing tiles divided into a tile grid over http. * * @constructor * @abstract * @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, transition: options.transition }); /** * @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); } /** * @private * @type {Object.<number, boolean>} */ this.tileLoadingKeys_ = {}; }; 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); var uid = ol.getUid(tile); var tileState = tile.getState(); var type; if (tileState == ol.TileState.LOADING) { this.tileLoadingKeys_[uid] = true; type = ol.source.TileEventType.TILELOADSTART; } else if (uid in this.tileLoadingKeys_) { delete this.tileLoadingKeys_[uid]; type = tileState == ol.TileState.ERROR ? ol.source.TileEventType.TILELOADERROR : (tileState == ol.TileState.LOADED || tileState == ol.TileState.ABORT) ? ol.source.TileEventType.TILELOADEND : undefined; } if (type != undefined) { this.dispatchEvent(new ol.source.Tile.Event(type, tile)); } }; /** * 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; this.tileCache.pruneExceptNewestZ(); if (typeof opt_key !== 'undefined') { this.setKey(opt_key); } else { this.changed(); } }; /** * Set the URL to use for requests. * @param {string} url URL. * @api */ 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 */ 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 = ol.tilecoord.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.TileCache'); goog.require('ol.TileState'); 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.tilecoord'); 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, transition: options.transition }); /** * @protected * @type {?string} */ this.crossOrigin = options.crossOrigin !== undefined ? options.crossOrigin : null; /** * @protected * @type {function(new: ol.ImageTile, ol.TileCoord, ol.TileState, string, * ?string, ol.TileLoadFunctionType, olx.TileOptions=)} */ 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(this.tileCache.highWaterMark); } 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.TileState.IDLE : ol.TileState.EMPTY, tileUrl !== undefined ? tileUrl : '', this.crossOrigin, this.tileLoadFunction, this.tileOptions); 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) { var sourceProjection = /** @type {!ol.proj.Projection} */ (this.getProjection()); if (!ol.ENABLE_RASTER_REPROJECTION || !sourceProjection || !projection || ol.proj.equivalent(sourceProjection, projection)) { return this.getTileInternal(z, x, y, pixelRatio, sourceProjection || projection); } else { var cache = this.getTileCacheForProjection(projection); var tileCoord = [z, x, y]; var tile; var tileCoordKey = ol.tilecoord.getKey(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 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; newTile.refreshInterimChain(); 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 = ol.tilecoord.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.TileState.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.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 */ 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, transition: options.transition }); /** * @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_ + '&c=' + this.culture_; 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 {string} * @api */ ol.source.BingMaps.TOS_ATTRIBUTION = '<a class="ol-attribution-bing-tos" ' + 'href="https://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]; 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.hidpi_ ? 2 : 1) }); 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) { 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()); this.setAttributions(function(frameState) { var attributions = []; var zoom = frameState.viewState.zoom; resource.imageryProviders.map(function(imageryProvider) { var intersects = false; var coverageAreas = imageryProvider.coverageAreas; for (var i = 0, ii = coverageAreas.length; i < ii; ++i) { var coverageArea = coverageAreas[i]; if (zoom >= coverageArea.zoomMin && zoom <= coverageArea.zoomMax) { var bbox = coverageArea.bbox; var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; var extent = ol.extent.applyTransform(epsg4326Extent, transform); if (ol.extent.intersects(extent, frameState.extent)) { intersects = true; break; } } } if (intersects) { attributions.push(imageryProvider.attribution); } }); attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION); return 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 */ 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, transition: options.transition }); }; 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 Maps API. * * @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_ + '.carto.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} * @protected */ this.resolution = undefined; /** * @type {number} * @protected */ this.distance = options.distance !== undefined ? options.distance : 20; /** * @type {Array.<ol.Feature>} * @protected */ this.features = []; /** * @param {ol.Feature} feature Feature. * @return {ol.geom.Point} Cluster calculation point. * @protected */ 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} * @protected */ 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 the distance in pixels between clusters. * @return {number} Distance. * @api */ ol.source.Cluster.prototype.getDistance = function() { return this.distance; }; /** * 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 * @override */ ol.source.Cluster.prototype.refresh = function() { this.clear(); this.cluster(); this.addFeatures(this.features); ol.source.Vector.prototype.refresh.call(this); }; /** * @protected */ 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); 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)); } } } }; /** * @param {Array.<ol.Feature>} features Features * @return {ol.Feature} The cluster feature. * @protected */ 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.source.Image'); goog.require('ol'); goog.require('ol.ImageState'); 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 * @abstract * @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; /** * @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. * @override */ 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 && 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.ImageState.LOADING: this.dispatchEvent( new ol.source.Image.Event(ol.source.Image.EventType_.IMAGELOADSTART, image)); break; case ol.ImageState.LOADED: this.dispatchEvent( new ol.source.Image.Event(ol.source.Image.EventType_.IMAGELOADEND, image)); break; case ol.ImageState.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} * @private */ 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.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 {boolean} */ this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; /** * @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 */ 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); 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; } 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, 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) { // 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'] = Math.round(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 */ 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 */ 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 */ ol.source.ImageArcGISRest.prototype.updateParams = function(params) { ol.obj.assign(this.params_, params); this.image_ = null; this.changed(); }; 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, canvasElement); } this.canvas_ = canvas; this.renderedRevision_ = this.getRevision(); return canvas; }; 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 */ 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 */ 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, 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 */ 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.ImageState'); 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 */ 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, 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.ImageState.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 { imageWidth = image.width; imageHeight = 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.ImageVector'); goog.require('ol'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.ext.rbush'); 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'); /** * @deprecated * @classdesc * **Deprecated**. Use an `ol.layer.Vector` with `renderMode: 'image'` and an * `ol.source.Vector` instead. * * 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]; /** * Declutter tree. * @private */ this.declutterTree_ = ol.ext.rbush(9); /** * @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, pixelRatio, this.source_.getOverlaps(), this.declutterTree_, 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]); } this.declutterTree_.clear(); var transform = this.getTransform_(ol.extent.getCenter(extent), resolution, pixelRatio, size); replayGroup.replay(this.canvasContext_, 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 = {}; var result = 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); } }, null); return result; } }; /** * 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 */ ol.source.ImageVector.prototype.getStyle = function() { return this.style_; }; /** * Get the style function. * @return {ol.StyleFunction|undefined} Layer style function. * @api */ 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 */ 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.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.reproj'); 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 */ 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 */ ol.source.ImageWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { if (this.url_ === undefined) { return undefined; } var projectionObj = ol.proj.get(projection); var sourceProjectionObj = this.getProjection(); if (sourceProjectionObj && sourceProjectionObj !== projectionObj) { resolution = ol.reproj.calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, resolution); coordinate = ol.proj.transform(coordinate, projectionObj, sourceProjectionObj); } 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, sourceProjectionObj || projectionObj, 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 */ 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; } var imageResolution = resolution / pixelRatio; var center = ol.extent.getCenter(extent); var viewWidth = Math.ceil(ol.extent.getWidth(extent) / imageResolution); var viewHeight = Math.ceil(ol.extent.getHeight(extent) / imageResolution); var viewExtent = ol.extent.getForViewAndSize(center, imageResolution, 0, [viewWidth, viewHeight]); var requestWidth = Math.ceil(this.ratio_ * ol.extent.getWidth(extent) / imageResolution); var requestHeight = Math.ceil(this.ratio_ * ol.extent.getHeight(extent) / imageResolution); var requestExtent = ol.extent.getForViewAndSize(center, imageResolution, 0, [requestWidth, requestHeight]); var image = this.image_; if (image && this.renderedRevision_ == this.getRevision() && image.getResolution() == resolution && image.getPixelRatio() == pixelRatio && ol.extent.containsExtent(image.getExtent(), viewExtent)) { return image; } 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.round(ol.extent.getWidth(requestExtent) / imageResolution); this.imageSize_[1] = Math.round(ol.extent.getHeight(requestExtent) / imageResolution); var url = this.getRequestUrl_(requestExtent, this.imageSize_, pixelRatio, projection, params); this.image_ = new ol.Image(requestExtent, resolution, pixelRatio, 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 */ 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 */ 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 */ 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.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 */ 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 {string} * @api */ ol.source.OSM.ATTRIBUTION = '© ' + '<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> ' + 'contributors.'; /** * @fileoverview * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} */ goog.provide('ol.ext.pixelworks.Processor'); /** @typedef {function(*)} */ ol.ext.pixelworks.Processor = function() {}; (function() {(function (exports) { 'use strict'; var hasImageData = true; try { new ImageData(10, 10); } catch (_) { hasImageData = false; } var context = document.createElement('canvas').getContext('2d'); function newImageData$1(data, width, height) { if (hasImageData) { return new ImageData(data, width, height); } else { var imageData = context.createImageData(width, height); imageData.data.set(data); return imageData; } } var newImageData_1 = newImageData$1; var util = { newImageData: newImageData_1 }; var newImageData = util.newImageData; 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) { 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; }; } 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; } function createFauxWorker(config, onMessage) { var minion = createMinion(config.operation); return { postMessage: function(data) { setTimeout(function() { onMessage({'data': {'buffer': minion(data), 'meta': data['meta']}}); }, 0); } }; } 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; } Processor.prototype.process = function(inputs, meta, callback) { this._enqueue({ inputs: inputs, meta: meta, callback: callback }); this._dispatch(); }; Processor.prototype.destroy = function() { for (var key in this) { this[key] = null; } this._destroyed = true; }; Processor.prototype._enqueue = function(job) { this._queue.push(job); while (this._queue.length > this._maxQueueLength) { this._queue.shift().callback(null, null); } }; 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); } } } }; Processor.prototype._onWorkerMessage = function(index, event) { if (this._destroyed) { return; } this._dataLookup[index] = event.data; --this._running; if (this._running === 0) { this._resolveJob(); } }; 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(); }; var processor = Processor; var Processor_1 = processor; var lib = { Processor: Processor_1 }; exports['default'] = lib; exports.Processor = Processor_1; }((this.pixelworks = this.pixelworks || {})));}).call(ol.ext); goog.provide('ol.source.RasterOperationType'); /** * Raster operation type. Supported values are `'pixel'` and `'image'`. * @enum {string} */ ol.source.RasterOperationType = { PIXEL: 'pixel', IMAGE: 'image' }; goog.provide('ol.source.Raster'); goog.require('ol'); 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.Processor'); 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.RasterOperationType'); goog.require('ol.source.State'); goog.require('ol.source.Tile'); goog.require('ol.transform'); /** * @classdesc * A source that transforms data from any number of input sources using an * {@link ol.RasterOperation} function 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.RasterOperationType} */ this.operationType_ = options.operationType !== undefined ? options.operationType : ol.source.RasterOperationType.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 {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 requested frame state. * @type {olx.FrameState} * @private */ this.requestedFrameState_; /** * The most recently rendered image canvas. * @type {ol.ImageCanvas} * @private */ this.renderedImageCanvas_ = null; /** * The most recently rendered revision. * @type {number} */ this.renderedRevision_; /** * @private * @type {olx.FrameState} */ this.frameState_ = { animate: false, 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.RasterOperationType.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); frameState.extent = extent.slice(); frameState.focus = center; frameState.size[0] = Math.round(ol.extent.getWidth(extent) / resolution); frameState.size[1] = Math.round(ol.extent.getHeight(extent) / resolution); frameState.time = Date.now(); frameState.animate = false; var viewState = frameState.viewState; viewState.center = center; viewState.projection = projection; viewState.resolution = resolution; return frameState; }; /** * 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; }; /** * @inheritDoc */ ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, projection) { if (!this.allSourcesReady_()) { return null; } var frameState = this.updateFrameState_(extent, resolution, projection); this.requestedFrameState_ = frameState; // check if we can't reuse the existing ol.ImageCanvas if (this.renderedImageCanvas_) { var renderedResolution = this.renderedImageCanvas_.getResolution(); var renderedExtent = this.renderedImageCanvas_.getExtent(); if (resolution !== renderedResolution || !ol.extent.equals(extent, renderedExtent)) { this.renderedImageCanvas_ = null; } } if (!this.renderedImageCanvas_ || this.getRevision() !== this.renderedRevision_) { this.processSources_(); } frameState.tileQueue.loadMoreTiles(16, 16); if (frameState.animate) { requestAnimationFrame(this.changed.bind(this)); } return this.renderedImageCanvas_; }; /** * Start processing source data. * @private */ ol.source.Raster.prototype.processSources_ = function() { var frameState = this.requestedFrameState_; 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 { return; } } 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)); }; /** * Called when pixel processing is complete. * @param {olx.FrameState} frameState The frame state. * @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, err, output, data) { if (err || !output) { return; } // do nothing if extent or resolution changed var extent = frameState.extent; var resolution = frameState.viewState.resolution; if (resolution !== this.requestedFrameState_.viewState.resolution || !ol.extent.equals(extent, this.requestedFrameState_.extent)) { return; } var context; if (this.renderedImageCanvas_) { context = this.renderedImageCanvas_.getImage().getContext('2d'); } else { var width = Math.round(ol.extent.getWidth(extent) / resolution); var height = Math.round(ol.extent.getHeight(extent) / resolution); context = ol.dom.createCanvasContext2D(width, height); this.renderedImageCanvas_ = new ol.ImageCanvas(extent, resolution, 1, context.canvas); } context.putImageData(output, 0, 0); this.changed(); this.renderedRevision_ = this.getRevision(); this.dispatchEvent(new ol.source.Raster.Event( ol.source.Raster.EventType_.AFTEROPERATIONS, frameState, data)); }; /** * 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); } 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); /** * @override */ ol.source.Raster.prototype.getImageInternal = function() { return null; // not implemented }; /** * @enum {string} * @private */ 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' }; goog.provide('ol.source.Stamen'); goog.require('ol'); 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 */ ol.source.Stamen = function(options) { var i = options.layer.indexOf('-'); var provider = i == -1 ? options.layer : options.layer.slice(0, i); var providerConfig = ol.source.Stamen.ProviderConfig[provider]; 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, wrapX: options.wrapX }); }; ol.inherits(ol.source.Stamen, ol.source.XYZ); /** * @const * @type {Array.<string>} */ ol.source.Stamen.ATTRIBUTIONS = [ 'Map tiles by <a href="https://stamen.com/">Stamen Design</a>, ' + 'under <a href="https://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, transition: options.transition }); /** * @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 */ 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.TileState'); goog.require('ol.dom'); goog.require('ol.size'); goog.require('ol.source.Tile'); goog.require('ol.tilecoord'); /** * @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 = ol.tilecoord.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.TileState.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; } }; /** * @override */ ol.source.TileDebug.Tile_.prototype.load = function() {}; // FIXME check order of async callbacks /** * @see http://mapbox.com/developers/api/ */ goog.provide('ol.source.TileJSON'); goog.require('ol'); goog.require('ol.TileUrlFunction'); goog.require('ol.asserts'); 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 */ 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, transition: options.transition }); if (options.url) { 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(); } } 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.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); } 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.getAttributions2()) { var attributionExtent = extent !== undefined ? extent : epsg4326Projection.getExtent(); this.setAttributions(function(frameState) { if (ol.extent.intersects(attributionExtent, frameState.extent)) { return [tileJSON.attribution]; } return null; }); } 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.Tile'); goog.require('ol.TileState'); 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.tilecoord'); 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); } 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(); this.setAttributions(function(frameState) { if (ol.extent.intersects(attributionExtent, frameState.extent)) { return [tileJSON.attribution]; } return null; }); } this.setState(ol.source.State.READY); }; /** * @inheritDoc */ ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projection) { var tileCoordKey = ol.tilecoord.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 = this.tileUrlFunction_(urlTileCoord, pixelRatio, projection); var tile = new ol.source.TileUTFGrid.Tile_( tileCoord, tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.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 = ol.tilecoord.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.TileState} 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.TileState.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.TileState.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.TileState.EMPTY; this.changed(); }; /** * @private */ ol.source.TileUTFGrid.Tile_.prototype.loadInternal_ = function() { if (this.state == ol.TileState.IDLE) { this.state = ol.TileState.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_(); }; /** * @override */ 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.reproj'); 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 */ 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, tileClass: options.tileClass, tileGrid: options.tileGrid, tileLoadFunction: options.tileLoadFunction, url: options.url, urls: options.urls, wrapX: options.wrapX !== undefined ? options.wrapX : true, transition: options.transition }); /** * @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 {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 */ ol.source.TileWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { var projectionObj = ol.proj.get(projection); var sourceProjectionObj = this.getProjection(); 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); } if (sourceProjectionObj && sourceProjectionObj !== projectionObj) { tileResolution = ol.reproj.calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, tileResolution); tileExtent = ol.proj.transformExtent(tileExtent, projectionObj, sourceProjectionObj); coordinate = ol.proj.transform(coordinate, projectionObj, sourceProjectionObj); } 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, sourceProjectionObj || projectionObj, baseParams); }; /** * @inheritDoc */ ol.source.TileWMS.prototype.getGutterInternal = function() { return this.gutter_; }; /** * 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.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 * @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); }; /** * Update the user-provided params. * @param {Object} params Params. * @api */ ol.source.TileWMS.prototype.updateParams = function(params) { ol.obj.assign(this.params_, params); 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.VectorImageTile'); goog.require('ol'); goog.require('ol.Tile'); goog.require('ol.TileState'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.extent'); goog.require('ol.events.EventType'); goog.require('ol.featureloader'); /** * @constructor * @extends {ol.Tile} * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileState} state State. * @param {number} sourceRevision Source revision. * @param {ol.format.Feature} format Feature format. * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. * @param {ol.TileCoord} urlTileCoord Wrapped tile coordinate for source urls. * @param {ol.TileUrlFunctionType} tileUrlFunction Tile url function. * @param {ol.tilegrid.TileGrid} sourceTileGrid Tile grid of the source. * @param {ol.tilegrid.TileGrid} tileGrid Tile grid of the renderer. * @param {Object.<string,ol.VectorTile>} sourceTiles Source tiles. * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @param {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string, * ol.format.Feature, ol.TileLoadFunctionType)} tileClass Class to * instantiate for source tiles. * @param {function(this: ol.source.VectorTile, ol.events.Event)} handleTileChange * Function to call when a source tile's state changes. * @param {olx.TileOptions=} opt_options Tile options. */ ol.VectorImageTile = function(tileCoord, state, sourceRevision, format, tileLoadFunction, urlTileCoord, tileUrlFunction, sourceTileGrid, tileGrid, sourceTiles, pixelRatio, projection, tileClass, handleTileChange, opt_options) { ol.Tile.call(this, tileCoord, state, opt_options); /** * @private * @type {Object.<string, CanvasRenderingContext2D>} */ this.context_ = {}; /** * @private * @type {ol.FeatureLoader} */ this.loader_; /** * @private * @type {Object.<string, ol.TileReplayState>} */ this.replayState_ = {}; /** * @private * @type {Object.<string,ol.VectorTile>} */ this.sourceTiles_ = sourceTiles; /** * Keys of source tiles used by this tile. Use with {@link #getTile}. * @type {Array.<string>} */ this.tileKeys = []; /** * @type {number} */ this.sourceRevision_ = sourceRevision; /** * @type {ol.TileCoord} */ this.wrappedTileCoord = urlTileCoord; /** * @type {Array.<ol.EventsKey>} */ this.loadListenerKeys_ = []; /** * @type {Array.<ol.EventsKey>} */ this.sourceTileListenerKeys_ = []; if (urlTileCoord) { var extent = tileGrid.getTileCoordExtent(urlTileCoord); var resolution = tileGrid.getResolution(tileCoord[0]); var sourceZ = sourceTileGrid.getZForResolution(resolution); sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { var sharedExtent = ol.extent.getIntersection(extent, sourceTileGrid.getTileCoordExtent(sourceTileCoord)); var sourceExtent = sourceTileGrid.getExtent(); if (sourceExtent) { sharedExtent = ol.extent.getIntersection(sharedExtent, sourceExtent); } if (ol.extent.getWidth(sharedExtent) / resolution >= 0.5 && ol.extent.getHeight(sharedExtent) / resolution >= 0.5) { // only include source tile if overlap is at least 1 pixel var sourceTileKey = sourceTileCoord.toString(); var sourceTile = sourceTiles[sourceTileKey]; if (!sourceTile) { var tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection); sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, tileUrl == undefined ? ol.TileState.EMPTY : ol.TileState.IDLE, tileUrl == undefined ? '' : tileUrl, format, tileLoadFunction); this.sourceTileListenerKeys_.push( ol.events.listen(sourceTile, ol.events.EventType.CHANGE, handleTileChange)); } sourceTile.consumers++; this.tileKeys.push(sourceTileKey); } }.bind(this)); } }; ol.inherits(ol.VectorImageTile, ol.Tile); /** * @inheritDoc */ ol.VectorImageTile.prototype.disposeInternal = function() { for (var i = 0, ii = this.tileKeys.length; i < ii; ++i) { var sourceTileKey = this.tileKeys[i]; var sourceTile = this.getTile(sourceTileKey); sourceTile.consumers--; if (sourceTile.consumers == 0) { delete this.sourceTiles_[sourceTileKey]; sourceTile.dispose(); } } this.tileKeys.length = 0; this.sourceTiles_ = null; this.loadListenerKeys_.forEach(ol.events.unlistenByKey); this.loadListenerKeys_.length = 0; if (this.interimTile) { this.interimTile.dispose(); } this.state = ol.TileState.ABORT; this.changed(); this.sourceTileListenerKeys_.forEach(ol.events.unlistenByKey); this.sourceTileListenerKeys_.length = 0; ol.Tile.prototype.disposeInternal.call(this); }; /** * @param {ol.layer.Layer} layer Layer. * @return {CanvasRenderingContext2D} The rendering context. */ ol.VectorImageTile.prototype.getContext = function(layer) { var key = ol.getUid(layer).toString(); if (!(key in this.context_)) { this.context_[key] = ol.dom.createCanvasContext2D(); } return this.context_[key]; }; /** * Get the Canvas for this tile. * @param {ol.layer.Layer} layer Layer. * @return {HTMLCanvasElement} Canvas. */ ol.VectorImageTile.prototype.getImage = function(layer) { return this.getReplayState(layer).renderedTileRevision == -1 ? null : this.getContext(layer).canvas; }; /** * @param {ol.layer.Layer} layer Layer. * @return {ol.TileReplayState} The replay state. */ ol.VectorImageTile.prototype.getReplayState = function(layer) { var key = ol.getUid(layer).toString(); if (!(key in this.replayState_)) { this.replayState_[key] = { dirty: false, renderedRenderOrder: null, renderedRevision: -1, renderedTileRevision: -1 }; } return this.replayState_[key]; }; /** * @inheritDoc */ ol.VectorImageTile.prototype.getKey = function() { return this.tileKeys.join('/') + '-' + this.sourceRevision_; }; /** * @param {string} tileKey Key (tileCoord) of the source tile. * @return {ol.VectorTile} Source tile. */ ol.VectorImageTile.prototype.getTile = function(tileKey) { return this.sourceTiles_[tileKey]; }; /** * @inheritDoc */ ol.VectorImageTile.prototype.load = function() { // Source tiles with LOADED state - we just count them because once they are // loaded, we're no longer listening to state changes. var leftToLoad = 0; // Source tiles with ERROR state - we track them because they can still have // an ERROR state after another load attempt. var errorSourceTiles = {}; if (this.state == ol.TileState.IDLE) { this.setState(ol.TileState.LOADING); } if (this.state == ol.TileState.LOADING) { this.tileKeys.forEach(function(sourceTileKey) { var sourceTile = this.getTile(sourceTileKey); if (sourceTile.state == ol.TileState.IDLE) { sourceTile.setLoader(this.loader_); sourceTile.load(); } if (sourceTile.state == ol.TileState.LOADING) { var key = ol.events.listen(sourceTile, ol.events.EventType.CHANGE, function(e) { var state = sourceTile.getState(); if (state == ol.TileState.LOADED || state == ol.TileState.ERROR) { var uid = ol.getUid(sourceTile); if (state == ol.TileState.ERROR) { errorSourceTiles[uid] = true; } else { --leftToLoad; delete errorSourceTiles[uid]; } if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { this.finishLoading_(); } } }.bind(this)); this.loadListenerKeys_.push(key); ++leftToLoad; } }.bind(this)); } if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { setTimeout(this.finishLoading_.bind(this), 0); } }; /** * @private */ ol.VectorImageTile.prototype.finishLoading_ = function() { var loaded = this.tileKeys.length; var empty = 0; for (var i = loaded - 1; i >= 0; --i) { var state = this.getTile(this.tileKeys[i]).getState(); if (state != ol.TileState.LOADED) { --loaded; } if (state == ol.TileState.EMPTY) { ++empty; } } if (loaded == this.tileKeys.length) { this.loadListenerKeys_.forEach(ol.events.unlistenByKey); this.loadListenerKeys_.length = 0; this.setState(ol.TileState.LOADED); } else { this.setState(empty == this.tileKeys.length ? ol.TileState.EMPTY : ol.TileState.ERROR); } }; /** * Sets the loader for a tile. * @param {ol.VectorTile} tile Vector tile. * @param {string} url URL. */ ol.VectorImageTile.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.VectorTile'); goog.require('ol'); goog.require('ol.Tile'); goog.require('ol.TileState'); /** * @constructor * @extends {ol.Tile} * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileState} state State. * @param {string} src Data source url. * @param {ol.format.Feature} format Feature format. * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. * @param {olx.TileOptions=} opt_options Tile options. */ ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction, opt_options) { ol.Tile.call(this, tileCoord, state, opt_options); /** * @type {number} */ this.consumers = 0; /** * @private * @type {ol.Extent} */ this.extent_ = null; /** * @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 {Object.<string, ol.render.ReplayGroup>} */ this.replayGroups_ = {}; /** * @private * @type {ol.TileLoadFunctionType} */ this.tileLoadFunction_ = tileLoadFunction; /** * @private * @type {string} */ this.url_ = src; }; ol.inherits(ol.VectorTile, ol.Tile); /** * @inheritDoc */ ol.VectorTile.prototype.disposeInternal = function() { this.features_ = null; this.replayGroups_ = {}; this.state = ol.TileState.ABORT; this.changed(); ol.Tile.prototype.disposeInternal.call(this); }; /** * Gets the extent of the vector tile. * @return {ol.Extent} The extent. * @api */ ol.VectorTile.prototype.getExtent = function() { return this.extent_ || ol.VectorTile.DEFAULT_EXTENT; }; /** * 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_; }; /** * Get the features for this tile. Geometries will be in the projection returned * by {@link ol.VectorTile#getProjection}. * @return {Array.<ol.Feature|ol.render.Feature>} Features. * @api */ ol.VectorTile.prototype.getFeatures = function() { return this.features_; }; /** * @inheritDoc */ ol.VectorTile.prototype.getKey = function() { return this.url_; }; /** * Get the feature projection of features returned by * {@link ol.VectorTile#getFeatures}. * @return {ol.proj.Projection} Feature projection. * @api */ ol.VectorTile.prototype.getProjection = function() { return this.projection_; }; /** * @param {ol.layer.Layer} layer Layer. * @param {string} key Key. * @return {ol.render.ReplayGroup} Replay group. */ ol.VectorTile.prototype.getReplayGroup = function(layer, key) { return this.replayGroups_[ol.getUid(layer) + ',' + key]; }; /** * @inheritDoc */ ol.VectorTile.prototype.load = function() { if (this.state == ol.TileState.IDLE) { this.setState(ol.TileState.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. * @param {ol.Extent} extent Extent. */ ol.VectorTile.prototype.onLoad = function(features, dataProjection, extent) { this.setProjection(dataProjection); this.setFeatures(features); this.setExtent(extent); }; /** * Handler for tile load errors. */ ol.VectorTile.prototype.onError = function() { this.setState(ol.TileState.ERROR); }; /** * Function for use in an {@link ol.source.VectorTile}'s `tileLoadFunction`. * Sets the extent of the vector tile. This is only required for tiles in * projections with `tile-pixels` as units. The extent should be set to * `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is calculated * by multiplying the tile size with the tile pixel ratio. For sources using * {@link ol.format.MVT} as feature format, the * {@link ol.format.MVT#getLastExtent} method will return the correct extent. * The default is `[0, 0, 4096, 4096]`. * @param {ol.Extent} extent The extent. * @api */ ol.VectorTile.prototype.setExtent = function(extent) { this.extent_ = extent; }; /** * Function for use in an {@link ol.source.VectorTile}'s `tileLoadFunction`. * Sets the features for the tile. * @param {Array.<ol.Feature>} features Features. * @api */ ol.VectorTile.prototype.setFeatures = function(features) { this.features_ = features; this.setState(ol.TileState.LOADED); }; /** * Function for use in an {@link ol.source.VectorTile}'s `tileLoadFunction`. * Sets the projection of the features that were added with * {@link ol.VectorTile#setFeatures}. * @param {ol.proj.Projection} projection Feature projection. * @api */ ol.VectorTile.prototype.setProjection = function(projection) { this.projection_ = projection; }; /** * @param {ol.layer.Layer} layer Layer. * @param {string} key Key. * @param {ol.render.ReplayGroup} replayGroup Replay group. */ ol.VectorTile.prototype.setReplayGroup = function(layer, key, replayGroup) { this.replayGroups_[ol.getUid(layer) + ',' + key] = replayGroup; }; /** * 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; }; /** * @const * @type {ol.Extent} */ ol.VectorTile.DEFAULT_EXTENT = [0, 0, 4096, 4096]; goog.provide('ol.source.VectorTile'); goog.require('ol'); goog.require('ol.TileState'); goog.require('ol.VectorImageTile'); goog.require('ol.VectorTile'); goog.require('ol.size'); goog.require('ol.source.UrlTile'); goog.require('ol.tilecoord'); goog.require('ol.tilegrid'); /** * @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) { var projection = options.projection || 'EPSG:3857'; var extent = options.extent || ol.tilegrid.extentFromProjection(projection); var tileGrid = options.tileGrid || ol.tilegrid.createXYZ({ extent: extent, maxZoom: options.maxZoom || 22, minZoom: options.minZoom, tileSize: options.tileSize || 512 }); ol.source.UrlTile.call(this, { attributions: options.attributions, cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128, extent: extent, logo: options.logo, opaque: false, projection: projection, state: options.state, tileGrid: tileGrid, tileLoadFunction: options.tileLoadFunction ? options.tileLoadFunction : ol.VectorImageTile.defaultLoadFunction, tileUrlFunction: options.tileUrlFunction, url: options.url, urls: options.urls, wrapX: options.wrapX === undefined ? true : options.wrapX, transition: options.transition }); /** * @private * @type {ol.format.Feature} */ this.format_ = options.format ? options.format : null; /** * @private * @type {Object.<string,ol.VectorTile>} */ this.sourceTiles_ = {}; /** * @private * @type {boolean} */ this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; /** * @protected * @type {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string, * ol.format.Feature, ol.TileLoadFunctionType)} */ this.tileClass = options.tileClass ? options.tileClass : ol.VectorTile; /** * @private * @type {Object.<string,ol.tilegrid.TileGrid>} */ this.tileGrids_ = {}; }; 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_; }; /** * clear {@link ol.TileCache} and delete all source tiles * @api */ ol.source.VectorTile.prototype.clear = function() { this.tileCache.clear(); this.sourceTiles_ = {}; }; /** * @inheritDoc */ ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projection) { var tileCoordKey = ol.tilecoord.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 tile = new ol.VectorImageTile( tileCoord, urlTileCoord !== null ? ol.TileState.IDLE : ol.TileState.EMPTY, this.getRevision(), this.format_, this.tileLoadFunction, urlTileCoord, this.tileUrlFunction, this.tileGrid, this.getTileGridForProjection(projection), this.sourceTiles_, pixelRatio, projection, this.tileClass, this.handleTileChange.bind(this), this.tileOptions); this.tileCache.set(tileCoordKey, tile); return tile; } }; /** * @inheritDoc */ ol.source.VectorTile.prototype.getTileGridForProjection = function(projection) { var code = projection.getCode(); var tileGrid = this.tileGrids_[code]; if (!tileGrid) { // A tile grid that matches the tile size of the source tile grid is more // likely to have 1:1 relationships between source tiles and rendered tiles. var sourceTileGrid = this.tileGrid; tileGrid = this.tileGrids_[code] = ol.tilegrid.createForProjection(projection, undefined, sourceTileGrid ? sourceTileGrid.getTileSize(sourceTileGrid.getMinZoom()) : undefined); } return tileGrid; }; /** * @inheritDoc */ ol.source.VectorTile.prototype.getTilePixelRatio = function(pixelRatio) { return pixelRatio; }; /** * @inheritDoc */ ol.source.VectorTile.prototype.getTilePixelSize = function(z, pixelRatio, projection) { var tileSize = ol.size.toSize(this.getTileGridForProjection(projection).getTileSize(z)); return [Math.round(tileSize[0] * pixelRatio), Math.round(tileSize[1] * pixelRatio)]; }; goog.provide('ol.source.WMTSRequestEncoding'); /** * Request encoding. One of 'KVP', 'REST'. * @enum {string} */ ol.source.WMTSRequestEncoding = { KVP: 'KVP', // see spec §8 REST: 'REST' // see spec §10 }; 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) { /** * @private * @type {!Array.<string>} */ this.matrixIds_ = options.matrixIds; // FIXME: should the matrixIds become optional? 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) { 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 code = matrixSet[supportedCRSPropName]; var projection = ol.proj.get(code.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')) || ol.proj.get(code); 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.source.WMTSRequestEncoding'); 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 */ 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.WMTSRequestEncoding} */ this.requestEncoding_ = options.requestEncoding !== undefined ? /** @type {ol.source.WMTSRequestEncoding} */ (options.requestEncoding) : ol.source.WMTSRequestEncoding.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.WMTSRequestEncoding.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. * @private */ this.createFromWMTSTemplate_ = function(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.WMTSRequestEncoding.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.WMTSRequestEncoding.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(this.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, transition: options.transition }); this.setKey(this.getKeyForDimensions_()); }; ol.inherits(ol.source.WMTS, ol.source.TileImage); /** * Set the URLs to use for requests. * URLs may contain OCG conform URL Template Variables: {TileMatrix}, {TileRow}, {TileCol}. * @override */ ol.source.WMTS.prototype.setUrls = function(urls) { this.urls = urls; var key = urls.join('\n'); this.setTileUrlFunction(this.fixedTileUrlFunction ? this.fixedTileUrlFunction.bind(this) : ol.TileUrlFunction.createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate_.bind(this))), key); }; /** * 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.WMTSRequestEncoding} 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. * - crossOrigin - {string|null|undefined} Cross origin. Default is `undefined`. * @return {?olx.source.WMTSOptions} WMTS source options object or `null` if the layer was not found. * @api */ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { var layers = wmtsCap['Contents']['Layer']; var l = ol.array.find(layers, function(elt, index, array) { return elt['Identifier'] == config['layer']; }); if (l === null) { return null; } 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']; var proj1 = ol.proj.get(supportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')) || 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']); 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) { value = elt['Value'][0]; } dimensions[key] = value; }); } var matrixSets = wmtsCap['Contents']['TileMatrixSet']; var matrixSetObj = ol.array.find(matrixSets, function(elt, index, array) { return elt['Identifier'] == matrixSet; }); var projection; var code = matrixSetObj['SupportedCRS']; if (code) { projection = ol.proj.get(code.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')) || ol.proj.get(code); } if ('projection' in config) { var projConfig = ol.proj.get(config['projection']); if (projConfig) { if (!projection || ol.proj.equivalent(projConfig, projection)) { projection = projConfig; } } } 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 : ''; if ('OperationsMetadata' in wmtsCap && 'GetTile' in wmtsCap['OperationsMetadata']) { var gets = wmtsCap['OperationsMetadata']['GetTile']['DCP']['HTTP']['Get']; for (var i = 0, ii = gets.length; i < ii; ++i) { if (gets[i]['Constraint']) { var constraint = ol.array.find(gets[i]['Constraint'], function(element) { return element['name'] == 'GetEncoding'; }); var encodings = constraint['AllowedValues']['Value']; if (requestEncoding === '') { // requestEncoding not provided, use the first encoding from the list requestEncoding = encodings[0]; } if (requestEncoding === ol.source.WMTSRequestEncoding.KVP) { if (ol.array.includes(encodings, ol.source.WMTSRequestEncoding.KVP)) { urls.push(/** @type {string} */ (gets[i]['href'])); } } else { break; } } else if (gets[i]['href']) { requestEncoding = ol.source.WMTSRequestEncoding.KVP; urls.push(/** @type {string} */ (gets[i]['href'])); } } } if (urls.length === 0) { requestEncoding = ol.source.WMTSRequestEncoding.REST; l['ResourceURL'].forEach(function(element) { if (element['resourceType'] === 'tile') { format = element['format']; urls.push(/** @type {string} */ (element['template'])); } }); } return { urls: urls, layer: config['layer'], matrixSet: matrixSet, format: format, projection: projection, requestEncoding: requestEncoding, tileGrid: tileGrid, style: style, dimensions: dimensions, wrapX: wrapX, crossOrigin: config['crossOrigin'] }; }; goog.provide('ol.source.Zoomify'); goog.require('ol'); goog.require('ol.ImageTile'); goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); goog.require('ol.asserts'); goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.size'); goog.require('ol.source.TileImage'); goog.require('ol.tilegrid.TileGrid'); /** * @classdesc * Layer source for tile data in Zoomify format (both Zoomify and Internet * Imaging Protocol are supported). * * @constructor * @extends {ol.source.TileImage} * @param {olx.source.ZoomifyOptions=} opt_options Options. * @api */ 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 extent = options.extent || [0, -size[1], size[0], 0]; var tierSizeInTiles = []; var tileSize = options.tileSize || ol.DEFAULT_TILE_SIZE; var tileSizeForTierSizeCalculation = tileSize; switch (tierSizeCalculation) { case ol.source.Zoomify.TierSizeCalculation_.DEFAULT: while (imageWidth > tileSizeForTierSizeCalculation || imageHeight > tileSizeForTierSizeCalculation) { tierSizeInTiles.push([ Math.ceil(imageWidth / tileSizeForTierSizeCalculation), Math.ceil(imageHeight / tileSizeForTierSizeCalculation) ]); tileSizeForTierSizeCalculation += tileSizeForTierSizeCalculation; } break; case ol.source.Zoomify.TierSizeCalculation_.TRUNCATED: var width = imageWidth; var height = imageHeight; while (width > tileSizeForTierSizeCalculation || height > tileSizeForTierSizeCalculation) { tierSizeInTiles.push([ Math.ceil(width / tileSizeForTierSizeCalculation), Math.ceil(height / tileSizeForTierSizeCalculation) ]); 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 tileGrid = new ol.tilegrid.TileGrid({ tileSize: tileSize, extent: extent, origin: ol.extent.getTopLeft(extent), resolutions: resolutions }); var url = options.url; if (url && url.indexOf('{TileGroup}') == -1 && url.indexOf('{tileIndex}') == -1) { url += '{TileGroup}/{z}-{x}-{y}.jpg'; } var urls = ol.TileUrlFunction.expandUrl(url); /** * @param {string} template Template. * @return {ol.TileUrlFunctionType} Tile URL function. */ function createFromTemplate(template) { 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 tileCoordZ = tileCoord[0]; var tileCoordX = tileCoord[1]; var tileCoordY = -tileCoord[2] - 1; var tileIndex = tileCoordX + tileCoordY * tierSizeInTiles[tileCoordZ][0]; var tileSize = tileGrid.getTileSize(tileCoordZ); var tileGroup = ((tileIndex + tileCountUpToTier[tileCoordZ]) / tileSize) | 0; var localContext = { 'z': tileCoordZ, 'x': tileCoordX, 'y': tileCoordY, 'tileIndex': tileIndex, 'TileGroup': 'TileGroup' + tileGroup }; return template.replace(/\{(\w+?)\}/g, function(m, p) { return localContext[p]; }); } }); } var tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions(urls.map(createFromTemplate)); var ZoomifyTileClass = ol.source.Zoomify.Tile_.bind(null, tileGrid); ol.source.TileImage.call(this, { attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, logo: options.logo, projection: options.projection, reprojectionErrorThreshold: options.reprojectionErrorThreshold, tileClass: ZoomifyTileClass, tileGrid: tileGrid, tileUrlFunction: tileUrlFunction, transition: options.transition }); }; ol.inherits(ol.source.Zoomify, ol.source.TileImage); /** * @constructor * @extends {ol.ImageTile} * @param {ol.tilegrid.TileGrid} tileGrid TileGrid that the tile belongs to. * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileState} state State. * @param {string} src Image source URI. * @param {?string} crossOrigin Cross origin. * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. * @param {olx.TileOptions=} opt_options Tile options. * @private */ ol.source.Zoomify.Tile_ = function( tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { ol.ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); /** * @private * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ this.zoomifyImage_ = null; /** * @private * @type {ol.Size} */ this.tileSize_ = ol.size.toSize(tileGrid.getTileSize(tileCoord[0])); }; ol.inherits(ol.source.Zoomify.Tile_, ol.ImageTile); /** * @inheritDoc */ ol.source.Zoomify.Tile_.prototype.getImage = function() { if (this.zoomifyImage_) { return this.zoomifyImage_; } var image = ol.ImageTile.prototype.getImage.call(this); if (this.state == ol.TileState.LOADED) { var tileSize = this.tileSize_; if (image.width == tileSize[0] && image.height == tileSize[1]) { this.zoomifyImage_ = image; return image; } else { var context = ol.dom.createCanvasContext2D(tileSize[0], tileSize[1]); context.drawImage(image, 0, 0); this.zoomifyImage_ = context.canvas; return context.canvas; } } else { return image; } }; /** * @enum {string} * @private */ ol.source.Zoomify.TierSizeCalculation_ = { DEFAULT: 'default', TRUNCATED: 'truncated' }; /** * @fileoverview * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} */ goog.provide('ol.ext.vectortile.VectorTile'); /** @typedef {function(*)} */ ol.ext.vectortile.VectorTile = function() {}; (function() {(function (exports) { 'use strict'; var index$2 = 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); }, multByPoint: function(p) { return this.clone()._multByPoint(p); }, divByPoint: function(p) { return this.clone()._divByPoint(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); }, rotateAround: function(a,p) { return this.clone()._rotateAround(a,p); }, 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(other) { return this.x === other.x && this.y === other.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); }, 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; }, _multByPoint: function(p) { this.x *= p.x; this.y *= p.y; return this; }, _divByPoint: function(p) { this.x /= p.x; this.y /= p.y; 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; }, _rotateAround: function(angle, p) { var cos = Math.cos(angle), sin = Math.sin(angle), x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y), y = p.y + sin * (this.x - p.x) + cos * (this.y - p.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; } }; Point.convert = function (a) { if (a instanceof Point) { return a; } if (Array.isArray(a)) { return new Point(a[0], a[1]); } return a; }; var vectortilefeature = VectorTileFeature$1; function VectorTileFeature$1(pbf, end, extent, keys, values) { this.properties = {}; this.extent = extent; this.type = 0; 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$1.types = ['Unknown', 'Point', 'LineString', 'Polygon']; VectorTileFeature$1.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) { if (line) lines.push(line); line = []; } line.push(new index$2(x, y)); } else if (cmd === 7) { if (line) { line.push(line[0].clone()); } } else { throw new Error('unknown command ' + cmd); } } if (line) lines.push(line); return lines; }; VectorTileFeature$1.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$1.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$1.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; }; 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; } var vectortilelayer = VectorTileLayer$1; function VectorTileLayer$1(pbf, end) { this.version = 1; this.name = null; this.extent = 4096; this.length = 0; 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; } VectorTileLayer$1.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); }; var vectortile = VectorTile$1; function VectorTile$1(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; } } var VectorTile = vectortile; var VectorTileFeature = vectortilefeature; var VectorTileLayer = vectortilelayer; var index = { VectorTile: VectorTile, VectorTileFeature: VectorTileFeature, VectorTileLayer: VectorTileLayer }; exports['default'] = index; exports.VectorTile = VectorTile; exports.VectorTileFeature = VectorTileFeature; exports.VectorTileLayer = VectorTileLayer; }((this.vectortile = this.vectortile || {})));}).call(ol.ext); // 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.CanvasMap'); 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.PluggableMap'); goog.require('ol.Sphere'); goog.require('ol.Tile'); goog.require('ol.VectorTile'); goog.require('ol.View'); 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.Contains'); goog.require('ol.format.filter.During'); 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.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.Map'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); goog.require('ol.renderer.canvas.VectorTileLayer'); goog.require('ol.renderer.webgl.ImageLayer'); goog.require('ol.renderer.webgl.Map'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VectorLayer'); 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'); goog.require('ol.style.AtlasManager'); goog.require('ol.style.Circle'); goog.require('ol.style.Fill'); goog.require('ol.style.Icon'); goog.require('ol.style.IconImageCache'); 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.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.CanvasMap', ol.CanvasMap, OPENLAYERS); 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.control.defaults', ol.control.defaults, 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.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.getArea', ol.extent.getArea, 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.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.exportSymbol( 'ol.inherits', ol.inherits, OPENLAYERS); goog.exportSymbol( 'ol.interaction.defaults', ol.interaction.defaults, 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.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.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.PluggableMap', ol.PluggableMap, OPENLAYERS); goog.exportProperty( ol.PluggableMap.prototype, 'addControl', ol.PluggableMap.prototype.addControl); goog.exportProperty( ol.PluggableMap.prototype, 'addInteraction', ol.PluggableMap.prototype.addInteraction); goog.exportProperty( ol.PluggableMap.prototype, 'addLayer', ol.PluggableMap.prototype.addLayer); goog.exportProperty( ol.PluggableMap.prototype, 'addOverlay', ol.PluggableMap.prototype.addOverlay); goog.exportProperty( ol.PluggableMap.prototype, 'forEachFeatureAtPixel', ol.PluggableMap.prototype.forEachFeatureAtPixel); goog.exportProperty( ol.PluggableMap.prototype, 'getFeaturesAtPixel', ol.PluggableMap.prototype.getFeaturesAtPixel); goog.exportProperty( ol.PluggableMap.prototype, 'forEachLayerAtPixel', ol.PluggableMap.prototype.forEachLayerAtPixel); goog.exportProperty( ol.PluggableMap.prototype, 'hasFeatureAtPixel', ol.PluggableMap.prototype.hasFeatureAtPixel); goog.exportProperty( ol.PluggableMap.prototype, 'getEventCoordinate', ol.PluggableMap.prototype.getEventCoordinate); goog.exportProperty( ol.PluggableMap.prototype, 'getEventPixel', ol.PluggableMap.prototype.getEventPixel); goog.exportProperty( ol.PluggableMap.prototype, 'getTarget', ol.PluggableMap.prototype.getTarget); goog.exportProperty( ol.PluggableMap.prototype, 'getTargetElement', ol.PluggableMap.prototype.getTargetElement); goog.exportProperty( ol.PluggableMap.prototype, 'getCoordinateFromPixel', ol.PluggableMap.prototype.getCoordinateFromPixel); goog.exportProperty( ol.PluggableMap.prototype, 'getControls', ol.PluggableMap.prototype.getControls); goog.exportProperty( ol.PluggableMap.prototype, 'getOverlays', ol.PluggableMap.prototype.getOverlays); goog.exportProperty( ol.PluggableMap.prototype, 'getOverlayById', ol.PluggableMap.prototype.getOverlayById); goog.exportProperty( ol.PluggableMap.prototype, 'getInteractions', ol.PluggableMap.prototype.getInteractions); goog.exportProperty( ol.PluggableMap.prototype, 'getLayerGroup', ol.PluggableMap.prototype.getLayerGroup); goog.exportProperty( ol.PluggableMap.prototype, 'getLayers', ol.PluggableMap.prototype.getLayers); goog.exportProperty( ol.PluggableMap.prototype, 'getPixelFromCoordinate', ol.PluggableMap.prototype.getPixelFromCoordinate); goog.exportProperty( ol.PluggableMap.prototype, 'getSize', ol.PluggableMap.prototype.getSize); goog.exportProperty( ol.PluggableMap.prototype, 'getView', ol.PluggableMap.prototype.getView); goog.exportProperty( ol.PluggableMap.prototype, 'getViewport', ol.PluggableMap.prototype.getViewport); goog.exportProperty( ol.PluggableMap.prototype, 'renderSync', ol.PluggableMap.prototype.renderSync); goog.exportProperty( ol.PluggableMap.prototype, 'render', ol.PluggableMap.prototype.render); goog.exportProperty( ol.PluggableMap.prototype, 'removeControl', ol.PluggableMap.prototype.removeControl); goog.exportProperty( ol.PluggableMap.prototype, 'removeInteraction', ol.PluggableMap.prototype.removeInteraction); goog.exportProperty( ol.PluggableMap.prototype, 'removeLayer', ol.PluggableMap.prototype.removeLayer); goog.exportProperty( ol.PluggableMap.prototype, 'removeOverlay', ol.PluggableMap.prototype.removeOverlay); goog.exportProperty( ol.PluggableMap.prototype, 'setLayerGroup', ol.PluggableMap.prototype.setLayerGroup); goog.exportProperty( ol.PluggableMap.prototype, 'setSize', ol.PluggableMap.prototype.setSize); goog.exportProperty( ol.PluggableMap.prototype, 'setTarget', ol.PluggableMap.prototype.setTarget); goog.exportProperty( ol.PluggableMap.prototype, 'setView', ol.PluggableMap.prototype.setView); goog.exportProperty( ol.PluggableMap.prototype, 'updateSize', ol.PluggableMap.prototype.updateSize); 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.render.toContext', ol.render.toContext, OPENLAYERS); goog.exportSymbol( 'ol.size.toSize', ol.size.toSize, OPENLAYERS); 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.Sphere.getLength', ol.Sphere.getLength, OPENLAYERS); goog.exportSymbol( 'ol.Sphere.getArea', ol.Sphere.getArea, OPENLAYERS); goog.exportSymbol( 'ol.style.iconImageCache', ol.style.iconImageCache, OPENLAYERS); goog.exportProperty( ol.Tile.prototype, 'getTileCoord', ol.Tile.prototype.getTileCoord); goog.exportProperty( ol.Tile.prototype, 'load', ol.Tile.prototype.load); goog.exportSymbol( 'ol.tilegrid.createXYZ', ol.tilegrid.createXYZ, OPENLAYERS); goog.exportProperty( ol.VectorTile.prototype, 'getExtent', ol.VectorTile.prototype.getExtent); goog.exportProperty( ol.VectorTile.prototype, 'getFormat', ol.VectorTile.prototype.getFormat); goog.exportProperty( ol.VectorTile.prototype, 'getFeatures', ol.VectorTile.prototype.getFeatures); goog.exportProperty( ol.VectorTile.prototype, 'getProjection', ol.VectorTile.prototype.getProjection); goog.exportProperty( ol.VectorTile.prototype, 'setExtent', ol.VectorTile.prototype.setExtent); 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, 'getAnimating', ol.View.prototype.getAnimating); goog.exportProperty( ol.View.prototype, 'getInteracting', ol.View.prototype.getInteracting); goog.exportProperty( ol.View.prototype, 'cancelAnimations', ol.View.prototype.cancelAnimations); 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, 'getMaxZoom', ol.View.prototype.getMaxZoom); goog.exportProperty( ol.View.prototype, 'setMaxZoom', ol.View.prototype.setMaxZoom); goog.exportProperty( ol.View.prototype, 'getMinZoom', ol.View.prototype.getMinZoom); goog.exportProperty( ol.View.prototype, 'setMinZoom', ol.View.prototype.setMinZoom); 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, 'getResolutionForExtent', ol.View.prototype.getResolutionForExtent); 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, 'getZoomForResolution', ol.View.prototype.getZoomForResolution); goog.exportProperty( ol.View.prototype, 'getResolutionForZoom', ol.View.prototype.getResolutionForZoom); 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.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, '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.exportProperty( ol.style.IconImageCache.prototype, 'setSize', ol.style.IconImageCache.prototype.setSize); 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, 'getLineDashOffset', ol.style.Stroke.prototype.getLineDashOffset); 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, 'setLineDashOffset', ol.style.Stroke.prototype.setLineDashOffset); 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, 'getRenderer', ol.style.Style.prototype.getRenderer); goog.exportProperty( ol.style.Style.prototype, 'setRenderer', ol.style.Style.prototype.setRenderer); 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, 'getOverflow', ol.style.Text.prototype.getOverflow); goog.exportProperty( ol.style.Text.prototype, 'getFont', ol.style.Text.prototype.getFont); goog.exportProperty( ol.style.Text.prototype, 'getMaxAngle', ol.style.Text.prototype.getMaxAngle); goog.exportProperty( ol.style.Text.prototype, 'getPlacement', ol.style.Text.prototype.getPlacement); 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, 'getBackgroundFill', ol.style.Text.prototype.getBackgroundFill); goog.exportProperty( ol.style.Text.prototype, 'getBackgroundStroke', ol.style.Text.prototype.getBackgroundStroke); goog.exportProperty( ol.style.Text.prototype, 'getPadding', ol.style.Text.prototype.getPadding); goog.exportProperty( ol.style.Text.prototype, 'setOverflow', ol.style.Text.prototype.setOverflow); goog.exportProperty( ol.style.Text.prototype, 'setFont', ol.style.Text.prototype.setFont); goog.exportProperty( ol.style.Text.prototype, 'setMaxAngle', ol.style.Text.prototype.setMaxAngle); 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, 'setPlacement', ol.style.Text.prototype.setPlacement); 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.exportProperty( ol.style.Text.prototype, 'setBackgroundFill', ol.style.Text.prototype.setBackgroundFill); goog.exportProperty( ol.style.Text.prototype, 'setBackgroundStroke', ol.style.Text.prototype.setBackgroundStroke); goog.exportProperty( ol.style.Text.prototype, 'setPadding', ol.style.Text.prototype.setPadding); 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, 'getDistance', ol.source.Cluster.prototype.getDistance); 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, 'removeLoadedExtent', ol.source.Vector.prototype.removeLoadedExtent); goog.exportProperty( ol.source.Vector.prototype, 'removeFeature', ol.source.Vector.prototype.removeFeature); goog.exportProperty( ol.source.Vector.prototype, 'setLoader', ol.source.Vector.prototype.setLoader); goog.exportProperty( ol.source.Vector.Event.prototype, 'feature', ol.source.Vector.Event.prototype.feature); goog.exportSymbol( 'ol.source.VectorTile', ol.source.VectorTile, OPENLAYERS); goog.exportProperty( ol.source.VectorTile.prototype, 'clear', ol.source.VectorTile.prototype.clear); 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.exportSymbol( 'ol.renderer.webgl.ImageLayer', ol.renderer.webgl.ImageLayer, OPENLAYERS); goog.exportSymbol( 'ol.renderer.webgl.Map', ol.renderer.webgl.Map, OPENLAYERS); goog.exportSymbol( 'ol.renderer.webgl.TileLayer', ol.renderer.webgl.TileLayer, OPENLAYERS); goog.exportSymbol( 'ol.renderer.webgl.VectorLayer', ol.renderer.webgl.VectorLayer, OPENLAYERS); goog.exportSymbol( 'ol.renderer.canvas.ImageLayer', ol.renderer.canvas.ImageLayer, OPENLAYERS); goog.exportSymbol( 'ol.renderer.canvas.Map', ol.renderer.canvas.Map, OPENLAYERS); goog.exportSymbol( 'ol.renderer.canvas.TileLayer', ol.renderer.canvas.TileLayer, OPENLAYERS); goog.exportSymbol( 'ol.renderer.canvas.VectorLayer', ol.renderer.canvas.VectorLayer, OPENLAYERS); goog.exportSymbol( 'ol.renderer.canvas.VectorTileLayer', ol.renderer.canvas.VectorTileLayer, 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, 'getId', ol.render.Feature.prototype.getId); 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.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, 'getAxisOrientation', ol.proj.Projection.prototype.getAxisOrientation); 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.exportProperty( ol.layer.VectorTile.prototype, 'getSource', ol.layer.VectorTile.prototype.getSource); 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.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.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.contains', ol.format.filter.contains, 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.during', ol.format.filter.during, 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, 'getLastExtent', ol.format.MVT.prototype.getLastExtent); 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.exportSymbol( 'ol.format.WFS.writeFilter', ol.format.WFS.writeFilter, OPENLAYERS); 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.Contains', ol.format.filter.Contains, OPENLAYERS); goog.exportSymbol( 'ol.format.filter.During', ol.format.filter.During, 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.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.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.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.PluggableMap.prototype, 'get', ol.PluggableMap.prototype.get); goog.exportProperty( ol.PluggableMap.prototype, 'getKeys', ol.PluggableMap.prototype.getKeys); goog.exportProperty( ol.PluggableMap.prototype, 'getProperties', ol.PluggableMap.prototype.getProperties); goog.exportProperty( ol.PluggableMap.prototype, 'set', ol.PluggableMap.prototype.set); goog.exportProperty( ol.PluggableMap.prototype, 'setProperties', ol.PluggableMap.prototype.setProperties); goog.exportProperty( ol.PluggableMap.prototype, 'unset', ol.PluggableMap.prototype.unset); goog.exportProperty( ol.PluggableMap.prototype, 'changed', ol.PluggableMap.prototype.changed); goog.exportProperty( ol.PluggableMap.prototype, 'dispatchEvent', ol.PluggableMap.prototype.dispatchEvent); goog.exportProperty( ol.PluggableMap.prototype, 'getRevision', ol.PluggableMap.prototype.getRevision); goog.exportProperty( ol.PluggableMap.prototype, 'on', ol.PluggableMap.prototype.on); goog.exportProperty( ol.PluggableMap.prototype, 'once', ol.PluggableMap.prototype.once); goog.exportProperty( ol.PluggableMap.prototype, 'un', ol.PluggableMap.prototype.un); goog.exportProperty( ol.CanvasMap.prototype, 'addControl', ol.CanvasMap.prototype.addControl); goog.exportProperty( ol.CanvasMap.prototype, 'addInteraction', ol.CanvasMap.prototype.addInteraction); goog.exportProperty( ol.CanvasMap.prototype, 'addLayer', ol.CanvasMap.prototype.addLayer); goog.exportProperty( ol.CanvasMap.prototype, 'addOverlay', ol.CanvasMap.prototype.addOverlay); goog.exportProperty( ol.CanvasMap.prototype, 'forEachFeatureAtPixel', ol.CanvasMap.prototype.forEachFeatureAtPixel); goog.exportProperty( ol.CanvasMap.prototype, 'getFeaturesAtPixel', ol.CanvasMap.prototype.getFeaturesAtPixel); goog.exportProperty( ol.CanvasMap.prototype, 'forEachLayerAtPixel', ol.CanvasMap.prototype.forEachLayerAtPixel); goog.exportProperty( ol.CanvasMap.prototype, 'hasFeatureAtPixel', ol.CanvasMap.prototype.hasFeatureAtPixel); goog.exportProperty( ol.CanvasMap.prototype, 'getEventCoordinate', ol.CanvasMap.prototype.getEventCoordinate); goog.exportProperty( ol.CanvasMap.prototype, 'getEventPixel', ol.CanvasMap.prototype.getEventPixel); goog.exportProperty( ol.CanvasMap.prototype, 'getTarget', ol.CanvasMap.prototype.getTarget); goog.exportProperty( ol.CanvasMap.prototype, 'getTargetElement', ol.CanvasMap.prototype.getTargetElement); goog.exportProperty( ol.CanvasMap.prototype, 'getCoordinateFromPixel', ol.CanvasMap.prototype.getCoordinateFromPixel); goog.exportProperty( ol.CanvasMap.prototype, 'getControls', ol.CanvasMap.prototype.getControls); goog.exportProperty( ol.CanvasMap.prototype, 'getOverlays', ol.CanvasMap.prototype.getOverlays); goog.exportProperty( ol.CanvasMap.prototype, 'getOverlayById', ol.CanvasMap.prototype.getOverlayById); goog.exportProperty( ol.CanvasMap.prototype, 'getInteractions', ol.CanvasMap.prototype.getInteractions); goog.exportProperty( ol.CanvasMap.prototype, 'getLayerGroup', ol.CanvasMap.prototype.getLayerGroup); goog.exportProperty( ol.CanvasMap.prototype, 'getLayers', ol.CanvasMap.prototype.getLayers); goog.exportProperty( ol.CanvasMap.prototype, 'getPixelFromCoordinate', ol.CanvasMap.prototype.getPixelFromCoordinate); goog.exportProperty( ol.CanvasMap.prototype, 'getSize', ol.CanvasMap.prototype.getSize); goog.exportProperty( ol.CanvasMap.prototype, 'getView', ol.CanvasMap.prototype.getView); goog.exportProperty( ol.CanvasMap.prototype, 'getViewport', ol.CanvasMap.prototype.getViewport); goog.exportProperty( ol.CanvasMap.prototype, 'renderSync', ol.CanvasMap.prototype.renderSync); goog.exportProperty( ol.CanvasMap.prototype, 'render', ol.CanvasMap.prototype.render); goog.exportProperty( ol.CanvasMap.prototype, 'removeControl', ol.CanvasMap.prototype.removeControl); goog.exportProperty( ol.CanvasMap.prototype, 'removeInteraction', ol.CanvasMap.prototype.removeInteraction); goog.exportProperty( ol.CanvasMap.prototype, 'removeLayer', ol.CanvasMap.prototype.removeLayer); goog.exportProperty( ol.CanvasMap.prototype, 'removeOverlay', ol.CanvasMap.prototype.removeOverlay); goog.exportProperty( ol.CanvasMap.prototype, 'setLayerGroup', ol.CanvasMap.prototype.setLayerGroup); goog.exportProperty( ol.CanvasMap.prototype, 'setSize', ol.CanvasMap.prototype.setSize); goog.exportProperty( ol.CanvasMap.prototype, 'setTarget', ol.CanvasMap.prototype.setTarget); goog.exportProperty( ol.CanvasMap.prototype, 'setView', ol.CanvasMap.prototype.setView); goog.exportProperty( ol.CanvasMap.prototype, 'updateSize', ol.CanvasMap.prototype.updateSize); goog.exportProperty( ol.CanvasMap.prototype, 'get', ol.CanvasMap.prototype.get); goog.exportProperty( ol.CanvasMap.prototype, 'getKeys', ol.CanvasMap.prototype.getKeys); goog.exportProperty( ol.CanvasMap.prototype, 'getProperties', ol.CanvasMap.prototype.getProperties); goog.exportProperty( ol.CanvasMap.prototype, 'set', ol.CanvasMap.prototype.set); goog.exportProperty( ol.CanvasMap.prototype, 'setProperties', ol.CanvasMap.prototype.setProperties); goog.exportProperty( ol.CanvasMap.prototype, 'unset', ol.CanvasMap.prototype.unset); goog.exportProperty( ol.CanvasMap.prototype, 'changed', ol.CanvasMap.prototype.changed); goog.exportProperty( ol.CanvasMap.prototype, 'dispatchEvent', ol.CanvasMap.prototype.dispatchEvent); goog.exportProperty( ol.CanvasMap.prototype, 'getRevision', ol.CanvasMap.prototype.getRevision); goog.exportProperty( ol.CanvasMap.prototype, 'on', ol.CanvasMap.prototype.on); goog.exportProperty( ol.CanvasMap.prototype, 'once', ol.CanvasMap.prototype.once); goog.exportProperty( ol.CanvasMap.prototype, 'un', ol.CanvasMap.prototype.un); 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.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.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.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.ImageTile.prototype, 'getTileCoord', ol.ImageTile.prototype.getTileCoord); goog.exportProperty( ol.ImageTile.prototype, 'load', ol.ImageTile.prototype.load); 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, 'forEachFeatureAtPixel', ol.Map.prototype.forEachFeatureAtPixel); goog.exportProperty( ol.Map.prototype, 'getFeaturesAtPixel', ol.Map.prototype.getFeaturesAtPixel); 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.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.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.VectorImageTile.prototype, 'getTileCoord', ol.VectorImageTile.prototype.getTileCoord); goog.exportProperty( ol.VectorImageTile.prototype, 'load', ol.VectorImageTile.prototype.load); goog.exportProperty( ol.VectorTile.prototype, 'getTileCoord', ol.VectorTile.prototype.getTileCoord); goog.exportProperty( ol.VectorTile.prototype, 'load', ol.VectorTile.prototype.load); 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.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, 'clone', ol.style.Circle.prototype.clone); 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.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.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.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.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.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.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.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.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, 'removeLoadedExtent', ol.source.Cluster.prototype.removeLoadedExtent); goog.exportProperty( ol.source.Cluster.prototype, 'removeFeature', ol.source.Cluster.prototype.removeFeature); goog.exportProperty( ol.source.Cluster.prototype, 'setLoader', ol.source.Cluster.prototype.setLoader); 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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); ol.VERSION = 'v4.6.4'; OPENLAYERS.ol = ol; return OPENLAYERS.ol; }));