Files
openlayers/src/ol/extent.js
Andreas Hocevar fbdbbfb7a7 Get rid of stability annotations and document stability with api
This change adds a stability value to the api annotation, with
'experimental' as default value.

enum, typedef and event annotations are never exportable, but
api annotations are needed there to make them appear in the
docs.

Nested typedefs are no longer inlined recursively, because the
resulting tables get too wide with the current template.
2014-04-29 09:53:07 -06:00

769 lines
19 KiB
JavaScript

goog.provide('ol.Extent');
goog.provide('ol.extent');
goog.provide('ol.extent.Relationship');
goog.require('goog.asserts');
goog.require('goog.vec.Mat4');
goog.require('ol.Coordinate');
goog.require('ol.Size');
goog.require('ol.TransformFunction');
/**
* An array of numbers representing an extent: `[minx, miny, maxx, maxy]`.
* @typedef {Array.<number>}
* @todo api
*/
ol.Extent;
/**
* Relationship to an extent.
* @enum {number}
*/
ol.extent.Relationship = {
UNKNOWN: 0,
INTERSECTING: 1,
ABOVE: 2,
RIGHT: 4,
BELOW: 8,
LEFT: 16
};
/**
* Builds an extent that includes all given coordinates.
*
* @param {Array.<ol.Coordinate>} coordinates Coordinates.
* @return {ol.Extent} Bounding extent.
* @todo api
*/
ol.extent.boundingExtent = function(coordinates) {
var extent = ol.extent.createEmpty();
for (var i = 0, ii = coordinates.length; i < ii; ++i) {
ol.extent.extendCoordinate(extent, coordinates[i]);
}
return extent;
};
/**
* @param {Array.<number>} xs Xs.
* @param {Array.<number>} ys Ys.
* @param {ol.Extent=} opt_extent Destination extent.
* @private
* @return {ol.Extent} Extent.
*/
ol.extent.boundingExtentXYs_ = function(xs, ys, opt_extent) {
goog.asserts.assert(xs.length > 0);
goog.asserts.assert(ys.length > 0);
var minX = Math.min.apply(null, xs);
var minY = Math.min.apply(null, ys);
var maxX = Math.max.apply(null, xs);
var maxY = Math.max.apply(null, ys);
return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
};
/**
* Return extent increased by the provided value.
* @param {ol.Extent} extent Extent.
* @param {number} value The amount by wich the extent should be buffered.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
* @todo api
*/
ol.extent.buffer = function(extent, value, opt_extent) {
if (goog.isDef(opt_extent)) {
opt_extent[0] = extent[0] - value;
opt_extent[1] = extent[1] - value;
opt_extent[2] = extent[2] + value;
opt_extent[3] = extent[3] + value;
return opt_extent;
} else {
return [
extent[0] - value,
extent[1] - value,
extent[2] + value,
extent[3] + value
];
}
};
/**
* Creates a clone of an extent.
*
* @param {ol.Extent} extent Extent to clone.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} The clone.
*/
ol.extent.clone = function(extent, opt_extent) {
if (goog.isDef(opt_extent)) {
opt_extent[0] = extent[0];
opt_extent[1] = extent[1];
opt_extent[2] = extent[2];
opt_extent[3] = extent[3];
return opt_extent;
} else {
return extent.slice();
}
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} x X.
* @param {number} y Y.
* @return {number} Closest squared distance.
*/
ol.extent.closestSquaredDistanceXY = function(extent, x, y) {
var dx, dy;
if (x < extent[0]) {
dx = extent[0] - x;
} else if (extent[2] < x) {
dx = x - extent[2];
} else {
dx = 0;
}
if (y < extent[1]) {
dy = extent[1] - y;
} else if (extent[3] < y) {
dy = y - extent[3];
} else {
dy = 0;
}
return dx * dx + dy * dy;
};
/**
* Checks if the passed coordinate is contained or on the edge of the extent.
*
* @param {ol.Extent} extent Extent.
* @param {ol.Coordinate} coordinate Coordinate.
* @return {boolean} Contains.
* @todo api
*/
ol.extent.containsCoordinate = function(extent, coordinate) {
return extent[0] <= coordinate[0] && coordinate[0] <= extent[2] &&
extent[1] <= coordinate[1] && coordinate[1] <= extent[3];
};
/**
* Checks if `extent2` is contained by or on the edge of `extent1`.
*
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
* @return {boolean} Contains.
* @todo api
*/
ol.extent.containsExtent = function(extent1, extent2) {
return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] &&
extent1[1] <= extent2[1] && extent2[3] <= extent1[3];
};
/**
* Get the relationship between a coordinate and extent.
* @param {ol.Extent} extent The extent.
* @param {ol.Coordinate} coordinate The coordinate.
* @return {number} The relationship (bitwise compare with
* ol.extent.Relationship).
*/
ol.extent.coordinateRelationship = function(extent, coordinate) {
var minX = extent[0];
var minY = extent[1];
var maxX = extent[2];
var maxY = extent[3];
var x = coordinate[0];
var y = coordinate[1];
var relationship = ol.extent.Relationship.UNKNOWN;
if (x < minX) {
relationship = relationship | ol.extent.Relationship.LEFT;
} else if (x > maxX) {
relationship = relationship | ol.extent.Relationship.RIGHT;
}
if (y < minY) {
relationship = relationship | ol.extent.Relationship.BELOW;
} else if (y > maxY) {
relationship = relationship | ol.extent.Relationship.ABOVE;
}
if (relationship === ol.extent.Relationship.UNKNOWN) {
relationship = ol.extent.Relationship.INTERSECTING;
}
return relationship;
};
/**
* @return {ol.Extent} Empty extent.
* @todo api
*/
ol.extent.createEmpty = function() {
return [Infinity, Infinity, -Infinity, -Infinity];
};
/**
* @param {number} minX Minimum X.
* @param {number} minY Minimum Y.
* @param {number} maxX Maximum X.
* @param {number} maxY Maximum Y.
* @param {ol.Extent=} opt_extent Destination extent.
* @return {ol.Extent} Extent.
*/
ol.extent.createOrUpdate = function(minX, minY, maxX, maxY, opt_extent) {
if (goog.isDef(opt_extent)) {
opt_extent[0] = minX;
opt_extent[1] = minY;
opt_extent[2] = maxX;
opt_extent[3] = maxY;
return opt_extent;
} else {
return [minX, minY, maxX, maxY];
}
};
/**
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.createOrUpdateEmpty = function(opt_extent) {
return ol.extent.createOrUpdate(
Infinity, Infinity, -Infinity, -Infinity, opt_extent);
};
/**
* @param {ol.Coordinate} coordinate Coordinate.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.createOrUpdateFromCoordinate = function(coordinate, opt_extent) {
var x = coordinate[0];
var y = coordinate[1];
return ol.extent.createOrUpdate(x, y, x, y, opt_extent);
};
/**
* @param {Array.<ol.Coordinate>} coordinates Coordinates.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.createOrUpdateFromCoordinates = function(coordinates, opt_extent) {
var extent = ol.extent.createOrUpdateEmpty(opt_extent);
return ol.extent.extendCoordinates(extent, coordinates);
};
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.createOrUpdateFromFlatCoordinates =
function(flatCoordinates, offset, end, stride, opt_extent) {
var extent = ol.extent.createOrUpdateEmpty(opt_extent);
return ol.extent.extendFlatCoordinates(
extent, flatCoordinates, offset, end, stride);
};
/**
* @param {Array.<Array.<ol.Coordinate>>} rings Rings.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.createOrUpdateFromRings = function(rings, opt_extent) {
var extent = ol.extent.createOrUpdateEmpty(opt_extent);
return ol.extent.extendRings(extent, rings);
};
/**
* Empties extent in place.
* @param {ol.Extent} extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.empty = function(extent) {
extent[0] = extent[1] = Infinity;
extent[2] = extent[3] = -Infinity;
return extent;
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
* @return {boolean} Equals.
* @todo api
*/
ol.extent.equals = function(extent1, extent2) {
return extent1[0] == extent2[0] && extent1[2] == extent2[2] &&
extent1[1] == extent2[1] && extent1[3] == extent2[3];
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
* @return {ol.Extent} Extent.
* @todo api
*/
ol.extent.extend = function(extent1, extent2) {
if (extent2[0] < extent1[0]) {
extent1[0] = extent2[0];
}
if (extent2[2] > extent1[2]) {
extent1[2] = extent2[2];
}
if (extent2[1] < extent1[1]) {
extent1[1] = extent2[1];
}
if (extent2[3] > extent1[3]) {
extent1[3] = extent2[3];
}
return extent1;
};
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Coordinate} coordinate Coordinate.
*/
ol.extent.extendCoordinate = function(extent, coordinate) {
if (coordinate[0] < extent[0]) {
extent[0] = coordinate[0];
}
if (coordinate[0] > extent[2]) {
extent[2] = coordinate[0];
}
if (coordinate[1] < extent[1]) {
extent[1] = coordinate[1];
}
if (coordinate[1] > extent[3]) {
extent[3] = coordinate[1];
}
};
/**
* @param {ol.Extent} extent Extent.
* @param {Array.<ol.Coordinate>} coordinates Coordinates.
* @return {ol.Extent} Extent.
*/
ol.extent.extendCoordinates = function(extent, coordinates) {
var i, ii;
for (i = 0, ii = coordinates.length; i < ii; ++i) {
ol.extent.extendCoordinate(extent, coordinates[i]);
}
return extent;
};
/**
* @param {ol.Extent} extent Extent.
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @return {ol.Extent} Extent.
*/
ol.extent.extendFlatCoordinates =
function(extent, flatCoordinates, offset, end, stride) {
for (; offset < end; offset += stride) {
ol.extent.extendXY(
extent, flatCoordinates[offset], flatCoordinates[offset + 1]);
}
return extent;
};
/**
* @param {ol.Extent} extent Extent.
* @param {Array.<Array.<ol.Coordinate>>} rings Rings.
* @return {ol.Extent} Extent.
*/
ol.extent.extendRings = function(extent, rings) {
var i, ii;
for (i = 0, ii = rings.length; i < ii; ++i) {
ol.extent.extendCoordinates(extent, rings[i]);
}
return extent;
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} x X.
* @param {number} y Y.
*/
ol.extent.extendXY = function(extent, x, y) {
extent[0] = Math.min(extent[0], x);
extent[1] = Math.min(extent[1], y);
extent[2] = Math.max(extent[2], x);
extent[3] = Math.max(extent[3], y);
};
/**
* @param {ol.Extent} extent Extent.
* @return {number} Area.
*/
ol.extent.getArea = function(extent) {
return ol.extent.getWidth(extent) * ol.extent.getHeight(extent);
};
/**
* @param {ol.Extent} extent Extent.
* @return {ol.Coordinate} Bottom left coordinate.
* @todo api
*/
ol.extent.getBottomLeft = function(extent) {
return [extent[0], extent[1]];
};
/**
* @param {ol.Extent} extent Extent.
* @return {ol.Coordinate} Bottom right coordinate.
* @todo api
*/
ol.extent.getBottomRight = function(extent) {
return [extent[2], extent[1]];
};
/**
* @param {ol.Extent} extent Extent.
* @return {ol.Coordinate} Center.
* @todo api
*/
ol.extent.getCenter = function(extent) {
return [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2];
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
* @return {number} Enlarged area.
*/
ol.extent.getEnlargedArea = function(extent1, extent2) {
var minX = Math.min(extent1[0], extent2[0]);
var minY = Math.min(extent1[1], extent2[1]);
var maxX = Math.max(extent1[2], extent2[2]);
var maxY = Math.max(extent1[3], extent2[3]);
return (maxX - minX) * (maxY - minY);
};
/**
* @param {ol.Coordinate} center Center.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @param {ol.Size} size Size.
* @param {ol.Extent=} opt_extent Destination extent.
* @return {ol.Extent} Extent.
*/
ol.extent.getForView2DAndSize =
function(center, resolution, rotation, size, opt_extent) {
var dx = resolution * size[0] / 2;
var dy = resolution * size[1] / 2;
var cosRotation = Math.cos(rotation);
var sinRotation = Math.sin(rotation);
/** @type {Array.<number>} */
var xs = [-dx, -dx, dx, dx];
/** @type {Array.<number>} */
var ys = [-dy, dy, -dy, dy];
var i, x, y;
for (i = 0; i < 4; ++i) {
x = xs[i];
y = ys[i];
xs[i] = center[0] + x * cosRotation - y * sinRotation;
ys[i] = center[1] + x * sinRotation + y * cosRotation;
}
return ol.extent.boundingExtentXYs_(xs, ys, opt_extent);
};
/**
* @param {ol.Extent} extent Extent.
* @return {number} Height.
* @todo api
*/
ol.extent.getHeight = function(extent) {
return extent[3] - extent[1];
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
* @return {number} Intersection area.
*/
ol.extent.getIntersectionArea = function(extent1, extent2) {
var minX = Math.max(extent1[0], extent2[0]);
var minY = Math.max(extent1[1], extent2[1]);
var maxX = Math.min(extent1[2], extent2[2]);
var maxY = Math.min(extent1[3], extent2[3]);
return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
};
/**
* @param {ol.Extent} extent Extent.
* @return {number} Margin.
*/
ol.extent.getMargin = function(extent) {
return ol.extent.getWidth(extent) + ol.extent.getHeight(extent);
};
/**
* @param {ol.Extent} extent Extent.
* @return {ol.Size} Size.
* @todo api
*/
ol.extent.getSize = function(extent) {
return [extent[2] - extent[0], extent[3] - extent[1]];
};
/**
* @param {ol.Extent} extent Extent.
* @return {ol.Coordinate} Top left coordinate.
* @todo api
*/
ol.extent.getTopLeft = function(extent) {
return [extent[0], extent[3]];
};
/**
* @param {ol.Extent} extent Extent.
* @return {ol.Coordinate} Top right coordinate.
* @todo api
*/
ol.extent.getTopRight = function(extent) {
return [extent[2], extent[3]];
};
/**
* @param {ol.Extent} extent Extent.
* @return {number} Width.
* @todo api
*/
ol.extent.getWidth = function(extent) {
return extent[2] - extent[0];
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent.
* @return {boolean} Intersects.
* @todo api
*/
ol.extent.intersects = function(extent1, extent2) {
return extent1[0] <= extent2[2] &&
extent1[2] >= extent2[0] &&
extent1[1] <= extent2[3] &&
extent1[3] >= extent2[1];
};
/**
* @param {ol.Extent} extent Extent.
* @return {boolean} Is empty.
* @todo api
*/
ol.extent.isEmpty = function(extent) {
return extent[2] < extent[0] || extent[3] < extent[1];
};
/**
* @param {ol.Extent} extent Extent.
* @return {boolean} Is infinite.
*/
ol.extent.isInfinite = function(extent) {
return extent[0] == -Infinity || extent[1] == -Infinity ||
extent[2] == Infinity || extent[3] == Infinity;
};
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Coordinate} coordinate Coordinate.
* @return {ol.Coordinate} Coordinate.
*/
ol.extent.normalize = function(extent, coordinate) {
return [
(coordinate[0] - extent[0]) / (extent[2] - extent[0]),
(coordinate[1] - extent[1]) / (extent[3] - extent[1])
];
};
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Extent=} opt_extent Extent.
* @return {ol.Extent} Extent.
*/
ol.extent.returnOrUpdate = function(extent, opt_extent) {
if (goog.isDef(opt_extent)) {
opt_extent[0] = extent[0];
opt_extent[1] = extent[1];
opt_extent[2] = extent[2];
opt_extent[3] = extent[3];
return opt_extent;
} else {
return extent;
}
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} value Value.
*/
ol.extent.scaleFromCenter = function(extent, value) {
var deltaX = ((extent[2] - extent[0]) / 2) * (value - 1);
var deltaY = ((extent[3] - extent[1]) / 2) * (value - 1);
extent[0] -= deltaX;
extent[2] += deltaX;
extent[1] -= deltaY;
extent[3] += deltaY;
};
/**
* Determine if the segment between two coordinates intersects (crosses,
* touches, or is contained by) the provided extent.
* @param {ol.Extent} extent The extent.
* @param {ol.Coordinate} start Segment start coordinate.
* @param {ol.Coordinate} end Segment end coordinate.
* @return {boolean} The segment intersects the extent.
*/
ol.extent.segmentIntersects = function(extent, start, end) {
var intersects = false;
var startRel = ol.extent.coordinateRelationship(extent, start);
var endRel = ol.extent.coordinateRelationship(extent, end);
if (startRel === ol.extent.Relationship.INTERSECTING ||
endRel === ol.extent.Relationship.INTERSECTING) {
intersects = true;
} else {
var minX = extent[0];
var minY = extent[1];
var maxX = extent[2];
var maxY = extent[3];
var startX = start[0];
var startY = start[1];
var endX = end[0];
var endY = end[1];
var slope = (endY - startY) / (endX - startX);
var x, y;
if (!!(endRel & ol.extent.Relationship.ABOVE) &&
!(startRel & ol.extent.Relationship.ABOVE)) {
// potentially intersects top
x = endX - ((endY - maxY) / slope);
intersects = x >= minX && x <= maxX;
} else if (!!(endRel & ol.extent.Relationship.RIGHT) &&
!(startRel & ol.extent.Relationship.RIGHT)) {
// potentially intersects right
y = endY - ((endX - maxX) * slope);
intersects = y >= minY && y <= maxY;
} else if (!!(endRel & ol.extent.Relationship.BELOW) &&
!(startRel & ol.extent.Relationship.BELOW)) {
// potentially intersects bottom
x = endX - ((endY - minY) / slope);
intersects = x >= minX && x <= maxX;
} else if (!!(endRel & ol.extent.Relationship.LEFT) &&
!(startRel & ol.extent.Relationship.LEFT)) {
// potentially intersects left
y = endY - ((endX - minX) * slope);
intersects = y >= minY && y <= maxY;
}
}
return intersects;
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
* @return {boolean} Touches.
*/
ol.extent.touches = function(extent1, extent2) {
var intersects = ol.extent.intersects(extent1, extent2);
return intersects &&
(extent1[0] == extent2[2] || extent1[2] == extent2[0] ||
extent1[1] == extent2[3] || extent1[3] == extent2[1]);
};
/**
* @param {ol.Extent} extent Extent.
* @param {ol.TransformFunction} transformFn Transform function.
* @param {ol.Extent=} opt_extent Destination extent.
* @return {ol.Extent} Extent.
* @todo api
*/
ol.extent.transform = function(extent, transformFn, opt_extent) {
var coordinates = [
extent[0], extent[1],
extent[0], extent[3],
extent[2], extent[1],
extent[2], extent[3]
];
transformFn(coordinates, coordinates, 2);
var xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]];
var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];
return ol.extent.boundingExtentXYs_(xs, ys, opt_extent);
};
/**
* Apply a 2d transform to an extent.
* @param {ol.Extent} extent Input extent.
* @param {goog.vec.Mat4.Number} transform The transform matrix.
* @param {ol.Extent=} opt_extent Optional extent for return values.
* @return {ol.Extent} The transformed extent.
*/
ol.extent.transform2D = function(extent, transform, opt_extent) {
var dest = goog.isDef(opt_extent) ? opt_extent : [];
var m00 = goog.vec.Mat4.getElement(transform, 0, 0);
var m10 = goog.vec.Mat4.getElement(transform, 1, 0);
var m01 = goog.vec.Mat4.getElement(transform, 0, 1);
var m11 = goog.vec.Mat4.getElement(transform, 1, 1);
var m03 = goog.vec.Mat4.getElement(transform, 0, 3);
var m13 = goog.vec.Mat4.getElement(transform, 1, 3);
var xi = [0, 2, 0, 2];
var yi = [1, 1, 3, 3];
var xs = [];
var ys = [];
var i, x, y;
for (i = 0; i < 4; ++i) {
x = extent[xi[i]];
y = extent[yi[i]];
xs[i] = m00 * x + m01 * y + m03;
ys[i] = m10 * x + m11 * y + m13;
}
dest[0] = Math.min.apply(null, xs);
dest[1] = Math.min.apply(null, ys);
dest[2] = Math.max.apply(null, xs);
dest[3] = Math.max.apply(null, ys);
return dest;
};