Fix #1951 by rounding all floats passed to Bounds.initialize() and

LonLat.initialize() to 14 significant figures.

Added an OpenLayers.Util.toFloat() function, and changed the LonLat constructor
and the Bounds constructor to truncate all float parameters to 14 significant
figures to avoid numeric comparison errors caused by floating point precision
loss. See the comments around the definition of
OpenLayers.Util.DEFAULT_PRECISION for how it works.

Also refactored Bounds.intersectBounds() for readability, and added a
Bounds.touchesBounds() in the process. After tweaking the tests for
Layer.SphericalMercator and Format.WKT (because they were expecting too many
significant figures), all tests pass.



git-svn-id: http://svn.openlayers.org/trunk/openlayers@9022 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
Schuyler Erle
2009-03-11 22:45:32 +00:00
parent 9afcb939be
commit e89774d568
8 changed files with 135 additions and 59 deletions

View File

@@ -65,16 +65,16 @@ OpenLayers.Bounds = OpenLayers.Class({
*/
initialize: function(left, bottom, right, top) {
if (left != null) {
this.left = parseFloat(left);
this.left = OpenLayers.Util.toFloat(left);
}
if (bottom != null) {
this.bottom = parseFloat(bottom);
this.bottom = OpenLayers.Util.toFloat(bottom);
}
if (right != null) {
this.right = parseFloat(right);
this.right = OpenLayers.Util.toFloat(right);
}
if (top != null) {
this.top = parseFloat(top);
this.top = OpenLayers.Util.toFloat(top);
}
},
@@ -158,7 +158,7 @@ OpenLayers.Bounds = OpenLayers.Class({
return bbox;
},
/**
* APIMethod: toGeometry
* Create a new polygon geometry based on this bounds.
@@ -386,12 +386,18 @@ OpenLayers.Bounds = OpenLayers.Class({
* bounds.
*/
contains:function(x, y, inclusive) {
//set default
if (inclusive == null) {
inclusive = true;
}
if (x == null || y == null) {
return false;
}
x = OpenLayers.Util.toFloat(x);
y = OpenLayers.Util.toFloat(y);
var contains = false;
if (inclusive) {
contains = ((x >= this.left) && (x <= this.right) &&
@@ -403,6 +409,23 @@ OpenLayers.Bounds = OpenLayers.Class({
return contains;
},
/**
* APIMethod: touchesBounds
*
* Parameters:
* bounds - {<OpenLayers.Bounds>}
*
* Returns:
* {Boolean} The passed-in OpenLayers.Bounds object touches this bounds
* at a single edge, and therefore do not inclusively intersect.
*/
touchesBounds:function(bounds) {
return (this.left == bounds.right ||
this.right == bounds.left ||
this.top == bounds.bottom ||
this.bottom == bounds.top);
},
/**
* APIMethod: intersectsBounds
*
@@ -417,26 +440,19 @@ OpenLayers.Bounds = OpenLayers.Class({
* partial.
*/
intersectsBounds:function(bounds, inclusive) {
if (inclusive == null) {
inclusive = true;
}
var inBottom = (bounds.bottom == this.bottom && bounds.top == this.top) ?
true : (((bounds.bottom > this.bottom) && (bounds.bottom < this.top)) ||
((this.bottom > bounds.bottom) && (this.bottom < bounds.top)));
var inTop = (bounds.bottom == this.bottom && bounds.top == this.top) ?
true : (((bounds.top > this.bottom) && (bounds.top < this.top)) ||
((this.top > bounds.bottom) && (this.top < bounds.top)));
var inRight = (bounds.right == this.right && bounds.left == this.left) ?
true : (((bounds.right > this.left) && (bounds.right < this.right)) ||
((this.right > bounds.left) && (this.right < bounds.right)));
var inLeft = (bounds.right == this.right && bounds.left == this.left) ?
true : (((bounds.left > this.left) && (bounds.left < this.right)) ||
((this.left > bounds.left) && (this.left < bounds.right)));
return (this.containsBounds(bounds, true, inclusive) ||
bounds.containsBounds(this, true, inclusive) ||
((inTop || inBottom ) && (inLeft || inRight )));
var intersects = false;
// if the two bounds only touch at an edge, and inclusive is false,
// then the bounds don't *really* intersect.
if (inclusive || !this.touchesBounds(bounds)) {
// otherwise, if one of the boundaries even partially contains another,
// inclusive of the edges, then they do intersect.
intersects = this.containsBounds(bounds, true, true) ||
bounds.containsBounds(this, true, true);
}
return intersects;
},
/**
@@ -453,34 +469,19 @@ OpenLayers.Bounds = OpenLayers.Class({
* {Boolean} The passed-in bounds object is contained within this bounds.
*/
containsBounds:function(bounds, partial, inclusive) {
//set defaults
if (partial == null) {
partial = false;
}
if (inclusive == null) {
inclusive = true;
}
var inLeft;
var inTop;
var inRight;
var inBottom;
var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
var topLeft = this.contains(bounds.left, bounds.top, inclusive);
var topRight = this.contains(bounds.right, bounds.top, inclusive);
if (inclusive) {
inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
} else {
inLeft = (bounds.left > this.left) && (bounds.left < this.right);
inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
inRight= (bounds.right > this.left) && (bounds.right < this.right);
inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
}
return (partial) ? (inTop || inBottom ) && (inLeft || inRight )
: (inTop && inLeft && inBottom && inRight);
return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
: (bottomLeft && bottomRight && topLeft && topRight);
},
/**

View File

@@ -37,8 +37,8 @@ OpenLayers.LonLat = OpenLayers.Class({
* it will be the y coordinate of the map location in your map units.
*/
initialize: function(lon, lat) {
this.lon = parseFloat(lon);
this.lat = parseFloat(lat);
this.lon = OpenLayers.Util.toFloat(lon);
this.lat = OpenLayers.Util.toFloat(lat);
},
/**

View File

@@ -796,6 +796,51 @@ OpenLayers.Util.mouseLeft = function (evt, div) {
return (target != div);
};
/**
* Property: precision
* {Number} The number of significant digits to retain to avoid
* floating point precision errors.
*
* We use 14 as a "safe" default because, although IEEE 754 double floats
* (standard on most modern operating systems) support up to about 16
* significant digits, 14 significant digits are sufficient to represent
* sub-millimeter accuracy in any coordinate system that anyone is likely to
* use with OpenLayers.
*
* If DEFAULT_PRECISION is set to 0, the original non-truncating behavior
* of OpenLayers <2.8 is preserved. Be aware that this will cause problems
* with certain projections, e.g. spherical Mercator.
*
*/
OpenLayers.Util.DEFAULT_PRECISION = 14;
/**
* Function: toFloat
* Convenience method to cast an object to a Number, rounded to the
* desired floating point precision.
*
* Parameters:
* number - {Number} The number to cast and round.
* precision - {Number} An integer suitable for use with
* Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION.
* If set to 0, no rounding is performed.
*
* Returns:
* {Number} The cast, rounded number.
*/
OpenLayers.Util.toFloat = function (number, precision) {
if (precision == null) {
precision = OpenLayers.Util.DEFAULT_PRECISION;
}
var number;
if (precision == 0) {
number = parseFloat(number);
} else {
number = parseFloat(parseFloat(number).toPrecision(precision))
}
return number;
};
/**
* Function: rad
*
@@ -1071,7 +1116,7 @@ OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
OpenLayers.DOTS_PER_INCH = 72;
/**
* Function: normalzeScale
* Function: normalizeScale
*
* Parameters:
* scale - {float}