} Outside margin of the popup. Used to prevent
│ │ │ │ │ - * the popup from getting too close to the map border.
│ │ │ │ │ - */
│ │ │ │ │ - paddingForPopups: null,
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * Property: layerContainerOriginPx
│ │ │ │ │ - * {Object} Cached object representing the layer container origin (in pixels).
│ │ │ │ │ - */
│ │ │ │ │ - layerContainerOriginPx: null,
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * Property: minPx
│ │ │ │ │ - * {Object} An object with a 'x' and 'y' values that is the lower
│ │ │ │ │ - * left of maxExtent in viewport pixel space.
│ │ │ │ │ - * Used to verify in moveByPx that the new location we're moving to
│ │ │ │ │ - * is valid. It is also used in the getLonLatFromViewPortPx function
│ │ │ │ │ - * of Layer.
│ │ │ │ │ - */
│ │ │ │ │ - minPx: null,
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * Property: maxPx
│ │ │ │ │ - * {Object} An object with a 'x' and 'y' values that is the top
│ │ │ │ │ - * right of maxExtent in viewport pixel space.
│ │ │ │ │ - * Used to verify in moveByPx that the new location we're moving to
│ │ │ │ │ - * is valid.
│ │ │ │ │ - */
│ │ │ │ │ - maxPx: null,
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * Constructor: OpenLayers.Map
│ │ │ │ │ - * Constructor for a new OpenLayers.Map instance. There are two possible
│ │ │ │ │ - * ways to call the map constructor. See the examples below.
│ │ │ │ │ + * APIMethod: distanceTo
│ │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane).
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * div - {DOMElement|String} The element or id of an element in your page
│ │ │ │ │ - * that will contain the map. May be omitted if the option is
│ │ │ │ │ - * provided or if you intend to call the method later.
│ │ │ │ │ - * options - {Object} Optional object with properties to tag onto the map.
│ │ │ │ │ - *
│ │ │ │ │ - * Valid options (in addition to the listed API properties):
│ │ │ │ │ - * center - {|Array} The default initial center of the map.
│ │ │ │ │ - * If provided as array, the first value is the x coordinate,
│ │ │ │ │ - * and the 2nd value is the y coordinate.
│ │ │ │ │ - * Only specify if is provided.
│ │ │ │ │ - * Note that if an ArgParser/Permalink control is present,
│ │ │ │ │ - * and the querystring contains coordinates, center will be set
│ │ │ │ │ - * by that, and this option will be ignored.
│ │ │ │ │ - * zoom - {Number} The initial zoom level for the map. Only specify if
│ │ │ │ │ - * is provided.
│ │ │ │ │ - * Note that if an ArgParser/Permalink control is present,
│ │ │ │ │ - * and the querystring contains a zoom level, zoom will be set
│ │ │ │ │ - * by that, and this option will be ignored.
│ │ │ │ │ - * extent - {|Array} The initial extent of the map.
│ │ │ │ │ - * If provided as an array, the array should consist of
│ │ │ │ │ - * four values (left, bottom, right, top).
│ │ │ │ │ - * Only specify if and are not provided.
│ │ │ │ │ - *
│ │ │ │ │ - * Examples:
│ │ │ │ │ - * (code)
│ │ │ │ │ - * // create a map with default options in an element with the id "map1"
│ │ │ │ │ - * var map = new OpenLayers.Map("map1");
│ │ │ │ │ - *
│ │ │ │ │ - * // create a map with non-default options in an element with id "map2"
│ │ │ │ │ - * var options = {
│ │ │ │ │ - * projection: "EPSG:3857",
│ │ │ │ │ - * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
│ │ │ │ │ - * center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)
│ │ │ │ │ - * };
│ │ │ │ │ - * var map = new OpenLayers.Map("map2", options);
│ │ │ │ │ + * geometry - {} The target geometry.
│ │ │ │ │ + * options - {Object} Optional properties for configuring the distance
│ │ │ │ │ + * calculation.
│ │ │ │ │ *
│ │ │ │ │ - * // map with non-default options - same as above but with a single argument,
│ │ │ │ │ - * // a restricted extent, and using arrays for bounds and center
│ │ │ │ │ - * var map = new OpenLayers.Map({
│ │ │ │ │ - * div: "map_id",
│ │ │ │ │ - * projection: "EPSG:3857",
│ │ │ │ │ - * maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],
│ │ │ │ │ - * restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],
│ │ │ │ │ - * center: [-12356463.476333, 5621521.4854095]
│ │ │ │ │ - * });
│ │ │ │ │ + * Valid options:
│ │ │ │ │ + * details - {Boolean} Return details from the distance calculation.
│ │ │ │ │ + * Default is false.
│ │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the
│ │ │ │ │ + * nearest edge of the target geometry. Default is true. If true,
│ │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within
│ │ │ │ │ + * the target will result in a non-zero distance. If false, whenever
│ │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false,
│ │ │ │ │ + * details cannot be returned.
│ │ │ │ │ *
│ │ │ │ │ - * // create a map without a reference to a container - call render later
│ │ │ │ │ - * var map = new OpenLayers.Map({
│ │ │ │ │ - * projection: "EPSG:3857",
│ │ │ │ │ - * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)
│ │ │ │ │ - * });
│ │ │ │ │ - * (end)
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Number | Object} The distance between this geometry and the target.
│ │ │ │ │ + * If details is true, the return will be an object with distance,
│ │ │ │ │ + * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
│ │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1
│ │ │ │ │ + * properties represent the coordinates of the closest point on the
│ │ │ │ │ + * target geometry.
│ │ │ │ │ */
│ │ │ │ │ - initialize: function(div, options) {
│ │ │ │ │ -
│ │ │ │ │ - // If only one argument is provided, check if it is an object.
│ │ │ │ │ - if (arguments.length === 1 && typeof div === "object") {
│ │ │ │ │ - options = div;
│ │ │ │ │ - div = options && options.div;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // Simple-type defaults are set in class definition.
│ │ │ │ │ - // Now set complex-type defaults
│ │ │ │ │ - this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
│ │ │ │ │ - OpenLayers.Map.TILE_HEIGHT);
│ │ │ │ │ -
│ │ │ │ │ - this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
│ │ │ │ │ -
│ │ │ │ │ - this.theme = OpenLayers._getScriptLocation() +
│ │ │ │ │ - 'theme/default/style.css';
│ │ │ │ │ -
│ │ │ │ │ - // backup original options
│ │ │ │ │ - this.options = OpenLayers.Util.extend({}, options);
│ │ │ │ │ -
│ │ │ │ │ - // now override default options
│ │ │ │ │ - OpenLayers.Util.extend(this, options);
│ │ │ │ │ -
│ │ │ │ │ - var projCode = this.projection instanceof OpenLayers.Projection ?
│ │ │ │ │ - this.projection.projCode : this.projection;
│ │ │ │ │ - OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);
│ │ │ │ │ -
│ │ │ │ │ - // allow extents and center to be arrays
│ │ │ │ │ - if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {
│ │ │ │ │ - this.maxExtent = new OpenLayers.Bounds(this.maxExtent);
│ │ │ │ │ - }
│ │ │ │ │ - if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {
│ │ │ │ │ - this.minExtent = new OpenLayers.Bounds(this.minExtent);
│ │ │ │ │ - }
│ │ │ │ │ - if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {
│ │ │ │ │ - this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);
│ │ │ │ │ - }
│ │ │ │ │ - if (this.center && !(this.center instanceof OpenLayers.LonLat)) {
│ │ │ │ │ - this.center = new OpenLayers.LonLat(this.center);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // initialize layers array
│ │ │ │ │ - this.layers = [];
│ │ │ │ │ -
│ │ │ │ │ - this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
│ │ │ │ │ -
│ │ │ │ │ - this.div = OpenLayers.Util.getElement(div);
│ │ │ │ │ - if (!this.div) {
│ │ │ │ │ - this.div = document.createElement("div");
│ │ │ │ │ - this.div.style.height = "1px";
│ │ │ │ │ - this.div.style.width = "1px";
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - OpenLayers.Element.addClass(this.div, 'olMap');
│ │ │ │ │ -
│ │ │ │ │ - // the viewPortDiv is the outermost div we modify
│ │ │ │ │ - var id = this.id + "_OpenLayers_ViewPort";
│ │ │ │ │ - this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
│ │ │ │ │ - "relative", null,
│ │ │ │ │ - "hidden");
│ │ │ │ │ - this.viewPortDiv.style.width = "100%";
│ │ │ │ │ - this.viewPortDiv.style.height = "100%";
│ │ │ │ │ - this.viewPortDiv.className = "olMapViewport";
│ │ │ │ │ - this.div.appendChild(this.viewPortDiv);
│ │ │ │ │ -
│ │ │ │ │ - this.events = new OpenLayers.Events(
│ │ │ │ │ - this, this.viewPortDiv, null, this.fallThrough, {
│ │ │ │ │ - includeXY: true
│ │ │ │ │ - }
│ │ │ │ │ - );
│ │ │ │ │ -
│ │ │ │ │ - if (OpenLayers.TileManager && this.tileManager !== null) {
│ │ │ │ │ - if (!(this.tileManager instanceof OpenLayers.TileManager)) {
│ │ │ │ │ - this.tileManager = new OpenLayers.TileManager(this.tileManager);
│ │ │ │ │ - }
│ │ │ │ │ - this.tileManager.addMap(this);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // the layerContainerDiv is the one that holds all the layers
│ │ │ │ │ - id = this.id + "_OpenLayers_Container";
│ │ │ │ │ - this.layerContainerDiv = OpenLayers.Util.createDiv(id);
│ │ │ │ │ - this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] - 1;
│ │ │ │ │ - this.layerContainerOriginPx = {
│ │ │ │ │ - x: 0,
│ │ │ │ │ - y: 0
│ │ │ │ │ - };
│ │ │ │ │ - this.applyTransform();
│ │ │ │ │ -
│ │ │ │ │ - this.viewPortDiv.appendChild(this.layerContainerDiv);
│ │ │ │ │ -
│ │ │ │ │ - this.updateSize();
│ │ │ │ │ - if (this.eventListeners instanceof Object) {
│ │ │ │ │ - this.events.on(this.eventListeners);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if (this.autoUpdateSize === true) {
│ │ │ │ │ - // updateSize on catching the window's resize
│ │ │ │ │ - // Note that this is ok, as updateSize() does nothing if the
│ │ │ │ │ - // map's size has not actually changed.
│ │ │ │ │ - this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,
│ │ │ │ │ - this);
│ │ │ │ │ - OpenLayers.Event.observe(window, 'resize',
│ │ │ │ │ - this.updateSizeDestroy);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // only append link stylesheet if the theme property is set
│ │ │ │ │ - if (this.theme) {
│ │ │ │ │ - // check existing links for equivalent url
│ │ │ │ │ - var addNode = true;
│ │ │ │ │ - var nodes = document.getElementsByTagName('link');
│ │ │ │ │ - for (var i = 0, len = nodes.length; i < len; ++i) {
│ │ │ │ │ - if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
│ │ │ │ │ - this.theme)) {
│ │ │ │ │ - addNode = false;
│ │ │ │ │ - break;
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - // only add a new node if one with an equivalent url hasn't already
│ │ │ │ │ - // been added
│ │ │ │ │ - if (addNode) {
│ │ │ │ │ - var cssNode = document.createElement('link');
│ │ │ │ │ - cssNode.setAttribute('rel', 'stylesheet');
│ │ │ │ │ - cssNode.setAttribute('type', 'text/css');
│ │ │ │ │ - cssNode.setAttribute('href', this.theme);
│ │ │ │ │ - document.getElementsByTagName('head')[0].appendChild(cssNode);
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if (this.controls == null) { // default controls
│ │ │ │ │ - this.controls = [];
│ │ │ │ │ - if (OpenLayers.Control != null) { // running full or lite?
│ │ │ │ │ - // Navigation or TouchNavigation depending on what is in build
│ │ │ │ │ - if (OpenLayers.Control.Navigation) {
│ │ │ │ │ - this.controls.push(new OpenLayers.Control.Navigation());
│ │ │ │ │ - } else if (OpenLayers.Control.TouchNavigation) {
│ │ │ │ │ - this.controls.push(new OpenLayers.Control.TouchNavigation());
│ │ │ │ │ - }
│ │ │ │ │ - if (OpenLayers.Control.Zoom) {
│ │ │ │ │ - this.controls.push(new OpenLayers.Control.Zoom());
│ │ │ │ │ - } else if (OpenLayers.Control.PanZoom) {
│ │ │ │ │ - this.controls.push(new OpenLayers.Control.PanZoom());
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if (OpenLayers.Control.ArgParser) {
│ │ │ │ │ - this.controls.push(new OpenLayers.Control.ArgParser());
│ │ │ │ │ - }
│ │ │ │ │ - if (OpenLayers.Control.Attribution) {
│ │ │ │ │ - this.controls.push(new OpenLayers.Control.Attribution());
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) {
│ │ │ │ │ - this.addControlToMap(this.controls[i]);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - this.popups = [];
│ │ │ │ │ -
│ │ │ │ │ - this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
│ │ │ │ │ -
│ │ │ │ │ -
│ │ │ │ │ - // always call map.destroy()
│ │ │ │ │ - OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
│ │ │ │ │ -
│ │ │ │ │ - // add any initial layers
│ │ │ │ │ - if (options && options.layers) {
│ │ │ │ │ - /**
│ │ │ │ │ - * If you have set options.center, the map center property will be
│ │ │ │ │ - * set at this point. However, since setCenter has not been called,
│ │ │ │ │ - * addLayers gets confused. So we delete the map center in this
│ │ │ │ │ - * case. Because the check below uses options.center, it will
│ │ │ │ │ - * be properly set below.
│ │ │ │ │ - */
│ │ │ │ │ - delete this.center;
│ │ │ │ │ - delete this.zoom;
│ │ │ │ │ - this.addLayers(options.layers);
│ │ │ │ │ - // set center (and optionally zoom)
│ │ │ │ │ - if (options.center && !this.getCenter()) {
│ │ │ │ │ - // zoom can be undefined here
│ │ │ │ │ - this.setCenter(options.center, options.zoom);
│ │ │ │ │ + distanceTo: function(geometry, options) {
│ │ │ │ │ + var edge = !(options && options.edge === false);
│ │ │ │ │ + var details = edge && options && options.details;
│ │ │ │ │ + var distance, x0, y0, x1, y1, result;
│ │ │ │ │ + if (geometry instanceof OpenLayers.Geometry.Point) {
│ │ │ │ │ + x0 = this.x;
│ │ │ │ │ + y0 = this.y;
│ │ │ │ │ + x1 = geometry.x;
│ │ │ │ │ + y1 = geometry.y;
│ │ │ │ │ + distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
│ │ │ │ │ + result = !details ?
│ │ │ │ │ + distance : {
│ │ │ │ │ + x0: x0,
│ │ │ │ │ + y0: y0,
│ │ │ │ │ + x1: x1,
│ │ │ │ │ + y1: y1,
│ │ │ │ │ + distance: distance
│ │ │ │ │ + };
│ │ │ │ │ + } else {
│ │ │ │ │ + result = geometry.distanceTo(this, options);
│ │ │ │ │ + if (details) {
│ │ │ │ │ + // switch coord order since this geom is target
│ │ │ │ │ + result = {
│ │ │ │ │ + x0: result.x1,
│ │ │ │ │ + y0: result.y1,
│ │ │ │ │ + x1: result.x0,
│ │ │ │ │ + y1: result.y0,
│ │ │ │ │ + distance: result.distance
│ │ │ │ │ + };
│ │ │ │ │ }
│ │ │ │ │ }
│ │ │ │ │ -
│ │ │ │ │ - if (this.panMethod) {
│ │ │ │ │ - this.panTween = new OpenLayers.Tween(this.panMethod);
│ │ │ │ │ - }
│ │ │ │ │ - if (this.zoomMethod && this.applyTransform.transform) {
│ │ │ │ │ - this.zoomTween = new OpenLayers.Tween(this.zoomMethod);
│ │ │ │ │ - }
│ │ │ │ │ + return result;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getViewport
│ │ │ │ │ - * Get the DOMElement representing the view port.
│ │ │ │ │ + * APIMethod: equals
│ │ │ │ │ + * Determine whether another geometry is equivalent to this one. Geometries
│ │ │ │ │ + * are considered equivalent if all components have the same coordinates.
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * geom - {} The geometry to test.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {DOMElement}
│ │ │ │ │ + * {Boolean} The supplied geometry is equivalent to this geometry.
│ │ │ │ │ */
│ │ │ │ │ - getViewport: function() {
│ │ │ │ │ - return this.viewPortDiv;
│ │ │ │ │ + equals: function(geom) {
│ │ │ │ │ + var equals = false;
│ │ │ │ │ + if (geom != null) {
│ │ │ │ │ + equals = ((this.x == geom.x && this.y == geom.y) ||
│ │ │ │ │ + (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
│ │ │ │ │ + }
│ │ │ │ │ + return equals;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: render
│ │ │ │ │ - * Render the map to a specified container.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * div - {String|DOMElement} The container that the map should be rendered
│ │ │ │ │ - * to. If different than the current container, the map viewport
│ │ │ │ │ - * will be moved from the current to the new container.
│ │ │ │ │ + * Method: toShortString
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {String} Shortened String representation of Point object.
│ │ │ │ │ + * (ex. "5, 42")
│ │ │ │ │ */
│ │ │ │ │ - render: function(div) {
│ │ │ │ │ - this.div = OpenLayers.Util.getElement(div);
│ │ │ │ │ - OpenLayers.Element.addClass(this.div, 'olMap');
│ │ │ │ │ - this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
│ │ │ │ │ - this.div.appendChild(this.viewPortDiv);
│ │ │ │ │ - this.updateSize();
│ │ │ │ │ + toShortString: function() {
│ │ │ │ │ + return (this.x + ", " + this.y);
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: unloadDestroy
│ │ │ │ │ - * Function that is called to destroy the map on page unload. stored here
│ │ │ │ │ - * so that if map is manually destroyed, we can unregister this.
│ │ │ │ │ - */
│ │ │ │ │ - unloadDestroy: null,
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * Method: updateSizeDestroy
│ │ │ │ │ - * When the map is destroyed, we need to stop listening to updateSize
│ │ │ │ │ - * events: this method stores the function we need to unregister in
│ │ │ │ │ - * non-IE browsers.
│ │ │ │ │ - */
│ │ │ │ │ - updateSizeDestroy: null,
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: destroy
│ │ │ │ │ - * Destroy this map.
│ │ │ │ │ - * Note that if you are using an application which removes a container
│ │ │ │ │ - * of the map from the DOM, you need to ensure that you destroy the
│ │ │ │ │ - * map *before* this happens; otherwise, the page unload handler
│ │ │ │ │ - * will fail because the DOM elements that map.destroy() wants
│ │ │ │ │ - * to clean up will be gone. (See
│ │ │ │ │ - * http://trac.osgeo.org/openlayers/ticket/2277 for more information).
│ │ │ │ │ - * This will apply to GeoExt and also to other applications which
│ │ │ │ │ - * modify the DOM of the container of the OpenLayers Map.
│ │ │ │ │ + * APIMethod: move
│ │ │ │ │ + * Moves a geometry by the given displacement along positive x and y axes.
│ │ │ │ │ + * This modifies the position of the geometry and clears the cached
│ │ │ │ │ + * bounds.
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * x - {Float} Distance to move geometry in positive x direction.
│ │ │ │ │ + * y - {Float} Distance to move geometry in positive y direction.
│ │ │ │ │ */
│ │ │ │ │ - destroy: function() {
│ │ │ │ │ - // if unloadDestroy is null, we've already been destroyed
│ │ │ │ │ - if (!this.unloadDestroy) {
│ │ │ │ │ - return false;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // make sure panning doesn't continue after destruction
│ │ │ │ │ - if (this.panTween) {
│ │ │ │ │ - this.panTween.stop();
│ │ │ │ │ - this.panTween = null;
│ │ │ │ │ - }
│ │ │ │ │ - // make sure zooming doesn't continue after destruction
│ │ │ │ │ - if (this.zoomTween) {
│ │ │ │ │ - this.zoomTween.stop();
│ │ │ │ │ - this.zoomTween = null;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // map has been destroyed. dont do it again!
│ │ │ │ │ - OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
│ │ │ │ │ - this.unloadDestroy = null;
│ │ │ │ │ -
│ │ │ │ │ - if (this.updateSizeDestroy) {
│ │ │ │ │ - OpenLayers.Event.stopObserving(window, 'resize',
│ │ │ │ │ - this.updateSizeDestroy);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - this.paddingForPopups = null;
│ │ │ │ │ -
│ │ │ │ │ - if (this.controls != null) {
│ │ │ │ │ - for (var i = this.controls.length - 1; i >= 0; --i) {
│ │ │ │ │ - this.controls[i].destroy();
│ │ │ │ │ - }
│ │ │ │ │ - this.controls = null;
│ │ │ │ │ - }
│ │ │ │ │ - if (this.layers != null) {
│ │ │ │ │ - for (var i = this.layers.length - 1; i >= 0; --i) {
│ │ │ │ │ - //pass 'false' to destroy so that map wont try to set a new
│ │ │ │ │ - // baselayer after each baselayer is removed
│ │ │ │ │ - this.layers[i].destroy(false);
│ │ │ │ │ - }
│ │ │ │ │ - this.layers = null;
│ │ │ │ │ - }
│ │ │ │ │ - if (this.viewPortDiv && this.viewPortDiv.parentNode) {
│ │ │ │ │ - this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
│ │ │ │ │ - }
│ │ │ │ │ - this.viewPortDiv = null;
│ │ │ │ │ -
│ │ │ │ │ - if (this.tileManager) {
│ │ │ │ │ - this.tileManager.removeMap(this);
│ │ │ │ │ - this.tileManager = null;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if (this.eventListeners) {
│ │ │ │ │ - this.events.un(this.eventListeners);
│ │ │ │ │ - this.eventListeners = null;
│ │ │ │ │ - }
│ │ │ │ │ - this.events.destroy();
│ │ │ │ │ - this.events = null;
│ │ │ │ │ -
│ │ │ │ │ - this.options = null;
│ │ │ │ │ + move: function(x, y) {
│ │ │ │ │ + this.x = this.x + x;
│ │ │ │ │ + this.y = this.y + y;
│ │ │ │ │ + this.clearBounds();
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: setOptions
│ │ │ │ │ - * Change the map options
│ │ │ │ │ + * APIMethod: rotate
│ │ │ │ │ + * Rotate a point around another.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * options - {Object} Hashtable of options to tag to the map
│ │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise
│ │ │ │ │ + * from the positive x-axis)
│ │ │ │ │ + * origin - {} Center point for the rotation
│ │ │ │ │ */
│ │ │ │ │ - setOptions: function(options) {
│ │ │ │ │ - var updatePxExtent = this.minPx &&
│ │ │ │ │ - options.restrictedExtent != this.restrictedExtent;
│ │ │ │ │ - OpenLayers.Util.extend(this, options);
│ │ │ │ │ - // force recalculation of minPx and maxPx
│ │ │ │ │ - updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
│ │ │ │ │ - forceZoomChange: true
│ │ │ │ │ - });
│ │ │ │ │ + rotate: function(angle, origin) {
│ │ │ │ │ + angle *= Math.PI / 180;
│ │ │ │ │ + var radius = this.distanceTo(origin);
│ │ │ │ │ + var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
│ │ │ │ │ + this.x = origin.x + (radius * Math.cos(theta));
│ │ │ │ │ + this.y = origin.y + (radius * Math.sin(theta));
│ │ │ │ │ + this.clearBounds();
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getTileSize
│ │ │ │ │ - * Get the tile size for the map
│ │ │ │ │ + * APIMethod: getCentroid
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {}
│ │ │ │ │ + * {} The centroid of the collection
│ │ │ │ │ */
│ │ │ │ │ - getTileSize: function() {
│ │ │ │ │ - return this.tileSize;
│ │ │ │ │ + getCentroid: function() {
│ │ │ │ │ + return new OpenLayers.Geometry.Point(this.x, this.y);
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getBy
│ │ │ │ │ - * Get a list of objects given a property and a match item.
│ │ │ │ │ + * APIMethod: resize
│ │ │ │ │ + * Resize a point relative to some origin. For points, this has the effect
│ │ │ │ │ + * of scaling a vector (from the origin to the point). This method is
│ │ │ │ │ + * more useful on geometry collection subclasses.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * array - {String} A property on the map whose value is an array.
│ │ │ │ │ - * property - {String} A property on each item of the given array.
│ │ │ │ │ - * match - {String | Object} A string to match. Can also be a regular
│ │ │ │ │ - * expression literal or object. In addition, it can be any object
│ │ │ │ │ - * with a method named test. For reqular expressions or other, if
│ │ │ │ │ - * match.test(map[array][i][property]) evaluates to true, the item will
│ │ │ │ │ - * be included in the array returned. If no items are found, an empty
│ │ │ │ │ - * array is returned.
│ │ │ │ │ - *
│ │ │ │ │ + * scale - {Float} Ratio of the new distance from the origin to the old
│ │ │ │ │ + * distance from the origin. A scale of 2 doubles the
│ │ │ │ │ + * distance between the point and origin.
│ │ │ │ │ + * origin - {} Point of origin for resizing
│ │ │ │ │ + * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Array} An array of items where the given property matches the given
│ │ │ │ │ - * criteria.
│ │ │ │ │ + * {} - The current geometry.
│ │ │ │ │ */
│ │ │ │ │ - getBy: function(array, property, match) {
│ │ │ │ │ - var test = (typeof match.test == "function");
│ │ │ │ │ - var found = OpenLayers.Array.filter(this[array], function(item) {
│ │ │ │ │ - return item[property] == match || (test && match.test(item[property]));
│ │ │ │ │ - });
│ │ │ │ │ - return found;
│ │ │ │ │ + resize: function(scale, origin, ratio) {
│ │ │ │ │ + ratio = (ratio == undefined) ? 1 : ratio;
│ │ │ │ │ + this.x = origin.x + (scale * ratio * (this.x - origin.x));
│ │ │ │ │ + this.y = origin.y + (scale * (this.y - origin.y));
│ │ │ │ │ + this.clearBounds();
│ │ │ │ │ + return this;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getLayersBy
│ │ │ │ │ - * Get a list of layers with properties matching the given criteria.
│ │ │ │ │ + * APIMethod: intersects
│ │ │ │ │ + * Determine if the input geometry intersects this one.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * property - {String} A layer property to be matched.
│ │ │ │ │ - * match - {String | Object} A string to match. Can also be a regular
│ │ │ │ │ - * expression literal or object. In addition, it can be any object
│ │ │ │ │ - * with a method named test. For reqular expressions or other, if
│ │ │ │ │ - * match.test(layer[property]) evaluates to true, the layer will be
│ │ │ │ │ - * included in the array returned. If no layers are found, an empty
│ │ │ │ │ - * array is returned.
│ │ │ │ │ + * geometry - {} Any type of geometry.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Array()} A list of layers matching the given criteria.
│ │ │ │ │ - * An empty array is returned if no matches are found.
│ │ │ │ │ + * {Boolean} The input geometry intersects this one.
│ │ │ │ │ */
│ │ │ │ │ - getLayersBy: function(property, match) {
│ │ │ │ │ - return this.getBy("layers", property, match);
│ │ │ │ │ + intersects: function(geometry) {
│ │ │ │ │ + var intersect = false;
│ │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
│ │ │ │ │ + intersect = this.equals(geometry);
│ │ │ │ │ + } else {
│ │ │ │ │ + intersect = geometry.intersects(this);
│ │ │ │ │ + }
│ │ │ │ │ + return intersect;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getLayersByName
│ │ │ │ │ - * Get a list of layers with names matching the given name.
│ │ │ │ │ - *
│ │ │ │ │ + * APIMethod: transform
│ │ │ │ │ + * Translate the x,y properties of the point from source to dest.
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * match - {String | Object} A layer name. The name can also be a regular
│ │ │ │ │ - * expression literal or object. In addition, it can be any object
│ │ │ │ │ - * with a method named test. For reqular expressions or other, if
│ │ │ │ │ - * name.test(layer.name) evaluates to true, the layer will be included
│ │ │ │ │ - * in the list of layers returned. If no layers are found, an empty
│ │ │ │ │ - * array is returned.
│ │ │ │ │ - *
│ │ │ │ │ + * source - {}
│ │ │ │ │ + * dest - {}
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Array()} A list of layers matching the given name.
│ │ │ │ │ - * An empty array is returned if no matches are found.
│ │ │ │ │ + * {}
│ │ │ │ │ */
│ │ │ │ │ - getLayersByName: function(match) {
│ │ │ │ │ - return this.getLayersBy("name", match);
│ │ │ │ │ + transform: function(source, dest) {
│ │ │ │ │ + if ((source && dest)) {
│ │ │ │ │ + OpenLayers.Projection.transform(
│ │ │ │ │ + this, source, dest);
│ │ │ │ │ + this.bounds = null;
│ │ │ │ │ + }
│ │ │ │ │ + return this;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getLayersByClass
│ │ │ │ │ - * Get a list of layers of a given class (CLASS_NAME).
│ │ │ │ │ + * APIMethod: getVertices
│ │ │ │ │ + * Return a list of all points in this geometry.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * match - {String | Object} A layer class name. The match can also be a
│ │ │ │ │ - * regular expression literal or object. In addition, it can be any
│ │ │ │ │ - * object with a method named test. For reqular expressions or other,
│ │ │ │ │ - * if type.test(layer.CLASS_NAME) evaluates to true, the layer will
│ │ │ │ │ - * be included in the list of layers returned. If no layers are
│ │ │ │ │ - * found, an empty array is returned.
│ │ │ │ │ + * nodes - {Boolean} For lines, only return vertices that are
│ │ │ │ │ + * endpoints. If false, for lines, only vertices that are not
│ │ │ │ │ + * endpoints will be returned. If not provided, all vertices will
│ │ │ │ │ + * be returned.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Array()} A list of layers matching the given class.
│ │ │ │ │ - * An empty array is returned if no matches are found.
│ │ │ │ │ + * {Array} A list of all vertices in the geometry.
│ │ │ │ │ */
│ │ │ │ │ - getLayersByClass: function(match) {
│ │ │ │ │ - return this.getLayersBy("CLASS_NAME", match);
│ │ │ │ │ + getVertices: function(nodes) {
│ │ │ │ │ + return [this];
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Point"
│ │ │ │ │ +});
│ │ │ │ │ +/* ======================================================================
│ │ │ │ │ + OpenLayers/Geometry/Collection.js
│ │ │ │ │ + ====================================================================== */
│ │ │ │ │ +
│ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ │ + * full text of the license. */
│ │ │ │ │ +
│ │ │ │ │ +/**
│ │ │ │ │ + * @requires OpenLayers/Geometry.js
│ │ │ │ │ + */
│ │ │ │ │ +
│ │ │ │ │ +/**
│ │ │ │ │ + * Class: OpenLayers.Geometry.Collection
│ │ │ │ │ + * A Collection is exactly what it sounds like: A collection of different
│ │ │ │ │ + * Geometries. These are stored in the local parameter (which
│ │ │ │ │ + * can be passed as a parameter to the constructor).
│ │ │ │ │ + *
│ │ │ │ │ + * As new geometries are added to the collection, they are NOT cloned.
│ │ │ │ │ + * When removing geometries, they need to be specified by reference (ie you
│ │ │ │ │ + * have to pass in the *exact* geometry to be removed).
│ │ │ │ │ + *
│ │ │ │ │ + * The and functions here merely iterate through
│ │ │ │ │ + * the components, summing their respective areas and lengths.
│ │ │ │ │ + *
│ │ │ │ │ + * Create a new instance with the constructor.
│ │ │ │ │ + *
│ │ │ │ │ + * Inherits from:
│ │ │ │ │ + * -
│ │ │ │ │ + */
│ │ │ │ │ +OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
│ │ │ │ │ +
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getControlsBy
│ │ │ │ │ - * Get a list of controls with properties matching the given criteria.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * property - {String} A control property to be matched.
│ │ │ │ │ - * match - {String | Object} A string to match. Can also be a regular
│ │ │ │ │ - * expression literal or object. In addition, it can be any object
│ │ │ │ │ - * with a method named test. For reqular expressions or other, if
│ │ │ │ │ - * match.test(layer[property]) evaluates to true, the layer will be
│ │ │ │ │ - * included in the array returned. If no layers are found, an empty
│ │ │ │ │ - * array is returned.
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Array()} A list of controls matching the given
│ │ │ │ │ - * criteria. An empty array is returned if no matches are found.
│ │ │ │ │ + * APIProperty: components
│ │ │ │ │ + * {Array()} The component parts of this geometry
│ │ │ │ │ */
│ │ │ │ │ - getControlsBy: function(property, match) {
│ │ │ │ │ - return this.getBy("controls", property, match);
│ │ │ │ │ - },
│ │ │ │ │ + components: null,
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getControlsByClass
│ │ │ │ │ - * Get a list of controls of a given class (CLASS_NAME).
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * match - {String | Object} A control class name. The match can also be a
│ │ │ │ │ - * regular expression literal or object. In addition, it can be any
│ │ │ │ │ - * object with a method named test. For reqular expressions or other,
│ │ │ │ │ - * if type.test(control.CLASS_NAME) evaluates to true, the control will
│ │ │ │ │ - * be included in the list of controls returned. If no controls are
│ │ │ │ │ - * found, an empty array is returned.
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Array()} A list of controls matching the given class.
│ │ │ │ │ - * An empty array is returned if no matches are found.
│ │ │ │ │ + * Property: componentTypes
│ │ │ │ │ + * {Array(String)} An array of class names representing the types of
│ │ │ │ │ + * components that the collection can include. A null value means the
│ │ │ │ │ + * component types are not restricted.
│ │ │ │ │ */
│ │ │ │ │ - getControlsByClass: function(match) {
│ │ │ │ │ - return this.getControlsBy("CLASS_NAME", match);
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Layer Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions deal with adding and */
│ │ │ │ │ - /* removing Layers to and from the Map */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ + componentTypes: null,
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getLayer
│ │ │ │ │ - * Get a layer based on its id
│ │ │ │ │ + * Constructor: OpenLayers.Geometry.Collection
│ │ │ │ │ + * Creates a Geometry Collection -- a list of geoms.
│ │ │ │ │ *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * id - {String} A layer id
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * components - {Array()} Optional array of geometries
│ │ │ │ │ *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} The Layer with the corresponding id from the map's
│ │ │ │ │ - * layer collection, or null if not found.
│ │ │ │ │ */
│ │ │ │ │ - getLayer: function(id) {
│ │ │ │ │ - var foundLayer = null;
│ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) {
│ │ │ │ │ - var layer = this.layers[i];
│ │ │ │ │ - if (layer.id == id) {
│ │ │ │ │ - foundLayer = layer;
│ │ │ │ │ - break;
│ │ │ │ │ - }
│ │ │ │ │ + initialize: function(components) {
│ │ │ │ │ + OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
│ │ │ │ │ + this.components = [];
│ │ │ │ │ + if (components != null) {
│ │ │ │ │ + this.addComponents(components);
│ │ │ │ │ }
│ │ │ │ │ - return foundLayer;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: setLayerZIndex
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * layer - {}
│ │ │ │ │ - * zIdx - {int}
│ │ │ │ │ + * APIMethod: destroy
│ │ │ │ │ + * Destroy this geometry.
│ │ │ │ │ */
│ │ │ │ │ - setLayerZIndex: function(layer, zIdx) {
│ │ │ │ │ - layer.setZIndex(
│ │ │ │ │ - this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] +
│ │ │ │ │ - zIdx * 5);
│ │ │ │ │ + destroy: function() {
│ │ │ │ │ + this.components.length = 0;
│ │ │ │ │ + this.components = null;
│ │ │ │ │ + OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: resetLayersZIndex
│ │ │ │ │ - * Reset each layer's z-index based on layer's array index
│ │ │ │ │ + * APIMethod: clone
│ │ │ │ │ + * Clone this geometry.
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {} An exact clone of this collection
│ │ │ │ │ */
│ │ │ │ │ - resetLayersZIndex: function() {
│ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) {
│ │ │ │ │ - var layer = this.layers[i];
│ │ │ │ │ - this.setLayerZIndex(layer, i);
│ │ │ │ │ + clone: function() {
│ │ │ │ │ + var geometry = eval("new " + this.CLASS_NAME + "()");
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + geometry.addComponent(this.components[i].clone());
│ │ │ │ │ }
│ │ │ │ │ +
│ │ │ │ │ + // catch any randomly tagged-on properties
│ │ │ │ │ + OpenLayers.Util.applyDefaults(geometry, this);
│ │ │ │ │ +
│ │ │ │ │ + return geometry;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: addLayer
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * layer - {}
│ │ │ │ │ - *
│ │ │ │ │ + * Method: getComponentsString
│ │ │ │ │ + * Get a string representing the components for this collection
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Boolean} True if the layer has been added to the map.
│ │ │ │ │ + * {String} A string representation of the components of this geometry
│ │ │ │ │ */
│ │ │ │ │ - addLayer: function(layer) {
│ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) {
│ │ │ │ │ - if (this.layers[i] == layer) {
│ │ │ │ │ - return false;
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - if (this.events.triggerEvent("preaddlayer", {
│ │ │ │ │ - layer: layer
│ │ │ │ │ - }) === false) {
│ │ │ │ │ - return false;
│ │ │ │ │ - }
│ │ │ │ │ - if (this.allOverlays) {
│ │ │ │ │ - layer.isBaseLayer = false;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - layer.div.className = "olLayerDiv";
│ │ │ │ │ - layer.div.style.overflow = "";
│ │ │ │ │ - this.setLayerZIndex(layer, this.layers.length);
│ │ │ │ │ -
│ │ │ │ │ - if (layer.isFixed) {
│ │ │ │ │ - this.viewPortDiv.appendChild(layer.div);
│ │ │ │ │ - } else {
│ │ │ │ │ - this.layerContainerDiv.appendChild(layer.div);
│ │ │ │ │ + getComponentsString: function() {
│ │ │ │ │ + var strings = [];
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + strings.push(this.components[i].toShortString());
│ │ │ │ │ }
│ │ │ │ │ - this.layers.push(layer);
│ │ │ │ │ - layer.setMap(this);
│ │ │ │ │ + return strings.join(",");
│ │ │ │ │ + },
│ │ │ │ │
│ │ │ │ │ - if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {
│ │ │ │ │ - if (this.baseLayer == null) {
│ │ │ │ │ - // set the first baselaye we add as the baselayer
│ │ │ │ │ - this.setBaseLayer(layer);
│ │ │ │ │ - } else {
│ │ │ │ │ - layer.setVisibility(false);
│ │ │ │ │ + /**
│ │ │ │ │ + * APIMethod: calculateBounds
│ │ │ │ │ + * Recalculate the bounds by iterating through the components and
│ │ │ │ │ + * calling calling extendBounds() on each item.
│ │ │ │ │ + */
│ │ │ │ │ + calculateBounds: function() {
│ │ │ │ │ + this.bounds = null;
│ │ │ │ │ + var bounds = new OpenLayers.Bounds();
│ │ │ │ │ + var components = this.components;
│ │ │ │ │ + if (components) {
│ │ │ │ │ + for (var i = 0, len = components.length; i < len; i++) {
│ │ │ │ │ + bounds.extend(components[i].getBounds());
│ │ │ │ │ }
│ │ │ │ │ - } else {
│ │ │ │ │ - layer.redraw();
│ │ │ │ │ }
│ │ │ │ │ -
│ │ │ │ │ - this.events.triggerEvent("addlayer", {
│ │ │ │ │ - layer: layer
│ │ │ │ │ - });
│ │ │ │ │ - layer.events.triggerEvent("added", {
│ │ │ │ │ - map: this,
│ │ │ │ │ - layer: layer
│ │ │ │ │ - });
│ │ │ │ │ - layer.afterAdd();
│ │ │ │ │ -
│ │ │ │ │ - return true;
│ │ │ │ │ + // to preserve old behavior, we only set bounds if non-null
│ │ │ │ │ + // in the future, we could add bounds.isEmpty()
│ │ │ │ │ + if (bounds.left != null && bounds.bottom != null &&
│ │ │ │ │ + bounds.right != null && bounds.top != null) {
│ │ │ │ │ + this.setBounds(bounds);
│ │ │ │ │ + }
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: addLayers
│ │ │ │ │ + * APIMethod: addComponents
│ │ │ │ │ + * Add components to this geometry.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * layers - {Array()}
│ │ │ │ │ + * components - {Array()} An array of geometries to add
│ │ │ │ │ */
│ │ │ │ │ - addLayers: function(layers) {
│ │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) {
│ │ │ │ │ - this.addLayer(layers[i]);
│ │ │ │ │ + addComponents: function(components) {
│ │ │ │ │ + if (!(OpenLayers.Util.isArray(components))) {
│ │ │ │ │ + components = [components];
│ │ │ │ │ + }
│ │ │ │ │ + for (var i = 0, len = components.length; i < len; i++) {
│ │ │ │ │ + this.addComponent(components[i]);
│ │ │ │ │ }
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: removeLayer
│ │ │ │ │ - * Removes a layer from the map by removing its visual element (the
│ │ │ │ │ - * layer.div property), then removing it from the map's internal list
│ │ │ │ │ - * of layers, setting the layer's map property to null.
│ │ │ │ │ - *
│ │ │ │ │ - * a "removelayer" event is triggered.
│ │ │ │ │ - *
│ │ │ │ │ - * very worthy of mention is that simply removing a layer from a map
│ │ │ │ │ - * will not cause the removal of any popups which may have been created
│ │ │ │ │ - * by the layer. this is due to the fact that it was decided at some
│ │ │ │ │ - * point that popups would not belong to layers. thus there is no way
│ │ │ │ │ - * for us to know here to which layer the popup belongs.
│ │ │ │ │ - *
│ │ │ │ │ - * A simple solution to this is simply to call destroy() on the layer.
│ │ │ │ │ - * the default OpenLayers.Layer class's destroy() function
│ │ │ │ │ - * automatically takes care to remove itself from whatever map it has
│ │ │ │ │ - * been attached to.
│ │ │ │ │ - *
│ │ │ │ │ - * The correct solution is for the layer itself to register an
│ │ │ │ │ - * event-handler on "removelayer" and when it is called, if it
│ │ │ │ │ - * recognizes itself as the layer being removed, then it cycles through
│ │ │ │ │ - * its own personal list of popups, removing them from the map.
│ │ │ │ │ + /**
│ │ │ │ │ + * Method: addComponent
│ │ │ │ │ + * Add a new component (geometry) to the collection. If this.componentTypes
│ │ │ │ │ + * is set, then the component class name must be in the componentTypes array.
│ │ │ │ │ + *
│ │ │ │ │ + * The bounds cache is reset.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * layer - {}
│ │ │ │ │ - * setNewBaseLayer - {Boolean} Default is true
│ │ │ │ │ + * component - {} A geometry to add
│ │ │ │ │ + * index - {int} Optional index into the array to insert the component
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Boolean} The component geometry was successfully added
│ │ │ │ │ */
│ │ │ │ │ - removeLayer: function(layer, setNewBaseLayer) {
│ │ │ │ │ - if (this.events.triggerEvent("preremovelayer", {
│ │ │ │ │ - layer: layer
│ │ │ │ │ - }) === false) {
│ │ │ │ │ - return;
│ │ │ │ │ - }
│ │ │ │ │ - if (setNewBaseLayer == null) {
│ │ │ │ │ - setNewBaseLayer = true;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if (layer.isFixed) {
│ │ │ │ │ - this.viewPortDiv.removeChild(layer.div);
│ │ │ │ │ - } else {
│ │ │ │ │ - this.layerContainerDiv.removeChild(layer.div);
│ │ │ │ │ - }
│ │ │ │ │ - OpenLayers.Util.removeItem(this.layers, layer);
│ │ │ │ │ - layer.removeMap(this);
│ │ │ │ │ - layer.map = null;
│ │ │ │ │ + addComponent: function(component, index) {
│ │ │ │ │ + var added = false;
│ │ │ │ │ + if (component) {
│ │ │ │ │ + if (this.componentTypes == null ||
│ │ │ │ │ + (OpenLayers.Util.indexOf(this.componentTypes,
│ │ │ │ │ + component.CLASS_NAME) > -1)) {
│ │ │ │ │
│ │ │ │ │ - // if we removed the base layer, need to set a new one
│ │ │ │ │ - if (this.baseLayer == layer) {
│ │ │ │ │ - this.baseLayer = null;
│ │ │ │ │ - if (setNewBaseLayer) {
│ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) {
│ │ │ │ │ - var iLayer = this.layers[i];
│ │ │ │ │ - if (iLayer.isBaseLayer || this.allOverlays) {
│ │ │ │ │ - this.setBaseLayer(iLayer);
│ │ │ │ │ - break;
│ │ │ │ │ - }
│ │ │ │ │ + if (index != null && (index < this.components.length)) {
│ │ │ │ │ + var components1 = this.components.slice(0, index);
│ │ │ │ │ + var components2 = this.components.slice(index,
│ │ │ │ │ + this.components.length);
│ │ │ │ │ + components1.push(component);
│ │ │ │ │ + this.components = components1.concat(components2);
│ │ │ │ │ + } else {
│ │ │ │ │ + this.components.push(component);
│ │ │ │ │ }
│ │ │ │ │ + component.parent = this;
│ │ │ │ │ + this.clearBounds();
│ │ │ │ │ + added = true;
│ │ │ │ │ }
│ │ │ │ │ }
│ │ │ │ │ -
│ │ │ │ │ - this.resetLayersZIndex();
│ │ │ │ │ -
│ │ │ │ │ - this.events.triggerEvent("removelayer", {
│ │ │ │ │ - layer: layer
│ │ │ │ │ - });
│ │ │ │ │ - layer.events.triggerEvent("removed", {
│ │ │ │ │ - map: this,
│ │ │ │ │ - layer: layer
│ │ │ │ │ - });
│ │ │ │ │ + return added;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getNumLayers
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Int} The number of layers attached to the map.
│ │ │ │ │ - */
│ │ │ │ │ - getNumLayers: function() {
│ │ │ │ │ - return this.layers.length;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getLayerIndex
│ │ │ │ │ + * APIMethod: removeComponents
│ │ │ │ │ + * Remove components from this geometry.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * layer - {}
│ │ │ │ │ + * components - {Array()} The components to be removed
│ │ │ │ │ *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Integer} The current (zero-based) index of the given layer in the map's
│ │ │ │ │ - * layer stack. Returns -1 if the layer isn't on the map.
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Boolean} A component was removed.
│ │ │ │ │ */
│ │ │ │ │ - getLayerIndex: function(layer) {
│ │ │ │ │ - return OpenLayers.Util.indexOf(this.layers, layer);
│ │ │ │ │ - },
│ │ │ │ │ + removeComponents: function(components) {
│ │ │ │ │ + var removed = false;
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: setLayerIndex
│ │ │ │ │ - * Move the given layer to the specified (zero-based) index in the layer
│ │ │ │ │ - * list, changing its z-index in the map display. Use
│ │ │ │ │ - * map.getLayerIndex() to find out the current index of a layer. Note
│ │ │ │ │ - * that this cannot (or at least should not) be effectively used to
│ │ │ │ │ - * raise base layers above overlays.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * layer - {}
│ │ │ │ │ - * idx - {int}
│ │ │ │ │ - */
│ │ │ │ │ - setLayerIndex: function(layer, idx) {
│ │ │ │ │ - var base = this.getLayerIndex(layer);
│ │ │ │ │ - if (idx < 0) {
│ │ │ │ │ - idx = 0;
│ │ │ │ │ - } else if (idx > this.layers.length) {
│ │ │ │ │ - idx = this.layers.length;
│ │ │ │ │ + if (!(OpenLayers.Util.isArray(components))) {
│ │ │ │ │ + components = [components];
│ │ │ │ │ }
│ │ │ │ │ - if (base != idx) {
│ │ │ │ │ - this.layers.splice(base, 1);
│ │ │ │ │ - this.layers.splice(idx, 0, layer);
│ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) {
│ │ │ │ │ - this.setLayerZIndex(this.layers[i], i);
│ │ │ │ │ - }
│ │ │ │ │ - this.events.triggerEvent("changelayer", {
│ │ │ │ │ - layer: layer,
│ │ │ │ │ - property: "order"
│ │ │ │ │ - });
│ │ │ │ │ - if (this.allOverlays) {
│ │ │ │ │ - if (idx === 0) {
│ │ │ │ │ - this.setBaseLayer(layer);
│ │ │ │ │ - } else if (this.baseLayer !== this.layers[0]) {
│ │ │ │ │ - this.setBaseLayer(this.layers[0]);
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ + for (var i = components.length - 1; i >= 0; --i) {
│ │ │ │ │ + removed = this.removeComponent(components[i]) || removed;
│ │ │ │ │ }
│ │ │ │ │ + return removed;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: raiseLayer
│ │ │ │ │ - * Change the index of the given layer by delta. If delta is positive,
│ │ │ │ │ - * the layer is moved up the map's layer stack; if delta is negative,
│ │ │ │ │ - * the layer is moved down. Again, note that this cannot (or at least
│ │ │ │ │ - * should not) be effectively used to raise base layers above overlays.
│ │ │ │ │ + /**
│ │ │ │ │ + * Method: removeComponent
│ │ │ │ │ + * Remove a component from this geometry.
│ │ │ │ │ *
│ │ │ │ │ - * Paremeters:
│ │ │ │ │ - * layer - {}
│ │ │ │ │ - * delta - {int}
│ │ │ │ │ - */
│ │ │ │ │ - raiseLayer: function(layer, delta) {
│ │ │ │ │ - var idx = this.getLayerIndex(layer) + delta;
│ │ │ │ │ - this.setLayerIndex(layer, idx);
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: setBaseLayer
│ │ │ │ │ - * Allows user to specify one of the currently-loaded layers as the Map's
│ │ │ │ │ - * new base layer.
│ │ │ │ │ - *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * newBaseLayer - {}
│ │ │ │ │ + * component - {}
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Boolean} The component was removed.
│ │ │ │ │ */
│ │ │ │ │ - setBaseLayer: function(newBaseLayer) {
│ │ │ │ │ -
│ │ │ │ │ - if (newBaseLayer != this.baseLayer) {
│ │ │ │ │ -
│ │ │ │ │ - // ensure newBaseLayer is already loaded
│ │ │ │ │ - if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
│ │ │ │ │ -
│ │ │ │ │ - // preserve center and scale when changing base layers
│ │ │ │ │ - var center = this.getCachedCenter();
│ │ │ │ │ - var newResolution = OpenLayers.Util.getResolutionFromScale(
│ │ │ │ │ - this.getScale(), newBaseLayer.units
│ │ │ │ │ - );
│ │ │ │ │ -
│ │ │ │ │ - // make the old base layer invisible
│ │ │ │ │ - if (this.baseLayer != null && !this.allOverlays) {
│ │ │ │ │ - this.baseLayer.setVisibility(false);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - // set new baselayer
│ │ │ │ │ - this.baseLayer = newBaseLayer;
│ │ │ │ │ -
│ │ │ │ │ - if (!this.allOverlays || this.baseLayer.visibility) {
│ │ │ │ │ - this.baseLayer.setVisibility(true);
│ │ │ │ │ - // Layer may previously have been visible but not in range.
│ │ │ │ │ - // In this case we need to redraw it to make it visible.
│ │ │ │ │ - if (this.baseLayer.inRange === false) {
│ │ │ │ │ - this.baseLayer.redraw();
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ + removeComponent: function(component) {
│ │ │ │ │
│ │ │ │ │ - // recenter the map
│ │ │ │ │ - if (center != null) {
│ │ │ │ │ - // new zoom level derived from old scale
│ │ │ │ │ - var newZoom = this.getZoomForResolution(
│ │ │ │ │ - newResolution || this.resolution, true
│ │ │ │ │ - );
│ │ │ │ │ - // zoom and force zoom change
│ │ │ │ │ - this.setCenter(center, newZoom, false, true);
│ │ │ │ │ - }
│ │ │ │ │ + OpenLayers.Util.removeItem(this.components, component);
│ │ │ │ │
│ │ │ │ │ - this.events.triggerEvent("changebaselayer", {
│ │ │ │ │ - layer: this.baseLayer
│ │ │ │ │ - });
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ + // clearBounds() so that it gets recalculated on the next call
│ │ │ │ │ + // to this.getBounds();
│ │ │ │ │ + this.clearBounds();
│ │ │ │ │ + return true;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ -
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Control Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions deal with adding and */
│ │ │ │ │ - /* removing Controls to and from the Map */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: addControl
│ │ │ │ │ - * Add the passed over control to the map. Optionally
│ │ │ │ │ - * position the control at the given pixel.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * control - {}
│ │ │ │ │ - * px - {}
│ │ │ │ │ + * APIMethod: getLength
│ │ │ │ │ + * Calculate the length of this geometry
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Float} The length of the geometry
│ │ │ │ │ */
│ │ │ │ │ - addControl: function(control, px) {
│ │ │ │ │ - this.controls.push(control);
│ │ │ │ │ - this.addControlToMap(control, px);
│ │ │ │ │ + getLength: function() {
│ │ │ │ │ + var length = 0.0;
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + length += this.components[i].getLength();
│ │ │ │ │ + }
│ │ │ │ │ + return length;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: addControls
│ │ │ │ │ - * Add all of the passed over controls to the map.
│ │ │ │ │ - * You can pass over an optional second array
│ │ │ │ │ - * with pixel-objects to position the controls.
│ │ │ │ │ - * The indices of the two arrays should match and
│ │ │ │ │ - * you can add null as pixel for those controls
│ │ │ │ │ - * you want to be autopositioned.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * controls - {Array()}
│ │ │ │ │ - * pixels - {Array()}
│ │ │ │ │ + * APIMethod: getArea
│ │ │ │ │ + * Calculate the area of this geometry. Note how this function is overridden
│ │ │ │ │ + * in .
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Float} The area of the collection by summing its parts
│ │ │ │ │ */
│ │ │ │ │ - addControls: function(controls, pixels) {
│ │ │ │ │ - var pxs = (arguments.length === 1) ? [] : pixels;
│ │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) {
│ │ │ │ │ - var ctrl = controls[i];
│ │ │ │ │ - var px = (pxs[i]) ? pxs[i] : null;
│ │ │ │ │ - this.addControl(ctrl, px);
│ │ │ │ │ + getArea: function() {
│ │ │ │ │ + var area = 0.0;
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + area += this.components[i].getArea();
│ │ │ │ │ }
│ │ │ │ │ + return area;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * Method: addControlToMap
│ │ │ │ │ - *
│ │ │ │ │ + /**
│ │ │ │ │ + * APIMethod: getGeodesicArea
│ │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto
│ │ │ │ │ + * the earth.
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ + * projection - {} The spatial reference system
│ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is
│ │ │ │ │ + * assumed.
│ │ │ │ │ *
│ │ │ │ │ - * control - {}
│ │ │ │ │ - * px - {}
│ │ │ │ │ + * Reference:
│ │ │ │ │ + * 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 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {float} The approximate geodesic area of the geometry in square meters.
│ │ │ │ │ */
│ │ │ │ │ - addControlToMap: function(control, px) {
│ │ │ │ │ - // If a control doesn't have a div at this point, it belongs in the
│ │ │ │ │ - // viewport.
│ │ │ │ │ - control.outsideViewport = (control.div != null);
│ │ │ │ │ -
│ │ │ │ │ - // If the map has a displayProjection, and the control doesn't, set
│ │ │ │ │ - // the display projection.
│ │ │ │ │ - if (this.displayProjection && !control.displayProjection) {
│ │ │ │ │ - control.displayProjection = this.displayProjection;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - control.setMap(this);
│ │ │ │ │ - var div = control.draw(px);
│ │ │ │ │ - if (div) {
│ │ │ │ │ - if (!control.outsideViewport) {
│ │ │ │ │ - div.style.zIndex = this.Z_INDEX_BASE['Control'] +
│ │ │ │ │ - this.controls.length;
│ │ │ │ │ - this.viewPortDiv.appendChild(div);
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - if (control.autoActivate) {
│ │ │ │ │ - control.activate();
│ │ │ │ │ + getGeodesicArea: function(projection) {
│ │ │ │ │ + var area = 0.0;
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + area += this.components[i].getGeodesicArea(projection);
│ │ │ │ │ }
│ │ │ │ │ + return area;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getControl
│ │ │ │ │ - *
│ │ │ │ │ + * APIMethod: getCentroid
│ │ │ │ │ + *
│ │ │ │ │ + * Compute the centroid for this geometry collection.
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * id - {String} ID of the control to return.
│ │ │ │ │ - *
│ │ │ │ │ + * weighted - {Boolean} Perform the getCentroid computation recursively,
│ │ │ │ │ + * returning an area weighted average of all geometries in this collection.
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} The control from the map's list of controls
│ │ │ │ │ - * which has a matching 'id'. If none found,
│ │ │ │ │ - * returns null.
│ │ │ │ │ + * {} The centroid of the collection
│ │ │ │ │ */
│ │ │ │ │ - getControl: function(id) {
│ │ │ │ │ - var returnControl = null;
│ │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) {
│ │ │ │ │ - var control = this.controls[i];
│ │ │ │ │ - if (control.id == id) {
│ │ │ │ │ - returnControl = control;
│ │ │ │ │ - break;
│ │ │ │ │ - }
│ │ │ │ │ + getCentroid: function(weighted) {
│ │ │ │ │ + if (!weighted) {
│ │ │ │ │ + return this.components.length && this.components[0].getCentroid();
│ │ │ │ │ + }
│ │ │ │ │ + var len = this.components.length;
│ │ │ │ │ + if (!len) {
│ │ │ │ │ + return false;
│ │ │ │ │ }
│ │ │ │ │ - return returnControl;
│ │ │ │ │ - },
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: removeControl
│ │ │ │ │ - * Remove a control from the map. Removes the control both from the map
│ │ │ │ │ - * object's internal array of controls, as well as from the map's
│ │ │ │ │ - * viewPort (assuming the control was not added outsideViewport)
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * control - {} The control to remove.
│ │ │ │ │ - */
│ │ │ │ │ - removeControl: function(control) {
│ │ │ │ │ - //make sure control is non-null and actually part of our map
│ │ │ │ │ - if ((control) && (control == this.getControl(control.id))) {
│ │ │ │ │ - if (control.div && (control.div.parentNode == this.viewPortDiv)) {
│ │ │ │ │ - this.viewPortDiv.removeChild(control.div);
│ │ │ │ │ + var areas = [];
│ │ │ │ │ + var centroids = [];
│ │ │ │ │ + var areaSum = 0;
│ │ │ │ │ + var minArea = Number.MAX_VALUE;
│ │ │ │ │ + var component;
│ │ │ │ │ + for (var i = 0; i < len; ++i) {
│ │ │ │ │ + component = this.components[i];
│ │ │ │ │ + var area = component.getArea();
│ │ │ │ │ + var centroid = component.getCentroid(true);
│ │ │ │ │ + if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
│ │ │ │ │ + continue;
│ │ │ │ │ }
│ │ │ │ │ - OpenLayers.Util.removeItem(this.controls, control);
│ │ │ │ │ + areas.push(area);
│ │ │ │ │ + areaSum += area;
│ │ │ │ │ + minArea = (area < minArea && area > 0) ? area : minArea;
│ │ │ │ │ + centroids.push(centroid);
│ │ │ │ │ }
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Popup Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions deal with adding and */
│ │ │ │ │ - /* removing Popups to and from the Map */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: addPopup
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * popup - {}
│ │ │ │ │ - * exclusive - {Boolean} If true, closes all other popups first
│ │ │ │ │ - */
│ │ │ │ │ - addPopup: function(popup, exclusive) {
│ │ │ │ │ -
│ │ │ │ │ - if (exclusive) {
│ │ │ │ │ - //remove all other popups from screen
│ │ │ │ │ - for (var i = this.popups.length - 1; i >= 0; --i) {
│ │ │ │ │ - this.removePopup(this.popups[i]);
│ │ │ │ │ + len = areas.length;
│ │ │ │ │ + if (areaSum === 0) {
│ │ │ │ │ + // all the components in this collection have 0 area
│ │ │ │ │ + // probably a collection of points -- weight all the points the same
│ │ │ │ │ + for (var i = 0; i < len; ++i) {
│ │ │ │ │ + areas[i] = 1;
│ │ │ │ │ + }
│ │ │ │ │ + areaSum = areas.length;
│ │ │ │ │ + } else {
│ │ │ │ │ + // normalize all the areas where the smallest area will get
│ │ │ │ │ + // a value of 1
│ │ │ │ │ + for (var i = 0; i < len; ++i) {
│ │ │ │ │ + areas[i] /= minArea;
│ │ │ │ │ }
│ │ │ │ │ + areaSum /= minArea;
│ │ │ │ │ }
│ │ │ │ │
│ │ │ │ │ - popup.map = this;
│ │ │ │ │ - this.popups.push(popup);
│ │ │ │ │ - var popupDiv = popup.draw();
│ │ │ │ │ - if (popupDiv) {
│ │ │ │ │ - popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
│ │ │ │ │ - this.popups.length;
│ │ │ │ │ - this.layerContainerDiv.appendChild(popupDiv);
│ │ │ │ │ + var xSum = 0,
│ │ │ │ │ + ySum = 0,
│ │ │ │ │ + centroid, area;
│ │ │ │ │ + for (var i = 0; i < len; ++i) {
│ │ │ │ │ + centroid = centroids[i];
│ │ │ │ │ + area = areas[i];
│ │ │ │ │ + xSum += centroid.x * area;
│ │ │ │ │ + ySum += centroid.y * area;
│ │ │ │ │ }
│ │ │ │ │ - },
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: removePopup
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * popup - {}
│ │ │ │ │ - */
│ │ │ │ │ - removePopup: function(popup) {
│ │ │ │ │ - OpenLayers.Util.removeItem(this.popups, popup);
│ │ │ │ │ - if (popup.div) {
│ │ │ │ │ - try {
│ │ │ │ │ - this.layerContainerDiv.removeChild(popup.div);
│ │ │ │ │ - } catch (e) {} // Popups sometimes apparently get disconnected
│ │ │ │ │ - // from the layerContainerDiv, and cause complaints.
│ │ │ │ │ - }
│ │ │ │ │ - popup.map = null;
│ │ │ │ │ + return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum);
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Container Div Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions deal with the access to */
│ │ │ │ │ - /* and maintenance of the size of the container div */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getSize
│ │ │ │ │ + * APIMethod: getGeodesicLength
│ │ │ │ │ + * Calculate the approximate length of the geometry were it projected onto
│ │ │ │ │ + * the earth.
│ │ │ │ │ + *
│ │ │ │ │ + * projection - {} The spatial reference system
│ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is
│ │ │ │ │ + * assumed.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} An object that represents the
│ │ │ │ │ - * size, in pixels, of the div into which OpenLayers
│ │ │ │ │ - * has been loaded.
│ │ │ │ │ - * Note - A clone() of this locally cached variable is
│ │ │ │ │ - * returned, so as not to allow users to modify it.
│ │ │ │ │ + * {Float} The appoximate geodesic length of the geometry in meters.
│ │ │ │ │ */
│ │ │ │ │ - getSize: function() {
│ │ │ │ │ - var size = null;
│ │ │ │ │ - if (this.size != null) {
│ │ │ │ │ - size = this.size.clone();
│ │ │ │ │ + getGeodesicLength: function(projection) {
│ │ │ │ │ + var length = 0.0;
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + length += this.components[i].getGeodesicLength(projection);
│ │ │ │ │ }
│ │ │ │ │ - return size;
│ │ │ │ │ + return length;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: updateSize
│ │ │ │ │ - * This function should be called by any external code which dynamically
│ │ │ │ │ - * changes the size of the map div (because mozilla wont let us catch
│ │ │ │ │ - * the "onresize" for an element)
│ │ │ │ │ + * APIMethod: move
│ │ │ │ │ + * Moves a geometry by the given displacement along positive x and y axes.
│ │ │ │ │ + * This modifies the position of the geometry and clears the cached
│ │ │ │ │ + * bounds.
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * x - {Float} Distance to move geometry in positive x direction.
│ │ │ │ │ + * y - {Float} Distance to move geometry in positive y direction.
│ │ │ │ │ */
│ │ │ │ │ - updateSize: function() {
│ │ │ │ │ - // the div might have moved on the page, also
│ │ │ │ │ - var newSize = this.getCurrentSize();
│ │ │ │ │ - if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
│ │ │ │ │ - this.events.clearMouseCache();
│ │ │ │ │ - var oldSize = this.getSize();
│ │ │ │ │ - if (oldSize == null) {
│ │ │ │ │ - this.size = oldSize = newSize;
│ │ │ │ │ - }
│ │ │ │ │ - if (!newSize.equals(oldSize)) {
│ │ │ │ │ -
│ │ │ │ │ - // store the new size
│ │ │ │ │ - this.size = newSize;
│ │ │ │ │ -
│ │ │ │ │ - //notify layers of mapresize
│ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) {
│ │ │ │ │ - this.layers[i].onMapResize();
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - var center = this.getCachedCenter();
│ │ │ │ │ -
│ │ │ │ │ - if (this.baseLayer != null && center != null) {
│ │ │ │ │ - var zoom = this.getZoom();
│ │ │ │ │ - this.zoom = null;
│ │ │ │ │ - this.setCenter(center, zoom);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - }
│ │ │ │ │ + move: function(x, y) {
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + this.components[i].move(x, y);
│ │ │ │ │ }
│ │ │ │ │ - this.events.triggerEvent("updatesize");
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: getCurrentSize
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} A new object with the dimensions
│ │ │ │ │ - * of the map div
│ │ │ │ │ - */
│ │ │ │ │ - getCurrentSize: function() {
│ │ │ │ │ -
│ │ │ │ │ - var size = new OpenLayers.Size(this.div.clientWidth,
│ │ │ │ │ - this.div.clientHeight);
│ │ │ │ │ -
│ │ │ │ │ - if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
│ │ │ │ │ - size.w = this.div.offsetWidth;
│ │ │ │ │ - size.h = this.div.offsetHeight;
│ │ │ │ │ - }
│ │ │ │ │ - if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
│ │ │ │ │ - size.w = parseInt(this.div.style.width);
│ │ │ │ │ - size.h = parseInt(this.div.style.height);
│ │ │ │ │ - }
│ │ │ │ │ - return size;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * Method: calculateBounds
│ │ │ │ │ - *
│ │ │ │ │ + * APIMethod: rotate
│ │ │ │ │ + * Rotate a geometry around some origin
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * center - {} Default is this.getCenter()
│ │ │ │ │ - * resolution - {float} Default is this.getResolution()
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} A bounds based on resolution, center, and
│ │ │ │ │ - * current mapsize.
│ │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise
│ │ │ │ │ + * from the positive x-axis)
│ │ │ │ │ + * origin - {} Center point for the rotation
│ │ │ │ │ */
│ │ │ │ │ - calculateBounds: function(center, resolution) {
│ │ │ │ │ -
│ │ │ │ │ - var extent = null;
│ │ │ │ │ -
│ │ │ │ │ - if (center == null) {
│ │ │ │ │ - center = this.getCachedCenter();
│ │ │ │ │ - }
│ │ │ │ │ - if (resolution == null) {
│ │ │ │ │ - resolution = this.getResolution();
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if ((center != null) && (resolution != null)) {
│ │ │ │ │ - var halfWDeg = (this.size.w * resolution) / 2;
│ │ │ │ │ - var halfHDeg = (this.size.h * resolution) / 2;
│ │ │ │ │ -
│ │ │ │ │ - extent = new OpenLayers.Bounds(center.lon - halfWDeg,
│ │ │ │ │ - center.lat - halfHDeg,
│ │ │ │ │ - center.lon + halfWDeg,
│ │ │ │ │ - center.lat + halfHDeg);
│ │ │ │ │ + rotate: function(angle, origin) {
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) {
│ │ │ │ │ + this.components[i].rotate(angle, origin);
│ │ │ │ │ }
│ │ │ │ │ -
│ │ │ │ │ - return extent;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ -
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Zoom, Center, Pan Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions handle the validation, */
│ │ │ │ │ - /* getting and setting of the Zoom Level and Center */
│ │ │ │ │ - /* as well as the panning of the Map */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getCenter
│ │ │ │ │ + * APIMethod: resize
│ │ │ │ │ + * Resize a geometry relative to some origin. Use this method to apply
│ │ │ │ │ + * a uniform scaling to a geometry.
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * scale - {Float} Factor by which to scale the geometry. A scale of 2
│ │ │ │ │ + * doubles the size of the geometry in each dimension
│ │ │ │ │ + * (lines, for example, will be twice as long, and polygons
│ │ │ │ │ + * will have four times the area).
│ │ │ │ │ + * origin - {} Point of origin for resizing
│ │ │ │ │ + * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {}
│ │ │ │ │ + * {} - The current geometry.
│ │ │ │ │ */
│ │ │ │ │ - getCenter: function() {
│ │ │ │ │ - var center = null;
│ │ │ │ │ - var cachedCenter = this.getCachedCenter();
│ │ │ │ │ - if (cachedCenter) {
│ │ │ │ │ - center = cachedCenter.clone();
│ │ │ │ │ + resize: function(scale, origin, ratio) {
│ │ │ │ │ + for (var i = 0; i < this.components.length; ++i) {
│ │ │ │ │ + this.components[i].resize(scale, origin, ratio);
│ │ │ │ │ }
│ │ │ │ │ - return center;
│ │ │ │ │ + return this;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: getCachedCenter
│ │ │ │ │ + * APIMethod: distanceTo
│ │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane).
│ │ │ │ │ *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {}
│ │ │ │ │ - */
│ │ │ │ │ - getCachedCenter: function() {
│ │ │ │ │ - if (!this.center && this.size) {
│ │ │ │ │ - this.center = this.getLonLatFromViewPortPx({
│ │ │ │ │ - x: this.size.w / 2,
│ │ │ │ │ - y: this.size.h / 2
│ │ │ │ │ - });
│ │ │ │ │ - }
│ │ │ │ │ - return this.center;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getZoom
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Integer}
│ │ │ │ │ - */
│ │ │ │ │ - getZoom: function() {
│ │ │ │ │ - return this.zoom;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: pan
│ │ │ │ │ - * Allows user to pan by a value of screen pixels
│ │ │ │ │ - *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * dx - {Integer}
│ │ │ │ │ - * dy - {Integer}
│ │ │ │ │ - * options - {Object} Options to configure panning:
│ │ │ │ │ - * - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
│ │ │ │ │ - * - *dragging* {Boolean} Call setCenter with dragging true. Default is
│ │ │ │ │ - * false.
│ │ │ │ │ + * geometry - {} The target geometry.
│ │ │ │ │ + * options - {Object} Optional properties for configuring the distance
│ │ │ │ │ + * calculation.
│ │ │ │ │ + *
│ │ │ │ │ + * Valid options:
│ │ │ │ │ + * details - {Boolean} Return details from the distance calculation.
│ │ │ │ │ + * Default is false.
│ │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the
│ │ │ │ │ + * nearest edge of the target geometry. Default is true. If true,
│ │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within
│ │ │ │ │ + * the target will result in a non-zero distance. If false, whenever
│ │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false,
│ │ │ │ │ + * details cannot be returned.
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Number | Object} The distance between this geometry and the target.
│ │ │ │ │ + * If details is true, the return will be an object with distance,
│ │ │ │ │ + * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
│ │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1
│ │ │ │ │ + * properties represent the coordinates of the closest point on the
│ │ │ │ │ + * target geometry.
│ │ │ │ │ */
│ │ │ │ │ - pan: function(dx, dy, options) {
│ │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, {
│ │ │ │ │ - animate: true,
│ │ │ │ │ - dragging: false
│ │ │ │ │ - });
│ │ │ │ │ - if (options.dragging) {
│ │ │ │ │ - if (dx != 0 || dy != 0) {
│ │ │ │ │ - this.moveByPx(dx, dy);
│ │ │ │ │ - }
│ │ │ │ │ - } else {
│ │ │ │ │ - // getCenter
│ │ │ │ │ - var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
│ │ │ │ │ -
│ │ │ │ │ - // adjust
│ │ │ │ │ - var newCenterPx = centerPx.add(dx, dy);
│ │ │ │ │ -
│ │ │ │ │ - if (this.dragging || !newCenterPx.equals(centerPx)) {
│ │ │ │ │ - var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
│ │ │ │ │ - if (options.animate) {
│ │ │ │ │ - this.panTo(newCenterLonLat);
│ │ │ │ │ - } else {
│ │ │ │ │ - this.moveTo(newCenterLonLat);
│ │ │ │ │ - if (this.dragging) {
│ │ │ │ │ - this.dragging = false;
│ │ │ │ │ - this.events.triggerEvent("moveend");
│ │ │ │ │ - }
│ │ │ │ │ + distanceTo: function(geometry, options) {
│ │ │ │ │ + var edge = !(options && options.edge === false);
│ │ │ │ │ + var details = edge && options && options.details;
│ │ │ │ │ + var result, best, distance;
│ │ │ │ │ + var min = Number.POSITIVE_INFINITY;
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) {
│ │ │ │ │ + result = this.components[i].distanceTo(geometry, options);
│ │ │ │ │ + distance = details ? result.distance : result;
│ │ │ │ │ + if (distance < min) {
│ │ │ │ │ + min = distance;
│ │ │ │ │ + best = result;
│ │ │ │ │ + if (min == 0) {
│ │ │ │ │ + break;
│ │ │ │ │ }
│ │ │ │ │ }
│ │ │ │ │ }
│ │ │ │ │ -
│ │ │ │ │ + return best;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: panTo
│ │ │ │ │ - * Allows user to pan to a new lonlat
│ │ │ │ │ - * If the new lonlat is in the current extent the map will slide smoothly
│ │ │ │ │ + * APIMethod: equals
│ │ │ │ │ + * Determine whether another geometry is equivalent to this one. Geometries
│ │ │ │ │ + * are considered equivalent if all components have the same coordinates.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * lonlat - {}
│ │ │ │ │ + * geometry - {} The geometry to test.
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Boolean} The supplied geometry is equivalent to this geometry.
│ │ │ │ │ */
│ │ │ │ │ - panTo: function(lonlat) {
│ │ │ │ │ - if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
│ │ │ │ │ - var center = this.getCachedCenter();
│ │ │ │ │ -
│ │ │ │ │ - // center will not change, don't do nothing
│ │ │ │ │ - if (lonlat.equals(center)) {
│ │ │ │ │ - return;
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - var from = this.getPixelFromLonLat(center);
│ │ │ │ │ - var to = this.getPixelFromLonLat(lonlat);
│ │ │ │ │ - var vector = {
│ │ │ │ │ - x: to.x - from.x,
│ │ │ │ │ - y: to.y - from.y
│ │ │ │ │ - };
│ │ │ │ │ - var last = {
│ │ │ │ │ - x: 0,
│ │ │ │ │ - y: 0
│ │ │ │ │ - };
│ │ │ │ │ -
│ │ │ │ │ - this.panTween.start({
│ │ │ │ │ - x: 0,
│ │ │ │ │ - y: 0
│ │ │ │ │ - }, vector, this.panDuration, {
│ │ │ │ │ - callbacks: {
│ │ │ │ │ - eachStep: OpenLayers.Function.bind(function(px) {
│ │ │ │ │ - var x = px.x - last.x,
│ │ │ │ │ - y = px.y - last.y;
│ │ │ │ │ - this.moveByPx(x, y);
│ │ │ │ │ - last.x = Math.round(px.x);
│ │ │ │ │ - last.y = Math.round(px.y);
│ │ │ │ │ - }, this),
│ │ │ │ │ - done: OpenLayers.Function.bind(function(px) {
│ │ │ │ │ - this.moveTo(lonlat);
│ │ │ │ │ - this.dragging = false;
│ │ │ │ │ - this.events.triggerEvent("moveend");
│ │ │ │ │ - }, this)
│ │ │ │ │ - }
│ │ │ │ │ - });
│ │ │ │ │ + equals: function(geometry) {
│ │ │ │ │ + var equivalent = true;
│ │ │ │ │ + if (!geometry || !geometry.CLASS_NAME ||
│ │ │ │ │ + (this.CLASS_NAME != geometry.CLASS_NAME)) {
│ │ │ │ │ + equivalent = false;
│ │ │ │ │ + } else if (!(OpenLayers.Util.isArray(geometry.components)) ||
│ │ │ │ │ + (geometry.components.length != this.components.length)) {
│ │ │ │ │ + equivalent = false;
│ │ │ │ │ } else {
│ │ │ │ │ - this.setCenter(lonlat);
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) {
│ │ │ │ │ + if (!this.components[i].equals(geometry.components[i])) {
│ │ │ │ │ + equivalent = false;
│ │ │ │ │ + break;
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ + return equivalent;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: setCenter
│ │ │ │ │ - * Set the map center (and optionally, the zoom level).
│ │ │ │ │ + * APIMethod: transform
│ │ │ │ │ + * Reproject the components geometry from source to dest.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * lonlat - {|Array} The new center location.
│ │ │ │ │ - * If provided as array, the first value is the x coordinate,
│ │ │ │ │ - * and the 2nd value is the y coordinate.
│ │ │ │ │ - * zoom - {Integer} Optional zoom level.
│ │ │ │ │ - * dragging - {Boolean} Specifies whether or not to trigger
│ │ │ │ │ - * movestart/end events
│ │ │ │ │ - * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom
│ │ │ │ │ - * change events (needed on baseLayer change)
│ │ │ │ │ - *
│ │ │ │ │ - * TBD: reconsider forceZoomChange in 3.0
│ │ │ │ │ - */
│ │ │ │ │ - setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
│ │ │ │ │ - if (this.panTween) {
│ │ │ │ │ - this.panTween.stop();
│ │ │ │ │ - }
│ │ │ │ │ - if (this.zoomTween) {
│ │ │ │ │ - this.zoomTween.stop();
│ │ │ │ │ - }
│ │ │ │ │ - this.moveTo(lonlat, zoom, {
│ │ │ │ │ - 'dragging': dragging,
│ │ │ │ │ - 'forceZoomChange': forceZoomChange
│ │ │ │ │ - });
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * Method: moveByPx
│ │ │ │ │ - * Drag the map by pixels.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * dx - {Number}
│ │ │ │ │ - * dy - {Number}
│ │ │ │ │ + * source - {}
│ │ │ │ │ + * dest - {}
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {}
│ │ │ │ │ */
│ │ │ │ │ - moveByPx: function(dx, dy) {
│ │ │ │ │ - var hw = this.size.w / 2;
│ │ │ │ │ - var hh = this.size.h / 2;
│ │ │ │ │ - var x = hw + dx;
│ │ │ │ │ - var y = hh + dy;
│ │ │ │ │ - var wrapDateLine = this.baseLayer.wrapDateLine;
│ │ │ │ │ - var xRestriction = 0;
│ │ │ │ │ - var yRestriction = 0;
│ │ │ │ │ - if (this.restrictedExtent) {
│ │ │ │ │ - xRestriction = hw;
│ │ │ │ │ - yRestriction = hh;
│ │ │ │ │ - // wrapping the date line makes no sense for restricted extents
│ │ │ │ │ - wrapDateLine = false;
│ │ │ │ │ - }
│ │ │ │ │ - dx = wrapDateLine ||
│ │ │ │ │ - x <= this.maxPx.x - xRestriction &&
│ │ │ │ │ - x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
│ │ │ │ │ - dy = y <= this.maxPx.y - yRestriction &&
│ │ │ │ │ - y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
│ │ │ │ │ - if (dx || dy) {
│ │ │ │ │ - if (!this.dragging) {
│ │ │ │ │ - this.dragging = true;
│ │ │ │ │ - this.events.triggerEvent("movestart");
│ │ │ │ │ - }
│ │ │ │ │ - this.center = null;
│ │ │ │ │ - if (dx) {
│ │ │ │ │ - this.layerContainerOriginPx.x -= dx;
│ │ │ │ │ - this.minPx.x -= dx;
│ │ │ │ │ - this.maxPx.x -= dx;
│ │ │ │ │ - }
│ │ │ │ │ - if (dy) {
│ │ │ │ │ - this.layerContainerOriginPx.y -= dy;
│ │ │ │ │ - this.minPx.y -= dy;
│ │ │ │ │ - this.maxPx.y -= dy;
│ │ │ │ │ - }
│ │ │ │ │ - this.applyTransform();
│ │ │ │ │ - var layer, i, len;
│ │ │ │ │ - for (i = 0, len = this.layers.length; i < len; ++i) {
│ │ │ │ │ - layer = this.layers[i];
│ │ │ │ │ - if (layer.visibility &&
│ │ │ │ │ - (layer === this.baseLayer || layer.inRange)) {
│ │ │ │ │ - layer.moveByPx(dx, dy);
│ │ │ │ │ - layer.events.triggerEvent("move");
│ │ │ │ │ - }
│ │ │ │ │ + transform: function(source, dest) {
│ │ │ │ │ + if (source && dest) {
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) {
│ │ │ │ │ + var component = this.components[i];
│ │ │ │ │ + component.transform(source, dest);
│ │ │ │ │ }
│ │ │ │ │ - this.events.triggerEvent("move");
│ │ │ │ │ + this.bounds = null;
│ │ │ │ │ }
│ │ │ │ │ + return this;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: adjustZoom
│ │ │ │ │ + * APIMethod: intersects
│ │ │ │ │ + * Determine if the input geometry intersects this one.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * zoom - {Number} The zoom level to adjust
│ │ │ │ │ + * geometry - {} Any type of geometry.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Integer} Adjusted zoom level that shows a map not wider than its
│ │ │ │ │ - * 's maxExtent.
│ │ │ │ │ + * {Boolean} The input geometry intersects this one.
│ │ │ │ │ */
│ │ │ │ │ - adjustZoom: function(zoom) {
│ │ │ │ │ - if (this.baseLayer && this.baseLayer.wrapDateLine) {
│ │ │ │ │ - var resolution, resolutions = this.baseLayer.resolutions,
│ │ │ │ │ - maxResolution = this.getMaxExtent().getWidth() / this.size.w;
│ │ │ │ │ - if (this.getResolutionForZoom(zoom) > maxResolution) {
│ │ │ │ │ - if (this.fractionalZoom) {
│ │ │ │ │ - zoom = this.getZoomForResolution(maxResolution);
│ │ │ │ │ - } else {
│ │ │ │ │ - for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) {
│ │ │ │ │ - if (resolutions[i] <= maxResolution) {
│ │ │ │ │ - zoom = i;
│ │ │ │ │ - break;
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ + intersects: function(geometry) {
│ │ │ │ │ + var intersect = false;
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) {
│ │ │ │ │ + intersect = geometry.intersects(this.components[i]);
│ │ │ │ │ + if (intersect) {
│ │ │ │ │ + break;
│ │ │ │ │ }
│ │ │ │ │ }
│ │ │ │ │ - return zoom;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getMinZoom
│ │ │ │ │ - * Returns the minimum zoom level for the current map view. If the base
│ │ │ │ │ - * layer is configured with set to true, this will be the
│ │ │ │ │ - * first zoom level that shows no more than one world width in the current
│ │ │ │ │ - * map viewport. Components that rely on this value (e.g. zoom sliders)
│ │ │ │ │ - * should also listen to the map's "updatesize" event and call this method
│ │ │ │ │ - * in the "updatesize" listener.
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Number} Minimum zoom level that shows a map not wider than its
│ │ │ │ │ - * 's maxExtent. This is an Integer value, unless the map is
│ │ │ │ │ - * configured with set to true.
│ │ │ │ │ - */
│ │ │ │ │ - getMinZoom: function() {
│ │ │ │ │ - return this.adjustZoom(0);
│ │ │ │ │ + return intersect;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: moveTo
│ │ │ │ │ + * APIMethod: getVertices
│ │ │ │ │ + * Return a list of all points in this geometry.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * lonlat - {}
│ │ │ │ │ - * zoom - {Integer}
│ │ │ │ │ - * options - {Object}
│ │ │ │ │ + * nodes - {Boolean} For lines, only return vertices that are
│ │ │ │ │ + * endpoints. If false, for lines, only vertices that are not
│ │ │ │ │ + * endpoints will be returned. If not provided, all vertices will
│ │ │ │ │ + * be returned.
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Array} A list of all vertices in the geometry.
│ │ │ │ │ */
│ │ │ │ │ - moveTo: function(lonlat, zoom, options) {
│ │ │ │ │ - if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {
│ │ │ │ │ - lonlat = new OpenLayers.LonLat(lonlat);
│ │ │ │ │ - }
│ │ │ │ │ - if (!options) {
│ │ │ │ │ - options = {};
│ │ │ │ │ - }
│ │ │ │ │ - if (zoom != null) {
│ │ │ │ │ - zoom = parseFloat(zoom);
│ │ │ │ │ - if (!this.fractionalZoom) {
│ │ │ │ │ - zoom = Math.round(zoom);
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - var requestedZoom = zoom;
│ │ │ │ │ - zoom = this.adjustZoom(zoom);
│ │ │ │ │ - if (zoom !== requestedZoom) {
│ │ │ │ │ - // zoom was adjusted, so keep old lonlat to avoid panning
│ │ │ │ │ - lonlat = this.getCenter();
│ │ │ │ │ - }
│ │ │ │ │ - // dragging is false by default
│ │ │ │ │ - var dragging = options.dragging || this.dragging;
│ │ │ │ │ - // forceZoomChange is false by default
│ │ │ │ │ - var forceZoomChange = options.forceZoomChange;
│ │ │ │ │ -
│ │ │ │ │ - if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
│ │ │ │ │ - lonlat = this.maxExtent.getCenterLonLat();
│ │ │ │ │ - this.center = lonlat.clone();
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - if (this.restrictedExtent != null) {
│ │ │ │ │ - // In 3.0, decide if we want to change interpretation of maxExtent.
│ │ │ │ │ - if (lonlat == null) {
│ │ │ │ │ - lonlat = this.center;
│ │ │ │ │ - }
│ │ │ │ │ - if (zoom == null) {
│ │ │ │ │ - zoom = this.getZoom();
│ │ │ │ │ - }
│ │ │ │ │ - var resolution = this.getResolutionForZoom(zoom);
│ │ │ │ │ - var extent = this.calculateBounds(lonlat, resolution);
│ │ │ │ │ - if (!this.restrictedExtent.containsBounds(extent)) {
│ │ │ │ │ - var maxCenter = this.restrictedExtent.getCenterLonLat();
│ │ │ │ │ - if (extent.getWidth() > this.restrictedExtent.getWidth()) {
│ │ │ │ │ - lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
│ │ │ │ │ - } else if (extent.left < this.restrictedExtent.left) {
│ │ │ │ │ - lonlat = lonlat.add(this.restrictedExtent.left -
│ │ │ │ │ - extent.left, 0);
│ │ │ │ │ - } else if (extent.right > this.restrictedExtent.right) {
│ │ │ │ │ - lonlat = lonlat.add(this.restrictedExtent.right -
│ │ │ │ │ - extent.right, 0);
│ │ │ │ │ - }
│ │ │ │ │ - if (extent.getHeight() > this.restrictedExtent.getHeight()) {
│ │ │ │ │ - lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
│ │ │ │ │ - } else if (extent.bottom < this.restrictedExtent.bottom) {
│ │ │ │ │ - lonlat = lonlat.add(0, this.restrictedExtent.bottom -
│ │ │ │ │ - extent.bottom);
│ │ │ │ │ - } else if (extent.top > this.restrictedExtent.top) {
│ │ │ │ │ - lonlat = lonlat.add(0, this.restrictedExtent.top -
│ │ │ │ │ - extent.top);
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ + getVertices: function(nodes) {
│ │ │ │ │ + var vertices = [];
│ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) {
│ │ │ │ │ + Array.prototype.push.apply(
│ │ │ │ │ + vertices, this.components[i].getVertices(nodes)
│ │ │ │ │ + );
│ │ │ │ │ }
│ │ │ │ │ + return vertices;
│ │ │ │ │ + },
│ │ │ │ │
│ │ │ │ │ - var zoomChanged = forceZoomChange || (
│ │ │ │ │ - (this.isValidZoomLevel(zoom)) &&
│ │ │ │ │ - (zoom != this.getZoom()));
│ │ │ │ │
│ │ │ │ │ - var centerChanged = (this.isValidLonLat(lonlat)) &&
│ │ │ │ │ - (!lonlat.equals(this.center));
│ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Collection"
│ │ │ │ │ +});
│ │ │ │ │ +/* ======================================================================
│ │ │ │ │ + OpenLayers/Geometry/MultiPoint.js
│ │ │ │ │ + ====================================================================== */
│ │ │ │ │
│ │ │ │ │ - // if neither center nor zoom will change, no need to do anything
│ │ │ │ │ - if (zoomChanged || centerChanged || dragging) {
│ │ │ │ │ - dragging || this.events.triggerEvent("movestart", {
│ │ │ │ │ - zoomChanged: zoomChanged
│ │ │ │ │ - });
│ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ │ + * full text of the license. */
│ │ │ │ │
│ │ │ │ │ - if (centerChanged) {
│ │ │ │ │ - if (!zoomChanged && this.center) {
│ │ │ │ │ - // if zoom hasnt changed, just slide layerContainer
│ │ │ │ │ - // (must be done before setting this.center to new value)
│ │ │ │ │ - this.centerLayerContainer(lonlat);
│ │ │ │ │ - }
│ │ │ │ │ - this.center = lonlat.clone();
│ │ │ │ │ - }
│ │ │ │ │ +/**
│ │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js
│ │ │ │ │ + * @requires OpenLayers/Geometry/Point.js
│ │ │ │ │ + */
│ │ │ │ │
│ │ │ │ │ - var res = zoomChanged ?
│ │ │ │ │ - this.getResolutionForZoom(zoom) : this.getResolution();
│ │ │ │ │ - // (re)set the layerContainerDiv's location
│ │ │ │ │ - if (zoomChanged || this.layerContainerOrigin == null) {
│ │ │ │ │ - this.layerContainerOrigin = this.getCachedCenter();
│ │ │ │ │ - this.layerContainerOriginPx.x = 0;
│ │ │ │ │ - this.layerContainerOriginPx.y = 0;
│ │ │ │ │ - this.applyTransform();
│ │ │ │ │ - var maxExtent = this.getMaxExtent({
│ │ │ │ │ - restricted: true
│ │ │ │ │ - });
│ │ │ │ │ - var maxExtentCenter = maxExtent.getCenterLonLat();
│ │ │ │ │ - var lonDelta = this.center.lon - maxExtentCenter.lon;
│ │ │ │ │ - var latDelta = maxExtentCenter.lat - this.center.lat;
│ │ │ │ │ - var extentWidth = Math.round(maxExtent.getWidth() / res);
│ │ │ │ │ - var extentHeight = Math.round(maxExtent.getHeight() / res);
│ │ │ │ │ - this.minPx = {
│ │ │ │ │ - x: (this.size.w - extentWidth) / 2 - lonDelta / res,
│ │ │ │ │ - y: (this.size.h - extentHeight) / 2 - latDelta / res
│ │ │ │ │ - };
│ │ │ │ │ - this.maxPx = {
│ │ │ │ │ - x: this.minPx.x + Math.round(maxExtent.getWidth() / res),
│ │ │ │ │ - y: this.minPx.y + Math.round(maxExtent.getHeight() / res)
│ │ │ │ │ - };
│ │ │ │ │ - }
│ │ │ │ │ +/**
│ │ │ │ │ + * Class: OpenLayers.Geometry.MultiPoint
│ │ │ │ │ + * MultiPoint is a collection of Points. Create a new instance with the
│ │ │ │ │ + * constructor.
│ │ │ │ │ + *
│ │ │ │ │ + * Inherits from:
│ │ │ │ │ + * -
│ │ │ │ │ + * -
│ │ │ │ │ + */
│ │ │ │ │ +OpenLayers.Geometry.MultiPoint = OpenLayers.Class(
│ │ │ │ │ + OpenLayers.Geometry.Collection, {
│ │ │ │ │
│ │ │ │ │ - if (zoomChanged) {
│ │ │ │ │ - this.zoom = zoom;
│ │ │ │ │ - this.resolution = res;
│ │ │ │ │ - }
│ │ │ │ │ + /**
│ │ │ │ │ + * Property: componentTypes
│ │ │ │ │ + * {Array(String)} An array of class names representing the types of
│ │ │ │ │ + * components that the collection can include. A null value means the
│ │ │ │ │ + * component types are not restricted.
│ │ │ │ │ + */
│ │ │ │ │ + componentTypes: ["OpenLayers.Geometry.Point"],
│ │ │ │ │
│ │ │ │ │ - var bounds = this.getExtent();
│ │ │ │ │ + /**
│ │ │ │ │ + * Constructor: OpenLayers.Geometry.MultiPoint
│ │ │ │ │ + * Create a new MultiPoint Geometry
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * components - {Array()}
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {}
│ │ │ │ │ + */
│ │ │ │ │
│ │ │ │ │ - //send the move call to the baselayer and all the overlays
│ │ │ │ │ + /**
│ │ │ │ │ + * APIMethod: addPoint
│ │ │ │ │ + * Wrapper for
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * point - {} Point to be added
│ │ │ │ │ + * index - {Integer} Optional index
│ │ │ │ │ + */
│ │ │ │ │ + addPoint: function(point, index) {
│ │ │ │ │ + this.addComponent(point, index);
│ │ │ │ │ + },
│ │ │ │ │
│ │ │ │ │ - if (this.baseLayer.visibility) {
│ │ │ │ │ - this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
│ │ │ │ │ - options.dragging || this.baseLayer.events.triggerEvent(
│ │ │ │ │ - "moveend", {
│ │ │ │ │ - zoomChanged: zoomChanged
│ │ │ │ │ - }
│ │ │ │ │ - );
│ │ │ │ │ - }
│ │ │ │ │ + /**
│ │ │ │ │ + * APIMethod: removePoint
│ │ │ │ │ + * Wrapper for
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * point - {} Point to be removed
│ │ │ │ │ + */
│ │ │ │ │ + removePoint: function(point) {
│ │ │ │ │ + this.removeComponent(point);
│ │ │ │ │ + },
│ │ │ │ │
│ │ │ │ │ - bounds = this.baseLayer.getExtent();
│ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
│ │ │ │ │ + });
│ │ │ │ │ +/* ======================================================================
│ │ │ │ │ + OpenLayers/Geometry/Curve.js
│ │ │ │ │ + ====================================================================== */
│ │ │ │ │
│ │ │ │ │ - for (var i = this.layers.length - 1; i >= 0; --i) {
│ │ │ │ │ - var layer = this.layers[i];
│ │ │ │ │ - if (layer !== this.baseLayer && !layer.isBaseLayer) {
│ │ │ │ │ - var inRange = layer.calculateInRange();
│ │ │ │ │ - if (layer.inRange != inRange) {
│ │ │ │ │ - // the inRange property has changed. If the layer is
│ │ │ │ │ - // no longer in range, we turn it off right away. If
│ │ │ │ │ - // the layer is no longer out of range, the moveTo
│ │ │ │ │ - // call below will turn on the layer.
│ │ │ │ │ - layer.inRange = inRange;
│ │ │ │ │ - if (!inRange) {
│ │ │ │ │ - layer.display(false);
│ │ │ │ │ - }
│ │ │ │ │ - this.events.triggerEvent("changelayer", {
│ │ │ │ │ - layer: layer,
│ │ │ │ │ - property: "visibility"
│ │ │ │ │ - });
│ │ │ │ │ - }
│ │ │ │ │ - if (inRange && layer.visibility) {
│ │ │ │ │ - layer.moveTo(bounds, zoomChanged, options.dragging);
│ │ │ │ │ - options.dragging || layer.events.triggerEvent(
│ │ │ │ │ - "moveend", {
│ │ │ │ │ - zoomChanged: zoomChanged
│ │ │ │ │ - }
│ │ │ │ │ - );
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ │ + * full text of the license. */
│ │ │ │ │
│ │ │ │ │ - this.events.triggerEvent("move");
│ │ │ │ │ - dragging || this.events.triggerEvent("moveend");
│ │ │ │ │ +/**
│ │ │ │ │ + * @requires OpenLayers/Geometry/MultiPoint.js
│ │ │ │ │ + */
│ │ │ │ │
│ │ │ │ │ - if (zoomChanged) {
│ │ │ │ │ - //redraw popups
│ │ │ │ │ - for (var i = 0, len = this.popups.length; i < len; i++) {
│ │ │ │ │ - this.popups[i].updatePosition();
│ │ │ │ │ - }
│ │ │ │ │ - this.events.triggerEvent("zoomend");
│ │ │ │ │ - }
│ │ │ │ │ - }
│ │ │ │ │ - },
│ │ │ │ │ +/**
│ │ │ │ │ + * Class: OpenLayers.Geometry.Curve
│ │ │ │ │ + * A Curve is a MultiPoint, whose points are assumed to be connected. To
│ │ │ │ │ + * this end, we provide a "getLength()" function, which iterates through
│ │ │ │ │ + * the points, summing the distances between them.
│ │ │ │ │ + *
│ │ │ │ │ + * Inherits:
│ │ │ │ │ + * -
│ │ │ │ │ + */
│ │ │ │ │ +OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * Method: centerLayerContainer
│ │ │ │ │ - * This function takes care to recenter the layerContainerDiv.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * lonlat - {}
│ │ │ │ │ + /**
│ │ │ │ │ + * Property: componentTypes
│ │ │ │ │ + * {Array(String)} An array of class names representing the types of
│ │ │ │ │ + * components that the collection can include. A null
│ │ │ │ │ + * value means the component types are not restricted.
│ │ │ │ │ */
│ │ │ │ │ - centerLayerContainer: function(lonlat) {
│ │ │ │ │ - var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
│ │ │ │ │ - var newPx = this.getViewPortPxFromLonLat(lonlat);
│ │ │ │ │ -
│ │ │ │ │ - if ((originPx != null) && (newPx != null)) {
│ │ │ │ │ - var oldLeft = this.layerContainerOriginPx.x;
│ │ │ │ │ - var oldTop = this.layerContainerOriginPx.y;
│ │ │ │ │ - var newLeft = Math.round(originPx.x - newPx.x);
│ │ │ │ │ - var newTop = Math.round(originPx.y - newPx.y);
│ │ │ │ │ - this.applyTransform(
│ │ │ │ │ - (this.layerContainerOriginPx.x = newLeft),
│ │ │ │ │ - (this.layerContainerOriginPx.y = newTop));
│ │ │ │ │ - var dx = oldLeft - newLeft;
│ │ │ │ │ - var dy = oldTop - newTop;
│ │ │ │ │ - this.minPx.x -= dx;
│ │ │ │ │ - this.maxPx.x -= dx;
│ │ │ │ │ - this.minPx.y -= dy;
│ │ │ │ │ - this.maxPx.y -= dy;
│ │ │ │ │ - }
│ │ │ │ │ - },
│ │ │ │ │ + componentTypes: ["OpenLayers.Geometry.Point"],
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: isValidZoomLevel
│ │ │ │ │ + * Constructor: OpenLayers.Geometry.Curve
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * zoomLevel - {Integer}
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Boolean} Whether or not the zoom level passed in is non-null and
│ │ │ │ │ - * within the min/max range of zoom levels.
│ │ │ │ │ + * point - {Array()}
│ │ │ │ │ */
│ │ │ │ │ - isValidZoomLevel: function(zoomLevel) {
│ │ │ │ │ - return ((zoomLevel != null) &&
│ │ │ │ │ - (zoomLevel >= 0) &&
│ │ │ │ │ - (zoomLevel < this.getNumZoomLevels()));
│ │ │ │ │ - },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: isValidLonLat
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * lonlat - {}
│ │ │ │ │ + * APIMethod: getLength
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Boolean} Whether or not the lonlat passed in is non-null and within
│ │ │ │ │ - * the maxExtent bounds
│ │ │ │ │ + * {Float} The length of the curve
│ │ │ │ │ */
│ │ │ │ │ - isValidLonLat: function(lonlat) {
│ │ │ │ │ - var valid = false;
│ │ │ │ │ - if (lonlat != null) {
│ │ │ │ │ - var maxExtent = this.getMaxExtent();
│ │ │ │ │ - var worldBounds = this.baseLayer.wrapDateLine && maxExtent;
│ │ │ │ │ - valid = maxExtent.containsLonLat(lonlat, {
│ │ │ │ │ - worldBounds: worldBounds
│ │ │ │ │ - });
│ │ │ │ │ + getLength: function() {
│ │ │ │ │ + var length = 0.0;
│ │ │ │ │ + if (this.components && (this.components.length > 1)) {
│ │ │ │ │ + for (var i = 1, len = this.components.length; i < len; i++) {
│ │ │ │ │ + length += this.components[i - 1].distanceTo(this.components[i]);
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ - return valid;
│ │ │ │ │ + return length;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Layer Options */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Accessor functions to Layer Options parameters */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getProjection
│ │ │ │ │ - * This method returns a string representing the projection. In
│ │ │ │ │ - * the case of projection support, this will be the srsCode which
│ │ │ │ │ - * is loaded -- otherwise it will simply be the string value that
│ │ │ │ │ - * was passed to the projection at startup.
│ │ │ │ │ + * APIMethod: getGeodesicLength
│ │ │ │ │ + * Calculate the approximate length of the geometry were it projected onto
│ │ │ │ │ + * the earth.
│ │ │ │ │ *
│ │ │ │ │ - * FIXME: In 3.0, we will remove getProjectionObject, and instead
│ │ │ │ │ - * return a Projection object from this function.
│ │ │ │ │ + * projection - {} The spatial reference system
│ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is
│ │ │ │ │ + * assumed.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {String} The Projection string from the base layer or null.
│ │ │ │ │ - */
│ │ │ │ │ - getProjection: function() {
│ │ │ │ │ - var projection = this.getProjectionObject();
│ │ │ │ │ - return projection ? projection.getCode() : null;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getProjectionObject
│ │ │ │ │ - * Returns the projection obect from the baselayer.
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} The Projection of the base layer.
│ │ │ │ │ + * {Float} The appoximate geodesic length of the geometry in meters.
│ │ │ │ │ */
│ │ │ │ │ - getProjectionObject: function() {
│ │ │ │ │ - var projection = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - projection = this.baseLayer.projection;
│ │ │ │ │ + getGeodesicLength: function(projection) {
│ │ │ │ │ + var geom = this; // so we can work with a clone if needed
│ │ │ │ │ + if (projection) {
│ │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326");
│ │ │ │ │ + if (!gg.equals(projection)) {
│ │ │ │ │ + geom = this.clone().transform(projection, gg);
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ - return projection;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getMaxResolution
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {String} The Map's Maximum Resolution
│ │ │ │ │ - */
│ │ │ │ │ - getMaxResolution: function() {
│ │ │ │ │ - var maxResolution = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - maxResolution = this.baseLayer.maxResolution;
│ │ │ │ │ + var length = 0.0;
│ │ │ │ │ + if (geom.components && (geom.components.length > 1)) {
│ │ │ │ │ + var p1, p2;
│ │ │ │ │ + for (var i = 1, len = geom.components.length; i < len; i++) {
│ │ │ │ │ + p1 = geom.components[i - 1];
│ │ │ │ │ + p2 = geom.components[i];
│ │ │ │ │ + // this returns km and requires lon/lat properties
│ │ │ │ │ + length += OpenLayers.Util.distVincenty({
│ │ │ │ │ + lon: p1.x,
│ │ │ │ │ + lat: p1.y
│ │ │ │ │ + }, {
│ │ │ │ │ + lon: p2.x,
│ │ │ │ │ + lat: p2.y
│ │ │ │ │ + });
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ - return maxResolution;
│ │ │ │ │ + // convert to m
│ │ │ │ │ + return length * 1000;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Curve"
│ │ │ │ │ +});
│ │ │ │ │ +/* ======================================================================
│ │ │ │ │ + OpenLayers/Geometry/LineString.js
│ │ │ │ │ + ====================================================================== */
│ │ │ │ │ +
│ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ │ + * full text of the license. */
│ │ │ │ │ +
│ │ │ │ │ +/**
│ │ │ │ │ + * @requires OpenLayers/Geometry/Curve.js
│ │ │ │ │ + */
│ │ │ │ │ +
│ │ │ │ │ +/**
│ │ │ │ │ + * Class: OpenLayers.Geometry.LineString
│ │ │ │ │ + * A LineString is a Curve which, once two points have been added to it, can
│ │ │ │ │ + * never be less than two points long.
│ │ │ │ │ + *
│ │ │ │ │ + * Inherits from:
│ │ │ │ │ + * -
│ │ │ │ │ + */
│ │ │ │ │ +OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
│ │ │ │ │ +
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getMaxExtent
│ │ │ │ │ + * Constructor: OpenLayers.Geometry.LineString
│ │ │ │ │ + * Create a new LineString geometry
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * options - {Object}
│ │ │ │ │ - *
│ │ │ │ │ - * Allowed Options:
│ │ │ │ │ - * restricted - {Boolean} If true, returns restricted extent (if it is
│ │ │ │ │ - * available.)
│ │ │ │ │ + * points - {Array()} An array of points used to
│ │ │ │ │ + * generate the linestring
│ │ │ │ │ *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} The maxExtent property as set on the current
│ │ │ │ │ - * baselayer, unless the 'restricted' option is set, in which case
│ │ │ │ │ - * the 'restrictedExtent' option from the map is returned (if it
│ │ │ │ │ - * is set).
│ │ │ │ │ */
│ │ │ │ │ - getMaxExtent: function(options) {
│ │ │ │ │ - var maxExtent = null;
│ │ │ │ │ - if (options && options.restricted && this.restrictedExtent) {
│ │ │ │ │ - maxExtent = this.restrictedExtent;
│ │ │ │ │ - } else if (this.baseLayer != null) {
│ │ │ │ │ - maxExtent = this.baseLayer.maxExtent;
│ │ │ │ │ - }
│ │ │ │ │ - return maxExtent;
│ │ │ │ │ - },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getNumZoomLevels
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Integer} The total number of zoom levels that can be displayed by the
│ │ │ │ │ - * current baseLayer.
│ │ │ │ │ + * APIMethod: removeComponent
│ │ │ │ │ + * Only allows removal of a point if there are three or more points in
│ │ │ │ │ + * the linestring. (otherwise the result would be just a single point)
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * point - {} The point to be removed
│ │ │ │ │ + *
│ │ │ │ │ + * Returns:
│ │ │ │ │ + * {Boolean} The component was removed.
│ │ │ │ │ */
│ │ │ │ │ - getNumZoomLevels: function() {
│ │ │ │ │ - var numZoomLevels = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - numZoomLevels = this.baseLayer.numZoomLevels;
│ │ │ │ │ + removeComponent: function(point) {
│ │ │ │ │ + var removed = this.components && (this.components.length > 2);
│ │ │ │ │ + if (removed) {
│ │ │ │ │ + OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
│ │ │ │ │ + arguments);
│ │ │ │ │ }
│ │ │ │ │ - return numZoomLevels;
│ │ │ │ │ + return removed;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Baselayer Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions, all publicly exposed */
│ │ │ │ │ - /* in the API?, are all merely wrappers to the */
│ │ │ │ │ - /* the same calls on whatever layer is set as */
│ │ │ │ │ - /* the current base layer */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getExtent
│ │ │ │ │ - *
│ │ │ │ │ + * APIMethod: intersects
│ │ │ │ │ + * Test for instersection between two geometries. This is a cheapo
│ │ │ │ │ + * implementation of the Bently-Ottmann algorigithm. It doesn't
│ │ │ │ │ + * really keep track of a sweep line data structure. It is closer
│ │ │ │ │ + * to the brute force method, except that segments are sorted and
│ │ │ │ │ + * potential intersections are only calculated when bounding boxes
│ │ │ │ │ + * intersect.
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * geometry - {}
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} A Bounds object which represents the lon/lat
│ │ │ │ │ - * bounds of the current viewPort.
│ │ │ │ │ - * If no baselayer is set, returns null.
│ │ │ │ │ + * {Boolean} The input geometry intersects this geometry.
│ │ │ │ │ */
│ │ │ │ │ - getExtent: function() {
│ │ │ │ │ - var extent = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - extent = this.baseLayer.getExtent();
│ │ │ │ │ + intersects: function(geometry) {
│ │ │ │ │ + var intersect = false;
│ │ │ │ │ + var type = geometry.CLASS_NAME;
│ │ │ │ │ + if (type == "OpenLayers.Geometry.LineString" ||
│ │ │ │ │ + type == "OpenLayers.Geometry.LinearRing" ||
│ │ │ │ │ + type == "OpenLayers.Geometry.Point") {
│ │ │ │ │ + var segs1 = this.getSortedSegments();
│ │ │ │ │ + var segs2;
│ │ │ │ │ + if (type == "OpenLayers.Geometry.Point") {
│ │ │ │ │ + segs2 = [{
│ │ │ │ │ + x1: geometry.x,
│ │ │ │ │ + y1: geometry.y,
│ │ │ │ │ + x2: geometry.x,
│ │ │ │ │ + y2: geometry.y
│ │ │ │ │ + }];
│ │ │ │ │ + } else {
│ │ │ │ │ + segs2 = geometry.getSortedSegments();
│ │ │ │ │ + }
│ │ │ │ │ + var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
│ │ │ │ │ + seg2, seg2y1, seg2y2;
│ │ │ │ │ + // sweep right
│ │ │ │ │ + outer: for (var i = 0, len = segs1.length; i < len; ++i) {
│ │ │ │ │ + seg1 = segs1[i];
│ │ │ │ │ + seg1x1 = seg1.x1;
│ │ │ │ │ + seg1x2 = seg1.x2;
│ │ │ │ │ + seg1y1 = seg1.y1;
│ │ │ │ │ + seg1y2 = seg1.y2;
│ │ │ │ │ + inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) {
│ │ │ │ │ + seg2 = segs2[j];
│ │ │ │ │ + if (seg2.x1 > seg1x2) {
│ │ │ │ │ + // seg1 still left of seg2
│ │ │ │ │ + break;
│ │ │ │ │ + }
│ │ │ │ │ + if (seg2.x2 < seg1x1) {
│ │ │ │ │ + // seg2 still left of seg1
│ │ │ │ │ + continue;
│ │ │ │ │ + }
│ │ │ │ │ + seg2y1 = seg2.y1;
│ │ │ │ │ + seg2y2 = seg2.y2;
│ │ │ │ │ + if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
│ │ │ │ │ + // seg2 above seg1
│ │ │ │ │ + continue;
│ │ │ │ │ + }
│ │ │ │ │ + if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
│ │ │ │ │ + // seg2 below seg1
│ │ │ │ │ + continue;
│ │ │ │ │ + }
│ │ │ │ │ + if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
│ │ │ │ │ + intersect = true;
│ │ │ │ │ + break outer;
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + } else {
│ │ │ │ │ + intersect = geometry.intersects(this);
│ │ │ │ │ }
│ │ │ │ │ - return extent;
│ │ │ │ │ + return intersect;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getResolution
│ │ │ │ │ - *
│ │ │ │ │ + * Method: getSortedSegments
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Float} The current resolution of the map.
│ │ │ │ │ - * If no baselayer is set, returns null.
│ │ │ │ │ + * {Array} An array of segment objects. Segment objects have properties
│ │ │ │ │ + * x1, y1, x2, and y2. The start point is represented by x1 and y1.
│ │ │ │ │ + * The end point is represented by x2 and y2. Start and end are
│ │ │ │ │ + * ordered so that x1 < x2.
│ │ │ │ │ */
│ │ │ │ │ - getResolution: function() {
│ │ │ │ │ - var resolution = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - resolution = this.baseLayer.getResolution();
│ │ │ │ │ - } else if (this.allOverlays === true && this.layers.length > 0) {
│ │ │ │ │ - // while adding the 1st layer to the map in allOverlays mode,
│ │ │ │ │ - // this.baseLayer is not set yet when we need the resolution
│ │ │ │ │ - // for calculateInRange.
│ │ │ │ │ - resolution = this.layers[0].getResolution();
│ │ │ │ │ + getSortedSegments: function() {
│ │ │ │ │ + var numSeg = this.components.length - 1;
│ │ │ │ │ + var segments = new Array(numSeg),
│ │ │ │ │ + point1, point2;
│ │ │ │ │ + for (var i = 0; i < numSeg; ++i) {
│ │ │ │ │ + point1 = this.components[i];
│ │ │ │ │ + point2 = this.components[i + 1];
│ │ │ │ │ + if (point1.x < point2.x) {
│ │ │ │ │ + segments[i] = {
│ │ │ │ │ + x1: point1.x,
│ │ │ │ │ + y1: point1.y,
│ │ │ │ │ + x2: point2.x,
│ │ │ │ │ + y2: point2.y
│ │ │ │ │ + };
│ │ │ │ │ + } else {
│ │ │ │ │ + segments[i] = {
│ │ │ │ │ + x1: point2.x,
│ │ │ │ │ + y1: point2.y,
│ │ │ │ │ + x2: point1.x,
│ │ │ │ │ + y2: point1.y
│ │ │ │ │ + };
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ - return resolution;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getUnits
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Float} The current units of the map.
│ │ │ │ │ - * If no baselayer is set, returns null.
│ │ │ │ │ - */
│ │ │ │ │ - getUnits: function() {
│ │ │ │ │ - var units = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - units = this.baseLayer.units;
│ │ │ │ │ + // more efficient to define this somewhere static
│ │ │ │ │ + function byX1(seg1, seg2) {
│ │ │ │ │ + return seg1.x1 - seg2.x1;
│ │ │ │ │ }
│ │ │ │ │ - return units;
│ │ │ │ │ + return segments.sort(byX1);
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getScale
│ │ │ │ │ - *
│ │ │ │ │ + * Method: splitWithSegment
│ │ │ │ │ + * Split this geometry with the given segment.
│ │ │ │ │ + *
│ │ │ │ │ + * Parameters:
│ │ │ │ │ + * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
│ │ │ │ │ + * segment endpoint coordinates.
│ │ │ │ │ + * options - {Object} Properties of this object will be used to determine
│ │ │ │ │ + * how the split is conducted.
│ │ │ │ │ + *
│ │ │ │ │ + * Valid options:
│ │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is
│ │ │ │ │ + * true. If false, a vertex on the source segment must be within the
│ │ │ │ │ + * tolerance distance of the intersection to be considered a split.
│ │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections
│ │ │ │ │ + * within the tolerance distance of one of the source segment's
│ │ │ │ │ + * endpoints will be assumed to occur at the endpoint.
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Float} The current scale denominator of the map.
│ │ │ │ │ - * If no baselayer is set, returns null.
│ │ │ │ │ + * {Object} An object with *lines* and *points* properties. If the given
│ │ │ │ │ + * segment intersects this linestring, the lines array will reference
│ │ │ │ │ + * geometries that result from the split. The points array will contain
│ │ │ │ │ + * all intersection points. Intersection points are sorted along the
│ │ │ │ │ + * segment (in order from x1,y1 to x2,y2).
│ │ │ │ │ */
│ │ │ │ │ - getScale: function() {
│ │ │ │ │ - var scale = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - var res = this.getResolution();
│ │ │ │ │ - var units = this.baseLayer.units;
│ │ │ │ │ - scale = OpenLayers.Util.getScaleFromResolution(res, units);
│ │ │ │ │ + splitWithSegment: function(seg, options) {
│ │ │ │ │ + var edge = !(options && options.edge === false);
│ │ │ │ │ + var tolerance = options && options.tolerance;
│ │ │ │ │ + var lines = [];
│ │ │ │ │ + var verts = this.getVertices();
│ │ │ │ │ + var points = [];
│ │ │ │ │ + var intersections = [];
│ │ │ │ │ + var split = false;
│ │ │ │ │ + var vert1, vert2, point;
│ │ │ │ │ + var node, vertex, target;
│ │ │ │ │ + var interOptions = {
│ │ │ │ │ + point: true,
│ │ │ │ │ + tolerance: tolerance
│ │ │ │ │ + };
│ │ │ │ │ + var result = null;
│ │ │ │ │ + for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {
│ │ │ │ │ + vert1 = verts[i];
│ │ │ │ │ + points.push(vert1.clone());
│ │ │ │ │ + vert2 = verts[i + 1];
│ │ │ │ │ + target = {
│ │ │ │ │ + x1: vert1.x,
│ │ │ │ │ + y1: vert1.y,
│ │ │ │ │ + x2: vert2.x,
│ │ │ │ │ + y2: vert2.y
│ │ │ │ │ + };
│ │ │ │ │ + point = OpenLayers.Geometry.segmentsIntersect(
│ │ │ │ │ + seg, target, interOptions
│ │ │ │ │ + );
│ │ │ │ │ + if (point instanceof OpenLayers.Geometry.Point) {
│ │ │ │ │ + if ((point.x === seg.x1 && point.y === seg.y1) ||
│ │ │ │ │ + (point.x === seg.x2 && point.y === seg.y2) ||
│ │ │ │ │ + point.equals(vert1) || point.equals(vert2)) {
│ │ │ │ │ + vertex = true;
│ │ │ │ │ + } else {
│ │ │ │ │ + vertex = false;
│ │ │ │ │ + }
│ │ │ │ │ + if (vertex || edge) {
│ │ │ │ │ + // push intersections different than the previous
│ │ │ │ │ + if (!point.equals(intersections[intersections.length - 1])) {
│ │ │ │ │ + intersections.push(point.clone());
│ │ │ │ │ + }
│ │ │ │ │ + if (i === 0) {
│ │ │ │ │ + if (point.equals(vert1)) {
│ │ │ │ │ + continue;
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + if (point.equals(vert2)) {
│ │ │ │ │ + continue;
│ │ │ │ │ + }
│ │ │ │ │ + split = true;
│ │ │ │ │ + if (!point.equals(vert1)) {
│ │ │ │ │ + points.push(point);
│ │ │ │ │ + }
│ │ │ │ │ + lines.push(new OpenLayers.Geometry.LineString(points));
│ │ │ │ │ + points = [point.clone()];
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ - return scale;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getZoomForExtent
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * bounds - {}
│ │ │ │ │ - * closest - {Boolean} Find the zoom level that most closely fits the
│ │ │ │ │ - * specified bounds. Note that this may result in a zoom that does
│ │ │ │ │ - * not exactly contain the entire extent.
│ │ │ │ │ - * Default is false.
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Integer} A suitable zoom level for the specified bounds.
│ │ │ │ │ - * If no baselayer is set, returns null.
│ │ │ │ │ - */
│ │ │ │ │ - getZoomForExtent: function(bounds, closest) {
│ │ │ │ │ - var zoom = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - zoom = this.baseLayer.getZoomForExtent(bounds, closest);
│ │ │ │ │ + if (split) {
│ │ │ │ │ + points.push(vert2.clone());
│ │ │ │ │ + lines.push(new OpenLayers.Geometry.LineString(points));
│ │ │ │ │ }
│ │ │ │ │ - return zoom;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getResolutionForZoom
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * zoom - {Float}
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {Float} A suitable resolution for the specified zoom. If no baselayer
│ │ │ │ │ - * is set, returns null.
│ │ │ │ │ - */
│ │ │ │ │ - getResolutionForZoom: function(zoom) {
│ │ │ │ │ - var resolution = null;
│ │ │ │ │ - if (this.baseLayer) {
│ │ │ │ │ - resolution = this.baseLayer.getResolutionForZoom(zoom);
│ │ │ │ │ + if (intersections.length > 0) {
│ │ │ │ │ + // sort intersections along segment
│ │ │ │ │ + var xDir = seg.x1 < seg.x2 ? 1 : -1;
│ │ │ │ │ + var yDir = seg.y1 < seg.y2 ? 1 : -1;
│ │ │ │ │ + result = {
│ │ │ │ │ + lines: lines,
│ │ │ │ │ + points: intersections.sort(function(p1, p2) {
│ │ │ │ │ + return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
│ │ │ │ │ + })
│ │ │ │ │ + };
│ │ │ │ │ }
│ │ │ │ │ - return resolution;
│ │ │ │ │ + return result;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getZoomForResolution
│ │ │ │ │ + * Method: split
│ │ │ │ │ + * Use this geometry (the source) to attempt to split a target geometry.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * resolution - {Float}
│ │ │ │ │ - * closest - {Boolean} Find the zoom level that corresponds to the absolute
│ │ │ │ │ - * closest resolution, which may result in a zoom whose corresponding
│ │ │ │ │ - * resolution is actually smaller than we would have desired (if this
│ │ │ │ │ - * is being called from a getZoomForExtent() call, then this means that
│ │ │ │ │ - * the returned zoom index might not actually contain the entire
│ │ │ │ │ - * extent specified... but it'll be close).
│ │ │ │ │ - * Default is false.
│ │ │ │ │ + * target - {} The target geometry.
│ │ │ │ │ + * options - {Object} Properties of this object will be used to determine
│ │ │ │ │ + * how the split is conducted.
│ │ │ │ │ + *
│ │ │ │ │ + * Valid options:
│ │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target
│ │ │ │ │ + * geometry. Default is false.
│ │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is
│ │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance
│ │ │ │ │ + * distance of the intersection to be considered a split.
│ │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections
│ │ │ │ │ + * within the tolerance distance of an existing vertex on the source
│ │ │ │ │ + * will be assumed to occur at the vertex.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {Integer} A suitable zoom level for the specified resolution.
│ │ │ │ │ - * If no baselayer is set, returns null.
│ │ │ │ │ - */
│ │ │ │ │ - getZoomForResolution: function(resolution, closest) {
│ │ │ │ │ - var zoom = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - zoom = this.baseLayer.getZoomForResolution(resolution, closest);
│ │ │ │ │ - }
│ │ │ │ │ - return zoom;
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Zooming Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions, all publicly exposed */
│ │ │ │ │ - /* in the API, are all merely wrappers to the */
│ │ │ │ │ - /* the setCenter() function */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: zoomTo
│ │ │ │ │ - * Zoom to a specific zoom level. Zooming will be animated unless the map
│ │ │ │ │ - * is configured with {zoomMethod: null}. To zoom without animation, use
│ │ │ │ │ - * without a lonlat argument.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * zoom - {Integer}
│ │ │ │ │ + * {Array} A list of geometries (of this same type as the target) that
│ │ │ │ │ + * result from splitting the target with the source geometry. The
│ │ │ │ │ + * source and target geometry will remain unmodified. If no split
│ │ │ │ │ + * results, null will be returned. If mutual is true and a split
│ │ │ │ │ + * results, return will be an array of two arrays - the first will be
│ │ │ │ │ + * all geometries that result from splitting the source geometry and
│ │ │ │ │ + * the second will be all geometries that result from splitting the
│ │ │ │ │ + * target geometry.
│ │ │ │ │ */
│ │ │ │ │ - zoomTo: function(zoom, xy) {
│ │ │ │ │ - // non-API arguments:
│ │ │ │ │ - // xy - {} optional zoom origin
│ │ │ │ │ -
│ │ │ │ │ - var map = this;
│ │ │ │ │ - if (map.isValidZoomLevel(zoom)) {
│ │ │ │ │ - if (map.baseLayer.wrapDateLine) {
│ │ │ │ │ - zoom = map.adjustZoom(zoom);
│ │ │ │ │ - }
│ │ │ │ │ - if (map.zoomTween) {
│ │ │ │ │ - var currentRes = map.getResolution(),
│ │ │ │ │ - targetRes = map.getResolutionForZoom(zoom),
│ │ │ │ │ - start = {
│ │ │ │ │ - scale: 1
│ │ │ │ │ - },
│ │ │ │ │ - end = {
│ │ │ │ │ - scale: currentRes / targetRes
│ │ │ │ │ - };
│ │ │ │ │ - if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {
│ │ │ │ │ - // update the end scale, and reuse the running zoomTween
│ │ │ │ │ - map.zoomTween.finish = {
│ │ │ │ │ - scale: map.zoomTween.finish.scale * end.scale
│ │ │ │ │ - };
│ │ │ │ │ - } else {
│ │ │ │ │ - if (!xy) {
│ │ │ │ │ - var size = map.getSize();
│ │ │ │ │ - xy = {
│ │ │ │ │ - x: size.w / 2,
│ │ │ │ │ - y: size.h / 2
│ │ │ │ │ - };
│ │ │ │ │ - }
│ │ │ │ │ - map.zoomTween.start(start, end, map.zoomDuration, {
│ │ │ │ │ - minFrameRate: 50, // don't spend much time zooming
│ │ │ │ │ - callbacks: {
│ │ │ │ │ - eachStep: function(data) {
│ │ │ │ │ - var containerOrigin = map.layerContainerOriginPx,
│ │ │ │ │ - scale = data.scale,
│ │ │ │ │ - dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,
│ │ │ │ │ - dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;
│ │ │ │ │ - map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);
│ │ │ │ │ - },
│ │ │ │ │ - done: function(data) {
│ │ │ │ │ - map.applyTransform();
│ │ │ │ │ - var resolution = map.getResolution() / data.scale,
│ │ │ │ │ - zoom = map.getZoomForResolution(resolution, true)
│ │ │ │ │ - map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true);
│ │ │ │ │ + split: function(target, options) {
│ │ │ │ │ + var results = null;
│ │ │ │ │ + var mutual = options && options.mutual;
│ │ │ │ │ + var sourceSplit, targetSplit, sourceParts, targetParts;
│ │ │ │ │ + if (target instanceof OpenLayers.Geometry.LineString) {
│ │ │ │ │ + var verts = this.getVertices();
│ │ │ │ │ + var vert1, vert2, seg, splits, lines, point;
│ │ │ │ │ + var points = [];
│ │ │ │ │ + sourceParts = [];
│ │ │ │ │ + for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {
│ │ │ │ │ + vert1 = verts[i];
│ │ │ │ │ + vert2 = verts[i + 1];
│ │ │ │ │ + seg = {
│ │ │ │ │ + x1: vert1.x,
│ │ │ │ │ + y1: vert1.y,
│ │ │ │ │ + x2: vert2.x,
│ │ │ │ │ + y2: vert2.y
│ │ │ │ │ + };
│ │ │ │ │ + targetParts = targetParts || [target];
│ │ │ │ │ + if (mutual) {
│ │ │ │ │ + points.push(vert1.clone());
│ │ │ │ │ + }
│ │ │ │ │ + for (var j = 0; j < targetParts.length; ++j) {
│ │ │ │ │ + splits = targetParts[j].splitWithSegment(seg, options);
│ │ │ │ │ + if (splits) {
│ │ │ │ │ + // splice in new features
│ │ │ │ │ + lines = splits.lines;
│ │ │ │ │ + if (lines.length > 0) {
│ │ │ │ │ + lines.unshift(j, 1);
│ │ │ │ │ + Array.prototype.splice.apply(targetParts, lines);
│ │ │ │ │ + j += lines.length - 2;
│ │ │ │ │ + }
│ │ │ │ │ + if (mutual) {
│ │ │ │ │ + for (var k = 0, len = splits.points.length; k < len; ++k) {
│ │ │ │ │ + point = splits.points[k];
│ │ │ │ │ + if (!point.equals(vert1)) {
│ │ │ │ │ + points.push(point);
│ │ │ │ │ + sourceParts.push(new OpenLayers.Geometry.LineString(points));
│ │ │ │ │ + if (point.equals(vert2)) {
│ │ │ │ │ + points = [];
│ │ │ │ │ + } else {
│ │ │ │ │ + points = [point.clone()];
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ }
│ │ │ │ │ - });
│ │ │ │ │ + }
│ │ │ │ │ }
│ │ │ │ │ - } else {
│ │ │ │ │ - var center = xy ?
│ │ │ │ │ - map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :
│ │ │ │ │ - null;
│ │ │ │ │ - map.setCenter(center, zoom);
│ │ │ │ │ }
│ │ │ │ │ + if (mutual && sourceParts.length > 0 && points.length > 0) {
│ │ │ │ │ + points.push(vert2.clone());
│ │ │ │ │ + sourceParts.push(new OpenLayers.Geometry.LineString(points));
│ │ │ │ │ + }
│ │ │ │ │ + } else {
│ │ │ │ │ + results = target.splitWith(this, options);
│ │ │ │ │ }
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: zoomIn
│ │ │ │ │ - *
│ │ │ │ │ - */
│ │ │ │ │ - zoomIn: function() {
│ │ │ │ │ - this.zoomTo(this.getZoom() + 1);
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: zoomOut
│ │ │ │ │ - *
│ │ │ │ │ - */
│ │ │ │ │ - zoomOut: function() {
│ │ │ │ │ - this.zoomTo(this.getZoom() - 1);
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: zoomToExtent
│ │ │ │ │ - * Zoom to the passed in bounds, recenter
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * bounds - {|Array} If provided as an array, the array
│ │ │ │ │ - * should consist of four values (left, bottom, right, top).
│ │ │ │ │ - * closest - {Boolean} Find the zoom level that most closely fits the
│ │ │ │ │ - * specified bounds. Note that this may result in a zoom that does
│ │ │ │ │ - * not exactly contain the entire extent.
│ │ │ │ │ - * Default is false.
│ │ │ │ │ - *
│ │ │ │ │ - */
│ │ │ │ │ - zoomToExtent: function(bounds, closest) {
│ │ │ │ │ - if (!(bounds instanceof OpenLayers.Bounds)) {
│ │ │ │ │ - bounds = new OpenLayers.Bounds(bounds);
│ │ │ │ │ + if (targetParts && targetParts.length > 1) {
│ │ │ │ │ + targetSplit = true;
│ │ │ │ │ + } else {
│ │ │ │ │ + targetParts = [];
│ │ │ │ │ }
│ │ │ │ │ - var center = bounds.getCenterLonLat();
│ │ │ │ │ - if (this.baseLayer.wrapDateLine) {
│ │ │ │ │ - var maxExtent = this.getMaxExtent();
│ │ │ │ │ -
│ │ │ │ │ - //fix straddling bounds (in the case of a bbox that straddles the
│ │ │ │ │ - // dateline, it's left and right boundaries will appear backwards.
│ │ │ │ │ - // we fix this by allowing a right value that is greater than the
│ │ │ │ │ - // max value at the dateline -- this allows us to pass a valid
│ │ │ │ │ - // bounds to calculate zoom)
│ │ │ │ │ - //
│ │ │ │ │ - bounds = bounds.clone();
│ │ │ │ │ - while (bounds.right < bounds.left) {
│ │ │ │ │ - bounds.right += maxExtent.getWidth();
│ │ │ │ │ + if (sourceParts && sourceParts.length > 1) {
│ │ │ │ │ + sourceSplit = true;
│ │ │ │ │ + } else {
│ │ │ │ │ + sourceParts = [];
│ │ │ │ │ + }
│ │ │ │ │ + if (targetSplit || sourceSplit) {
│ │ │ │ │ + if (mutual) {
│ │ │ │ │ + results = [sourceParts, targetParts];
│ │ │ │ │ + } else {
│ │ │ │ │ + results = targetParts;
│ │ │ │ │ }
│ │ │ │ │ - //if the bounds was straddling (see above), then the center point
│ │ │ │ │ - // we got from it was wrong. So we take our new bounds and ask it
│ │ │ │ │ - // for the center.
│ │ │ │ │ - //
│ │ │ │ │ - center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
│ │ │ │ │ }
│ │ │ │ │ - this.setCenter(center, this.getZoomForExtent(bounds, closest));
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: zoomToMaxExtent
│ │ │ │ │ - * Zoom to the full extent and recenter.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * options - {Object}
│ │ │ │ │ - *
│ │ │ │ │ - * Allowed Options:
│ │ │ │ │ - * restricted - {Boolean} True to zoom to restricted extent if it is
│ │ │ │ │ - * set. Defaults to true.
│ │ │ │ │ - */
│ │ │ │ │ - zoomToMaxExtent: function(options) {
│ │ │ │ │ - //restricted is true by default
│ │ │ │ │ - var restricted = (options) ? options.restricted : true;
│ │ │ │ │ -
│ │ │ │ │ - var maxExtent = this.getMaxExtent({
│ │ │ │ │ - 'restricted': restricted
│ │ │ │ │ - });
│ │ │ │ │ - this.zoomToExtent(maxExtent);
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: zoomToScale
│ │ │ │ │ - * Zoom to a specified scale
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * scale - {float}
│ │ │ │ │ - * closest - {Boolean} Find the zoom level that most closely fits the
│ │ │ │ │ - * specified scale. Note that this may result in a zoom that does
│ │ │ │ │ - * not exactly contain the entire extent.
│ │ │ │ │ - * Default is false.
│ │ │ │ │ - *
│ │ │ │ │ - */
│ │ │ │ │ - zoomToScale: function(scale, closest) {
│ │ │ │ │ - var res = OpenLayers.Util.getResolutionFromScale(scale,
│ │ │ │ │ - this.baseLayer.units);
│ │ │ │ │ -
│ │ │ │ │ - var halfWDeg = (this.size.w * res) / 2;
│ │ │ │ │ - var halfHDeg = (this.size.h * res) / 2;
│ │ │ │ │ - var center = this.getCachedCenter();
│ │ │ │ │ -
│ │ │ │ │ - var extent = new OpenLayers.Bounds(center.lon - halfWDeg,
│ │ │ │ │ - center.lat - halfHDeg,
│ │ │ │ │ - center.lon + halfWDeg,
│ │ │ │ │ - center.lat + halfHDeg);
│ │ │ │ │ - this.zoomToExtent(extent, closest);
│ │ │ │ │ + return results;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ - /* */
│ │ │ │ │ - /* Translation Functions */
│ │ │ │ │ - /* */
│ │ │ │ │ - /* The following functions translate between */
│ │ │ │ │ - /* LonLat, LayerPx, and ViewPortPx */
│ │ │ │ │ - /* */
│ │ │ │ │ - /********************************************************/
│ │ │ │ │ -
│ │ │ │ │ - //
│ │ │ │ │ - // TRANSLATION: LonLat <-> ViewPortPx
│ │ │ │ │ - //
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * Method: getLonLatFromViewPortPx
│ │ │ │ │ - *
│ │ │ │ │ + * Method: splitWith
│ │ │ │ │ + * Split this geometry (the target) with the given geometry (the source).
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * viewPortPx - {|Object} An OpenLayers.Pixel or
│ │ │ │ │ - * an object with a 'x'
│ │ │ │ │ - * and 'y' properties.
│ │ │ │ │ + * geometry - {} A geometry used to split this
│ │ │ │ │ + * geometry (the source).
│ │ │ │ │ + * options - {Object} Properties of this object will be used to determine
│ │ │ │ │ + * how the split is conducted.
│ │ │ │ │ + *
│ │ │ │ │ + * Valid options:
│ │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target
│ │ │ │ │ + * geometry. Default is false.
│ │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is
│ │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance
│ │ │ │ │ + * distance of the intersection to be considered a split.
│ │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections
│ │ │ │ │ + * within the tolerance distance of an existing vertex on the source
│ │ │ │ │ + * will be assumed to occur at the vertex.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} An OpenLayers.LonLat which is the passed-in view
│ │ │ │ │ - * port , translated into lon/lat
│ │ │ │ │ - * by the current base layer.
│ │ │ │ │ + * {Array} A list of geometries (of this same type as the target) that
│ │ │ │ │ + * result from splitting the target with the source geometry. The
│ │ │ │ │ + * source and target geometry will remain unmodified. If no split
│ │ │ │ │ + * results, null will be returned. If mutual is true and a split
│ │ │ │ │ + * results, return will be an array of two arrays - the first will be
│ │ │ │ │ + * all geometries that result from splitting the source geometry and
│ │ │ │ │ + * the second will be all geometries that result from splitting the
│ │ │ │ │ + * target geometry.
│ │ │ │ │ */
│ │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) {
│ │ │ │ │ - var lonlat = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
│ │ │ │ │ - }
│ │ │ │ │ - return lonlat;
│ │ │ │ │ - },
│ │ │ │ │ + splitWith: function(geometry, options) {
│ │ │ │ │ + return geometry.split(this, options);
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getViewPortPxFromLonLat
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * lonlat - {}
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} An OpenLayers.Pixel which is the passed-in
│ │ │ │ │ - * , translated into view port
│ │ │ │ │ - * pixels by the current base layer.
│ │ │ │ │ - */
│ │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) {
│ │ │ │ │ - var px = null;
│ │ │ │ │ - if (this.baseLayer != null) {
│ │ │ │ │ - px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
│ │ │ │ │ - }
│ │ │ │ │ - return px;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: getZoomTargetCenter
│ │ │ │ │ + * APIMethod: getVertices
│ │ │ │ │ + * Return a list of all points in this geometry.
│ │ │ │ │ *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * xy - {} The zoom origin pixel location on the screen
│ │ │ │ │ - * resolution - {Float} The resolution we want to get the center for
│ │ │ │ │ + * nodes - {Boolean} For lines, only return vertices that are
│ │ │ │ │ + * endpoints. If false, for lines, only vertices that are not
│ │ │ │ │ + * endpoints will be returned. If not provided, all vertices will
│ │ │ │ │ + * be returned.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} The location of the map center after the
│ │ │ │ │ - * transformation described by the origin xy and the target resolution.
│ │ │ │ │ + * {Array} A list of all vertices in the geometry.
│ │ │ │ │ */
│ │ │ │ │ - getZoomTargetCenter: function(xy, resolution) {
│ │ │ │ │ - var lonlat = null,
│ │ │ │ │ - size = this.getSize(),
│ │ │ │ │ - deltaX = size.w / 2 - xy.x,
│ │ │ │ │ - deltaY = xy.y - size.h / 2,
│ │ │ │ │ - zoomPoint = this.getLonLatFromPixel(xy);
│ │ │ │ │ - if (zoomPoint) {
│ │ │ │ │ - lonlat = new OpenLayers.LonLat(
│ │ │ │ │ - zoomPoint.lon + deltaX * resolution,
│ │ │ │ │ - zoomPoint.lat + deltaY * resolution
│ │ │ │ │ - );
│ │ │ │ │ + getVertices: function(nodes) {
│ │ │ │ │ + var vertices;
│ │ │ │ │ + if (nodes === true) {
│ │ │ │ │ + vertices = [
│ │ │ │ │ + this.components[0],
│ │ │ │ │ + this.components[this.components.length - 1]
│ │ │ │ │ + ];
│ │ │ │ │ + } else if (nodes === false) {
│ │ │ │ │ + vertices = this.components.slice(1, this.components.length - 1);
│ │ │ │ │ + } else {
│ │ │ │ │ + vertices = this.components.slice();
│ │ │ │ │ }
│ │ │ │ │ - return lonlat;
│ │ │ │ │ + return vertices;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ - //
│ │ │ │ │ - // CONVENIENCE TRANSLATION FUNCTIONS FOR API
│ │ │ │ │ - //
│ │ │ │ │ -
│ │ │ │ │ /**
│ │ │ │ │ - * APIMethod: getLonLatFromPixel
│ │ │ │ │ - *
│ │ │ │ │ + * APIMethod: distanceTo
│ │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane).
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * px - {|Object} An OpenLayers.Pixel or an object with
│ │ │ │ │ - * a 'x' and 'y' properties.
│ │ │ │ │ + * geometry - {} The target geometry.
│ │ │ │ │ + * options - {Object} Optional properties for configuring the distance
│ │ │ │ │ + * calculation.
│ │ │ │ │ + *
│ │ │ │ │ + * Valid options:
│ │ │ │ │ + * details - {Boolean} Return details from the distance calculation.
│ │ │ │ │ + * Default is false.
│ │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the
│ │ │ │ │ + * nearest edge of the target geometry. Default is true. If true,
│ │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within
│ │ │ │ │ + * the target will result in a non-zero distance. If false, whenever
│ │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false,
│ │ │ │ │ + * details cannot be returned.
│ │ │ │ │ *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} An OpenLayers.LonLat corresponding to the given
│ │ │ │ │ - * OpenLayers.Pixel, translated into lon/lat by the
│ │ │ │ │ - * current base layer
│ │ │ │ │ - */
│ │ │ │ │ - getLonLatFromPixel: function(px) {
│ │ │ │ │ - return this.getLonLatFromViewPortPx(px);
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getPixelFromLonLat
│ │ │ │ │ - * Returns a pixel location given a map location. The map location is
│ │ │ │ │ - * translated to an integer pixel location (in viewport pixel
│ │ │ │ │ - * coordinates) by the current base layer.
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * lonlat - {} A map location.
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} An OpenLayers.Pixel corresponding to the
│ │ │ │ │ - * translated into view port pixels by the current
│ │ │ │ │ - * base layer.
│ │ │ │ │ + * {Number | Object} The distance between this geometry and the target.
│ │ │ │ │ + * If details is true, the return will be an object with distance,
│ │ │ │ │ + * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
│ │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1
│ │ │ │ │ + * properties represent the coordinates of the closest point on the
│ │ │ │ │ + * target geometry.
│ │ │ │ │ */
│ │ │ │ │ - getPixelFromLonLat: function(lonlat) {
│ │ │ │ │ - var px = this.getViewPortPxFromLonLat(lonlat);
│ │ │ │ │ - px.x = Math.round(px.x);
│ │ │ │ │ - px.y = Math.round(px.y);
│ │ │ │ │ - return px;
│ │ │ │ │ + distanceTo: function(geometry, options) {
│ │ │ │ │ + var edge = !(options && options.edge === false);
│ │ │ │ │ + var details = edge && options && options.details;
│ │ │ │ │ + var result, best = {};
│ │ │ │ │ + var min = Number.POSITIVE_INFINITY;
│ │ │ │ │ + if (geometry instanceof OpenLayers.Geometry.Point) {
│ │ │ │ │ + var segs = this.getSortedSegments();
│ │ │ │ │ + var x = geometry.x;
│ │ │ │ │ + var y = geometry.y;
│ │ │ │ │ + var seg;
│ │ │ │ │ + for (var i = 0, len = segs.length; i < len; ++i) {
│ │ │ │ │ + seg = segs[i];
│ │ │ │ │ + result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
│ │ │ │ │ + if (result.distance < min) {
│ │ │ │ │ + min = result.distance;
│ │ │ │ │ + best = result;
│ │ │ │ │ + if (min === 0) {
│ │ │ │ │ + break;
│ │ │ │ │ + }
│ │ │ │ │ + } else {
│ │ │ │ │ + // if distance increases and we cross y0 to the right of x0, no need to keep looking.
│ │ │ │ │ + if (seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {
│ │ │ │ │ + break;
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + if (details) {
│ │ │ │ │ + best = {
│ │ │ │ │ + distance: best.distance,
│ │ │ │ │ + x0: best.x,
│ │ │ │ │ + y0: best.y,
│ │ │ │ │ + x1: x,
│ │ │ │ │ + y1: y
│ │ │ │ │ + };
│ │ │ │ │ + } else {
│ │ │ │ │ + best = best.distance;
│ │ │ │ │ + }
│ │ │ │ │ + } else if (geometry instanceof OpenLayers.Geometry.LineString) {
│ │ │ │ │ + var segs0 = this.getSortedSegments();
│ │ │ │ │ + var segs1 = geometry.getSortedSegments();
│ │ │ │ │ + var seg0, seg1, intersection, x0, y0;
│ │ │ │ │ + var len1 = segs1.length;
│ │ │ │ │ + var interOptions = {
│ │ │ │ │ + point: true
│ │ │ │ │ + };
│ │ │ │ │ + outer: for (var i = 0, len = segs0.length; i < len; ++i) {
│ │ │ │ │ + seg0 = segs0[i];
│ │ │ │ │ + x0 = seg0.x1;
│ │ │ │ │ + y0 = seg0.y1;
│ │ │ │ │ + for (var j = 0; j < len1; ++j) {
│ │ │ │ │ + seg1 = segs1[j];
│ │ │ │ │ + intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
│ │ │ │ │ + if (intersection) {
│ │ │ │ │ + min = 0;
│ │ │ │ │ + best = {
│ │ │ │ │ + distance: 0,
│ │ │ │ │ + x0: intersection.x,
│ │ │ │ │ + y0: intersection.y,
│ │ │ │ │ + x1: intersection.x,
│ │ │ │ │ + y1: intersection.y
│ │ │ │ │ + };
│ │ │ │ │ + break outer;
│ │ │ │ │ + } else {
│ │ │ │ │ + result = OpenLayers.Geometry.distanceToSegment({
│ │ │ │ │ + x: x0,
│ │ │ │ │ + y: y0
│ │ │ │ │ + }, seg1);
│ │ │ │ │ + if (result.distance < min) {
│ │ │ │ │ + min = result.distance;
│ │ │ │ │ + best = {
│ │ │ │ │ + distance: min,
│ │ │ │ │ + x0: x0,
│ │ │ │ │ + y0: y0,
│ │ │ │ │ + x1: result.x,
│ │ │ │ │ + y1: result.y
│ │ │ │ │ + };
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + if (!details) {
│ │ │ │ │ + best = best.distance;
│ │ │ │ │ + }
│ │ │ │ │ + if (min !== 0) {
│ │ │ │ │ + // check the final vertex in this line's sorted segments
│ │ │ │ │ + if (seg0) {
│ │ │ │ │ + result = geometry.distanceTo(
│ │ │ │ │ + new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),
│ │ │ │ │ + options
│ │ │ │ │ + );
│ │ │ │ │ + var dist = details ? result.distance : result;
│ │ │ │ │ + if (dist < min) {
│ │ │ │ │ + if (details) {
│ │ │ │ │ + best = {
│ │ │ │ │ + distance: min,
│ │ │ │ │ + x0: result.x1,
│ │ │ │ │ + y0: result.y1,
│ │ │ │ │ + x1: result.x0,
│ │ │ │ │ + y1: result.y0
│ │ │ │ │ + };
│ │ │ │ │ + } else {
│ │ │ │ │ + best = dist;
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + } else {
│ │ │ │ │ + best = geometry.distanceTo(this, options);
│ │ │ │ │ + // swap since target comes from this line
│ │ │ │ │ + if (details) {
│ │ │ │ │ + best = {
│ │ │ │ │ + distance: best.distance,
│ │ │ │ │ + x0: best.x1,
│ │ │ │ │ + y0: best.y1,
│ │ │ │ │ + x1: best.x0,
│ │ │ │ │ + y1: best.y0
│ │ │ │ │ + };
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │ + return best;
│ │ │ │ │ },
│ │ │ │ │
│ │ │ │ │ /**
│ │ │ │ │ - * Method: getGeodesicPixelSize
│ │ │ │ │ - *
│ │ │ │ │ + * APIMethod: simplify
│ │ │ │ │ + * This function will return a simplified LineString.
│ │ │ │ │ + * Simplification is based on the Douglas-Peucker algorithm.
│ │ │ │ │ + *
│ │ │ │ │ + *
│ │ │ │ │ * Parameters:
│ │ │ │ │ - * px - {} The pixel to get the geodesic length for. If
│ │ │ │ │ - * not provided, the center pixel of the map viewport will be used.
│ │ │ │ │ - *
│ │ │ │ │ + * tolerance - {number} threshhold for simplification in map units
│ │ │ │ │ + *
│ │ │ │ │ * Returns:
│ │ │ │ │ - * {} The geodesic size of the pixel in kilometers.
│ │ │ │ │ + * {OpenLayers.Geometry.LineString} the simplified LineString
│ │ │ │ │ */
│ │ │ │ │ - getGeodesicPixelSize: function(px) {
│ │ │ │ │ - var lonlat = px ? this.getLonLatFromPixel(px) : (
│ │ │ │ │ - this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
│ │ │ │ │ - var res = this.getResolution();
│ │ │ │ │ - var left = lonlat.add(-res / 2, 0);
│ │ │ │ │ - var right = lonlat.add(res / 2, 0);
│ │ │ │ │ - var bottom = lonlat.add(0, -res / 2);
│ │ │ │ │ - var top = lonlat.add(0, res / 2);
│ │ │ │ │ - var dest = new OpenLayers.Projection("EPSG:4326");
│ │ │ │ │ - var source = this.getProjectionObject() || dest;
│ │ │ │ │ - if (!source.equals(dest)) {
│ │ │ │ │ - left.transform(source, dest);
│ │ │ │ │ - right.transform(source, dest);
│ │ │ │ │ - bottom.transform(source, dest);
│ │ │ │ │ - top.transform(source, dest);
│ │ │ │ │ - }
│ │ │ │ │ -
│ │ │ │ │ - return new OpenLayers.Size(
│ │ │ │ │ - OpenLayers.Util.distVincenty(left, right),
│ │ │ │ │ - OpenLayers.Util.distVincenty(bottom, top)
│ │ │ │ │ - );
│ │ │ │ │ - },
│ │ │ │ │ -
│ │ │ │ │ + simplify: function(tolerance) {
│ │ │ │ │ + if (this && this !== null) {
│ │ │ │ │ + var points = this.getVertices();
│ │ │ │ │ + if (points.length < 3) {
│ │ │ │ │ + return this;
│ │ │ │ │ + }
│ │ │ │ │
│ │ │ │ │ + var compareNumbers = function(a, b) {
│ │ │ │ │ + return (a - b);
│ │ │ │ │ + };
│ │ │ │ │
│ │ │ │ │ - //
│ │ │ │ │ - // TRANSLATION: ViewPortPx <-> LayerPx
│ │ │ │ │ - //
│ │ │ │ │ + /**
│ │ │ │ │ + * Private function doing the Douglas-Peucker reduction
│ │ │ │ │ + */
│ │ │ │ │ + var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) {
│ │ │ │ │ + var maxDistance = 0;
│ │ │ │ │ + var indexFarthest = 0;
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getViewPortPxFromLayerPx
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * layerPx - {}
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {} Layer Pixel translated into ViewPort Pixel
│ │ │ │ │ - * coordinates
│ │ │ │ │ - */
│ │ │ │ │ - getViewPortPxFromLayerPx: function(layerPx) {
│ │ │ │ │ - var viewPortPx = null;
│ │ │ │ │ - if (layerPx != null) {
│ │ │ │ │ - var dX = this.layerContainerOriginPx.x;
│ │ │ │ │ - var dY = this.layerContainerOriginPx.y;
│ │ │ │ │ - viewPortPx = layerPx.add(dX, dY);
│ │ │ │ │ - }
│ │ │ │ │ - return viewPortPx;
│ │ │ │ │ - },
│ │ │ │ │ + for (var index = firstPoint, distance; index < lastPoint; index++) {
│ │ │ │ │ + distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
│ │ │ │ │ + if (distance > maxDistance) {
│ │ │ │ │ + maxDistance = distance;
│ │ │ │ │ + indexFarthest = index;
│ │ │ │ │ + }
│ │ │ │ │ + }
│ │ │ │ │
│ │ │ │ │ - /**
│ │ │ │ │ - * APIMethod: getLayerPxFromViewPortPx
│ │ │ │ │ - *
│ │ │ │ │ - * Parameters:
│ │ │ │ │ - * viewPortPx - {}
│ │ │ │ │ - *
│ │ │ │ │ - * Returns:
│ │ │ │ │ - * {