diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 19ef7ded72..f6526d4c30 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -1052,3 +1052,12 @@ * @property {!Array.} resolutions Resolutions. * @todo stability experimental */ + +/** + * @typedef {Object} olx.View2D.fitGeometryOptions + * @property {!Array.} padding Padding (in pixels) to be cleared inside the view. Values in the array are top, right, bottom and left padding. Default is `[0, 0, 0, 0]`. + * @property {boolean|undefined} constrainResolution Constrain the resolution. Default is `true`. + * @property {boolean|undefined} nearest Get the nearest extent. Default is `false`. + * @property {number|undefined} minResolution Minimum resolution that we zoom to. Default is `0`. + * @todo stability experimental + */ diff --git a/src/ol/view2d.exports b/src/ol/view2d.exports index 85318dd373..8953445dd7 100644 --- a/src/ol/view2d.exports +++ b/src/ol/view2d.exports @@ -3,6 +3,7 @@ @exportProperty ol.View2D.prototype.constrainResolution @exportProperty ol.View2D.prototype.constrainRotation @exportProperty ol.View2D.prototype.fitExtent +@exportProperty ol.View2D.prototype.fitGeometry @exportProperty ol.View2D.prototype.getView2D @exportProperty ol.View2D.prototype.getZoom @exportProperty ol.View2D.prototype.setZoom diff --git a/src/ol/view2d.js b/src/ol/view2d.js index c11764adaa..3c800d3b61 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -430,6 +430,73 @@ ol.View2D.prototype.fitExtent = function(extent, size) { }; +/** + * Fit the given geometry based on the given map size and border. + * Take care on the map angle. + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @param {ol.Size} size Box pixel size. + * @param {olx.View2D.fitGeometryOptions=} opt_options Options. + * @todo stability experimental + */ +ol.View2D.prototype.fitGeometry = function(geometry, size, opt_options) { + var options = goog.isDef(opt_options) ? opt_options : {}; + + var padding = goog.isDef(options.padding) ? options.padding : [0, 0, 0, 0]; + var constrainResolution = goog.isDef(options.constrainResolution) ? + options.constrainResolution : true; + var nearest = goog.isDef(options.nearest) ? options.nearest : false; + var minResolution = goog.isDef(options.minResolution) ? + options.minResolution : 0; + var coords = geometry.getFlatCoordinates(); + + // calculate rotated extent + var rotation = this.getRotation(); + goog.asserts.assert(goog.isDef(rotation)); + var cosAngle = Math.cos(-rotation); + var sinAngle = Math.sin(-rotation); + var minRotX = +Infinity; + var minRotY = +Infinity; + var maxRotX = -Infinity; + var maxRotY = -Infinity; + var stride = geometry.getStride(); + for (var i = 0, ii = coords.length; i < ii; i += stride) { + var rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; + var rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; + minRotX = Math.min(minRotX, rotX); + minRotY = Math.min(minRotY, rotY); + maxRotX = Math.max(maxRotX, rotX); + maxRotY = Math.max(maxRotY, rotY); + } + + // calculate resolution + var resolution = this.getResolutionForExtent( + [minRotX, minRotY, maxRotX, maxRotY], + [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]); + resolution = isNaN(resolution) ? minResolution : + Math.max(resolution, minResolution); + if (constrainResolution) { + var constrainedResolution = this.constrainResolution(resolution, 0, 0); + if (!nearest && constrainedResolution < resolution) { + constrainedResolution = this.constrainResolution( + constrainedResolution, -1, 0); + } + resolution = constrainedResolution; + } + this.setResolution(resolution); + + // calculate center + sinAngle = -sinAngle; // go back to original rotation + var centerRotX = (minRotX + maxRotX) / 2; + var centerRotY = (minRotY + maxRotY) / 2; + centerRotX += (padding[1] - padding[3]) / 2 * resolution; + centerRotY += (padding[0] - padding[2]) / 2 * resolution; + var centerX = centerRotX * cosAngle - centerRotY * sinAngle; + var centerY = centerRotY * cosAngle + centerRotX * sinAngle; + + this.setCenter([centerX, centerY]); +}; + + /** * @return {boolean} Is defined. */ diff --git a/test/spec/ol/view2d.test.js b/test/spec/ol/view2d.test.js index 64c192c962..48693ccc62 100644 --- a/test/spec/ol/view2d.test.js +++ b/test/spec/ol/view2d.test.js @@ -102,6 +102,78 @@ describe('ol.View2D', function() { expect(view.getZoom()).to.be(undefined); }); }); + + describe('fitGeometry', function() { + var view; + beforeEach(function() { + view = new ol.View2D({ + resolutions: [200, 100, 50, 20, 10, 5, 2, 1] + }); + }); + it('fit correctly to the geometry', function() { + view.fitGeometry( + new ol.geom.LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), + [200, 200], + { + padding: [100, 0, 0, 100], + constrainResolution: false + } + ); + expect(view.getResolution()).to.be(11); + expect(view.getCenter()[0]).to.be(5950); + expect(view.getCenter()[1]).to.be(47100); + + view.fitGeometry( + new ol.geom.LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), + [200, 200], + { + padding: [100, 0, 0, 100] + } + ); + expect(view.getResolution()).to.be(20); + expect(view.getCenter()[0]).to.be(5500); + expect(view.getCenter()[1]).to.be(47550); + + view.fitGeometry( + new ol.geom.LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), + [200, 200], + { + padding: [100, 0, 0, 100], + nearest: true + } + ); + expect(view.getResolution()).to.be(10); + expect(view.getCenter()[0]).to.be(6000); + expect(view.getCenter()[1]).to.be(47050); + + view.fitGeometry( + new ol.geom.Point([6000, 46000]), + [200, 200], + { + padding: [100, 0, 0, 100], + minResolution: 2 + } + ); + expect(view.getResolution()).to.be(2); + expect(view.getCenter()[0]).to.be(5900); + expect(view.getCenter()[1]).to.be(46100); + + view.setRotation(Math.PI / 4); + view.fitGeometry( + new ol.geom.LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), + [200, 200], + { + padding: [100, 0, 0, 100], + constrainResolution: false + } + ); + expect(view.getResolution()).to.be(14.849242404917458); + expect(view.getCenter()[0]).to.be(5200.000000000011); + expect(view.getCenter()[1]).to.be(46300); + }); + }); }); goog.require('ol.View2D'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point');