diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index 6f3eb5575c..1b9b66073a 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -1,5 +1,17 @@ ## Upgrade notes +### v3.13.0 + +#### `proj4js` integration + +Before this release, OpenLayers depended on the global proj4 namespace. When using for instance browserify you might not want to depend on the global `proj4` namespace. You can use the `ol.proj.setProj4` function to set the proj4 function object. For example in a browserify ES6 environment: + +```js +import ol from 'openlayers'; +import proj4 from 'proj4'; +ol.proj.setProj4(proj4); +``` + ### v3.12.0 #### `ol.Map#forEachFeatureAtPixel` changes diff --git a/externs/proj4js.js b/externs/proj4js.js index aca6cfd8e2..48a635c1d8 100644 --- a/externs/proj4js.js +++ b/externs/proj4js.js @@ -5,12 +5,21 @@ /** - * @param {...*} var_args - * @return {undefined|Array.|Object.<{ - * forward: function(Array.): Array., - * inverse: function(Array.): Array.}>} + * @constructor */ -var proj4 = function(var_args) {}; +var proj4 = function() {}; + + +/** + * @type {function(Array.): Array.} + */ +proj4.prototype.forward; + + +/** + * @type {function(Array.): Array.} + */ +proj4.prototype.inverse; /** diff --git a/src/ol/proj/proj.js b/src/ol/proj/proj.js index 852ce7639e..d7184c118a 100644 --- a/src/ol/proj/proj.js +++ b/src/ol/proj/proj.js @@ -75,7 +75,8 @@ ol.proj.METERS_PER_UNIT[ol.proj.Units.USFEET] = 1200 / 3937; * http://www.opengis.net/gml/srs/epsg.xml#3857 * * If you use proj4js, aliases can be added using `proj4.defs()`; see - * [documentation](https://github.com/proj4js/proj4js). + * [documentation](https://github.com/proj4js/proj4js). To set an alternative + * namespace for proj4, use {@link ol.proj.setProj4}. * * @constructor * @param {olx.ProjectionOptions} options Projection options. @@ -146,35 +147,37 @@ ol.proj.Projection = function(options) { var code = options.code; goog.asserts.assert(code !== undefined, 'Option "code" is required for constructing instance'); - if (ol.ENABLE_PROJ4JS && typeof proj4 == 'function' && - projections[code] === undefined) { - var def = proj4.defs(code); - if (def !== undefined) { - if (def.axis !== undefined && options.axisOrientation === undefined) { - this.axisOrientation_ = def.axis; - } - if (options.units === undefined) { - var units = def.units; - if (def.to_meter !== undefined) { - if (units === undefined || - ol.proj.METERS_PER_UNIT[units] === undefined) { - units = def.to_meter.toString(); - ol.proj.METERS_PER_UNIT[units] = def.to_meter; - } + if (ol.ENABLE_PROJ4JS) { + var proj4js = ol.proj.proj4_ || goog.global['proj4']; + if (typeof proj4js == 'function' && projections[code] === undefined) { + var def = proj4js.defs(code); + if (def !== undefined) { + if (def.axis !== undefined && options.axisOrientation === undefined) { + this.axisOrientation_ = def.axis; } - this.units_ = units; - } - var currentCode, currentDef, currentProj, proj4Transform; - for (currentCode in projections) { - currentDef = proj4.defs(currentCode); - if (currentDef !== undefined) { - currentProj = ol.proj.get(currentCode); - if (currentDef === def) { - ol.proj.addEquivalentProjections([currentProj, this]); - } else { - proj4Transform = proj4(currentCode, code); - ol.proj.addCoordinateTransforms(currentProj, this, - proj4Transform.forward, proj4Transform.inverse); + if (options.units === undefined) { + var units = def.units; + if (def.to_meter !== undefined) { + if (units === undefined || + ol.proj.METERS_PER_UNIT[units] === undefined) { + units = def.to_meter.toString(); + ol.proj.METERS_PER_UNIT[units] = def.to_meter; + } + } + this.units_ = units; + } + var currentCode, currentDef, currentProj, proj4Transform; + for (currentCode in projections) { + currentDef = proj4js.defs(currentCode); + if (currentDef !== undefined) { + currentProj = ol.proj.get(currentCode); + if (currentDef === def) { + ol.proj.addEquivalentProjections([currentProj, this]); + } else { + proj4Transform = proj4js(currentCode, code); + ol.proj.addCoordinateTransforms(currentProj, this, + proj4Transform.forward, proj4Transform.inverse); + } } } } @@ -405,6 +408,34 @@ ol.proj.projections_ = {}; ol.proj.transforms_ = {}; +/** + * @private + * @type {proj4} + */ +ol.proj.proj4_ = null; + + +if (ol.ENABLE_PROJ4JS) { + /** + * Register proj4. If not explicitly registered, it will be assumed that + * proj4js will be loaded in the global namespace. For example in a + * browserify ES6 environment you could use: + * + * import ol from 'openlayers'; + * import proj4 from 'proj4'; + * ol.proj.setProj4(proj4); + * + * @param {proj4} proj4 Proj4. + * @api + */ + ol.proj.setProj4 = function(proj4) { + goog.asserts.assert(typeof proj4 == 'function', + 'proj4 argument should be a function'); + ol.proj.proj4_ = proj4; + }; +} + + /** * Registers transformation functions that don't alter coordinates. Those allow * to transform between projections with equal meaning. @@ -653,10 +684,13 @@ ol.proj.get = function(projectionLike) { } else if (goog.isString(projectionLike)) { var code = projectionLike; projection = ol.proj.projections_[code]; - if (ol.ENABLE_PROJ4JS && projection === undefined && - typeof proj4 == 'function' && proj4.defs(code) !== undefined) { - projection = new ol.proj.Projection({code: code}); - ol.proj.addProjection(projection); + if (ol.ENABLE_PROJ4JS) { + var proj4js = ol.proj.proj4_ || goog.global['proj4']; + if (projection === undefined && typeof proj4js == 'function' && + proj4js.defs(code) !== undefined) { + projection = new ol.proj.Projection({code: code}); + ol.proj.addProjection(projection); + } } } else { projection = null; diff --git a/test/spec/ol/proj/proj.test.js b/test/spec/ol/proj/proj.test.js index 672625040c..c6b83fff26 100644 --- a/test/spec/ol/proj/proj.test.js +++ b/test/spec/ol/proj/proj.test.js @@ -221,6 +221,25 @@ describe('ol.proj', function() { delete proj4.defs['EPSG:21781']; }); + it('can use an alternative namespace for proj4', function() { + var proj4 = window.proj4; + var proj4new = proj4; + delete window.proj4; + ol.proj.setProj4(proj4new); + proj4new.defs('EPSG:21781', + '+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 ' + + '+k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel ' + + '+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs'); + var proj = ol.proj.get('EPSG:21781'); + expect(proj.getCode()).to.eql('EPSG:21781'); + expect(proj.getUnits()).to.eql('m'); + expect(proj.getMetersPerUnit()).to.eql(1); + + delete proj4new.defs['EPSG:21781']; + window.proj4 = proj4; + ol.proj.setProj4(window.proj4); + }); + it('creates ol.proj.Projection instance from EPSG:3739', function() { proj4.defs('EPSG:3739', '+proj=tmerc +lat_0=40.5 +lon_0=-110.0833333333333 +k=0.9999375 ' +