diff --git a/build.py b/build.py index cf7ff7a6b7..b24075599f 100755 --- a/build.py +++ b/build.py @@ -128,10 +128,6 @@ SRC = [path if path.endswith('.js') if path not in SHADER_SRC] -PROJ4JS = 'build/proj4js/lib/proj4js-combined.js' -PROJ4JS_ZIP = 'build/proj4js-1.1.0.zip' -PROJ4JS_ZIP_MD5 = '17caad64cf6ebc6e6fe62f292b134897' - def report_sizes(t): stringio = StringIO() @@ -348,7 +344,7 @@ def examples_star_combined_js(name, match): return Target(name, action=action, dependencies=dependencies) -@target('serve', PROJ4JS, 'examples') +@target('serve', 'examples') def serve(t): t.run('node', 'tasks/serve.js') @@ -671,24 +667,7 @@ def check_examples(t): t.run('%(PHANTOMJS)s', 'bin/check-example.js', example) -@target(PROJ4JS, PROJ4JS_ZIP) -def proj4js(t): - from zipfile import ZipFile - zf = ZipFile(PROJ4JS_ZIP) - contents = zf.open('proj4js/lib/proj4js-combined.js').read() - with open(t.name, 'wb') as f: - f.write(contents) - - -@target(PROJ4JS_ZIP, clean=False) -def proj4js_zip(t): - t.info('downloading %r', t.name) - t.download('http://download.osgeo.org/proj4js/' + - os.path.basename(t.name), md5=PROJ4JS_ZIP_MD5) - t.info('downloaded %r', t.name) - - -@target('test', PROJ4JS, phony=True) +@target('test', phony=True) def test(t): t.run('node', 'tasks/test.js') diff --git a/examples/wms-custom-proj.html b/examples/wms-custom-proj.html index 12a1dec0f1..45fe8f8fec 100644 --- a/examples/wms-custom-proj.html +++ b/examples/wms-custom-proj.html @@ -8,7 +8,7 @@ - Tiled WMS with Proj4js projection example + Tiled WMS with custom projection example @@ -31,21 +31,18 @@
-

Tiled WMS with Proj4js projection example

+

Tiled WMS with custom projection example

Example of two tiled WMS layers (Pixelmap 1:1'000'000 and national parks) using the projection EPSG:21781.

See the wms-custom-proj.js source to see how this is done.

-
wms, tile, tilelayer, proj4js, projection
+
wms, tile, tilelayer, projection
- - - diff --git a/examples/wms-custom-proj.js b/examples/wms-custom-proj.js index 39ba860d2b..e3b5f2920b 100644 --- a/examples/wms-custom-proj.js +++ b/examples/wms-custom-proj.js @@ -5,13 +5,41 @@ goog.require('ol.control'); goog.require('ol.control.ScaleLine'); goog.require('ol.layer.Tile'); goog.require('ol.proj'); +goog.require('ol.proj.Projection'); goog.require('ol.source.TileWMS'); -var projection = ol.proj.configureProj4jsProjection({ + +// By default OpenLayers does not know about the EPSG:21781 (Swiss) projection. +// So we create a projection instance for EPSG:21781 and pass it to +// ol.proj.addProjection to make it available to the library. + +var projection = new ol.proj.Projection({ code: 'EPSG:21781', - extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864] + // The extent is used to determine zoom level 0. Recommended values for a + // projection's validity extent can be found at http://epsg.io/. + extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864], + units: 'm' }); +ol.proj.addProjection(projection); + +// We also declare EPSG:21781/EPSG:4326 transform functions. These functions +// are necessary for the ScaleLine control and when calling ol.proj.transform +// for setting the view's initial center (see below). + +ol.proj.addCoordinateTransforms('EPSG:4326', projection, + function(coordinate) { + return [ + WGStoCHy(coordinate[1], coordinate[0]), + WGStoCHx(coordinate[1], coordinate[0]) + ]; + }, + function(coordinate) { + return [ + CHtoWGSlng(coordinate[0], coordinate[1]), + CHtoWGSlat(coordinate[0], coordinate[1]) + ]; + }); var extent = [420000, 30000, 900000, 350000]; var layers = [ @@ -61,8 +89,160 @@ var map = new ol.Map({ target: 'map', view: new ol.View({ projection: projection, - center: [660000, 190000], + center: ol.proj.transform([8.23, 46.86], 'EPSG:4326', 'EPSG:21781'), extent: extent, zoom: 2 }) }); + + + +/* + * Swiss projection transform functions downloaded from + * http://www.swisstopo.admin.ch/internet/swisstopo/en/home/products/software/products/skripts.html + */ + +// Convert WGS lat/long (° dec) to CH y +function WGStoCHy(lat, lng) { + + // Converts degrees dec to sex + lat = DECtoSEX(lat); + lng = DECtoSEX(lng); + + // Converts degrees to seconds (sex) + lat = DEGtoSEC(lat); + lng = DEGtoSEC(lng); + + // Axiliary values (% Bern) + var lat_aux = (lat - 169028.66) / 10000; + var lng_aux = (lng - 26782.5) / 10000; + + // Process Y + var y = 600072.37 + + 211455.93 * lng_aux - + 10938.51 * lng_aux * lat_aux - + 0.36 * lng_aux * Math.pow(lat_aux, 2) - + 44.54 * Math.pow(lng_aux, 3); + + return y; +} + +// Convert WGS lat/long (° dec) to CH x +function WGStoCHx(lat, lng) { + + // Converts degrees dec to sex + lat = DECtoSEX(lat); + lng = DECtoSEX(lng); + + // Converts degrees to seconds (sex) + lat = DEGtoSEC(lat); + lng = DEGtoSEC(lng); + + // Axiliary values (% Bern) + var lat_aux = (lat - 169028.66) / 10000; + var lng_aux = (lng - 26782.5) / 10000; + + // Process X + var x = 200147.07 + + 308807.95 * lat_aux + + 3745.25 * Math.pow(lng_aux, 2) + + 76.63 * Math.pow(lat_aux, 2) - + 194.56 * Math.pow(lng_aux, 2) * lat_aux + + 119.79 * Math.pow(lat_aux, 3); + + return x; + +} + + +// Convert CH y/x to WGS lat +function CHtoWGSlat(y, x) { + + // Converts militar to civil and to unit = 1000km + // Axiliary values (% Bern) + var y_aux = (y - 600000) / 1000000; + var x_aux = (x - 200000) / 1000000; + + // Process lat + var lat = 16.9023892 + + 3.238272 * x_aux - + 0.270978 * Math.pow(y_aux, 2) - + 0.002528 * Math.pow(x_aux, 2) - + 0.0447 * Math.pow(y_aux, 2) * x_aux - + 0.0140 * Math.pow(x_aux, 3); + + // Unit 10000" to 1 " and converts seconds to degrees (dec) + lat = lat * 100 / 36; + + return lat; + +} + +// Convert CH y/x to WGS long +function CHtoWGSlng(y, x) { + + // Converts militar to civil and to unit = 1000km + // Axiliary values (% Bern) + var y_aux = (y - 600000) / 1000000; + var x_aux = (x - 200000) / 1000000; + + // Process long + var lng = 2.6779094 + + 4.728982 * y_aux + + 0.791484 * y_aux * x_aux + + 0.1306 * y_aux * Math.pow(x_aux, 2) - + 0.0436 * Math.pow(y_aux, 3); + + // Unit 10000" to 1 " and converts seconds to degrees (dec) + lng = lng * 100 / 36; + + return lng; + +} + + +// Convert SEX DMS angle to DEC +function SEXtoDEC(angle) { + + // Extract DMS + var deg = parseInt(angle, 10); + var min = parseInt((angle - deg) * 100, 10); + var sec = (((angle - deg) * 100) - min) * 100; + + // Result in degrees sex (dd.mmss) + return deg + (sec / 60 + min) / 60; + +} + +// Convert DEC angle to SEX DMS +function DECtoSEX(angle) { + + // Extract DMS + var deg = parseInt(angle, 10); + var min = parseInt((angle - deg) * 60, 10); + var sec = (((angle - deg) * 60) - min) * 60; + + // Result in degrees sex (dd.mmss) + return deg + min / 100 + sec / 10000; + +} + +// Convert Degrees angle to seconds +function DEGtoSEC(angle) { + + // Extract DMS + var deg = parseInt(angle, 10); + var min = parseInt((angle - deg) * 100, 10); + var sec = (((angle - deg) * 100) - min) * 100; + + // Avoid rounding problems with seconds=0 + var parts = String(angle).split('.'); + if (parts.length == 2 && parts[1].length == 2) { + min = Number(parts[1]); + sec = 0; + } + + // Result in degrees sex (dd.mmss) + return sec + min * 60 + deg * 3600; + +} diff --git a/examples/wms-image-custom-proj.html b/examples/wms-image-custom-proj.html index e8c7230996..bbdd94039e 100644 --- a/examples/wms-image-custom-proj.html +++ b/examples/wms-image-custom-proj.html @@ -8,7 +8,7 @@ - Single image WMS with custom projection example + Single image WMS with Proj4js projection example @@ -31,13 +31,13 @@
-

Single image WMS with custom projection example

+

Single image WMS with Proj4js projection example

Example of two single image WMS layers.

Pixelmap 1:1'000'000 with National Parks overlay using the projection EPSG:21781.

See the wms-image-custom-proj.js source to see how this is done.

-
wms, single image, projection
+
wms, single image, proj4js, projection
@@ -45,8 +45,8 @@ - - + + diff --git a/examples/wms-image-custom-proj.js b/examples/wms-image-custom-proj.js index d7f0b45f0b..05292d72ae 100644 --- a/examples/wms-image-custom-proj.js +++ b/examples/wms-image-custom-proj.js @@ -1,15 +1,21 @@ goog.require('ol.Attribution'); goog.require('ol.Map'); goog.require('ol.View'); +goog.require('ol.control'); +goog.require('ol.control.ScaleLine'); goog.require('ol.layer.Image'); goog.require('ol.proj'); goog.require('ol.source.ImageWMS'); -var projection = ol.proj.configureProj4jsProjection({ - code: 'EPSG:21781', - extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864] -}); +// Transparent Proj4js support: ol.proj.get() creates and returns a projection +// known to Proj4js if it is unknown to OpenLayers, and registers functions to +// transform between all registered projections. +// EPSG:21781 is known to Proj4js because its definition was loaded in the html. +var projection = ol.proj.get('EPSG:21781'); +// The extent is used to determine zoom level 0. Recommended values for a +// projection's validity extent can be found at http://epsg.io/. +projection.setExtent([485869.5728, 76443.1884, 837076.5648, 299941.7864]); var extent = [420000, 30000, 900000, 350000]; var layers = [ @@ -49,12 +55,15 @@ var layers = [ ]; var map = new ol.Map({ + controls: ol.control.defaults().extend([ + new ol.control.ScaleLine() + ]), layers: layers, renderer: exampleNS.getRendererFromQueryString(), target: 'map', view: new ol.View({ projection: projection, - center: [660000, 190000], + center: ol.proj.transform([8.23, 46.86], 'EPSG:4326', 'EPSG:21781'), extent: extent, zoom: 2 }) diff --git a/externs/olx.js b/externs/olx.js index 4dc7e75a87..59f641f951 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -274,37 +274,6 @@ olx.OverlayOptions.prototype.stopEvent; olx.OverlayOptions.prototype.insertFirst; -/** - * Object literal with config options for the Proj4js projection. - * @typedef {{code: string, - * extent: (ol.Extent|undefined), - * global: (boolean|undefined)}} - * @api - */ -olx.Proj4jsProjectionOptions; - - -/** - * The SRS identifier code, e.g. `EPSG:31256`. - * @type {string} - */ -olx.Proj4jsProjectionOptions.prototype.code; - - -/** - * The validity extent for the SRS. - * @type {ol.Extent|undefined} - */ -olx.Proj4jsProjectionOptions.prototype.extent; - - -/** - * Whether the projection is valid for the whole globe. Default is `false`. - * @type {boolean|undefined} - */ -olx.Proj4jsProjectionOptions.prototype.global; - - /** * Object literal with config options for the projection. * @typedef {{code: string, diff --git a/externs/proj4js.js b/externs/proj4js.js index d91a06b561..aca6cfd8e2 100644 --- a/externs/proj4js.js +++ b/externs/proj4js.js @@ -1,92 +1,22 @@ /** * @externs - * @see http://trac.osgeo.org/proj4js/ + * @see http://proj4js.org/ */ /** - * @type {Object} + * @param {...*} var_args + * @return {undefined|Array.|Object.<{ + * forward: function(Array.): Array., + * inverse: function(Array.): Array.}>} */ -var Proj4js = {}; +var proj4 = function(var_args) {}; /** - * @type {Object.} + * @param {string} name + * @param {(string|Object)=} opt_def + * @return {undefined|Object.>} */ -Proj4js.defs; - - -/** - * @type {function(string)} - */ -Proj4js.reportError; - - - -/** - * @constructor - * @param {number} x - * @param {number} y - */ -Proj4js.Point = function(x, y) {}; - - -/** - * @type {number} - */ -Proj4js.Point.prototype.x; - - -/** - * @type {number} - */ -Proj4js.Point.prototype.y; - - - -/** - * @constructor - * @param {string} srsCode - * @param {Function=} opt_callback - */ -Proj4js.Proj = function(srsCode, opt_callback) {}; - - -/** - * @type {string} - */ -Proj4js.Proj.prototype.axis; - - -/** - * @type {string} - */ -Proj4js.Proj.prototype.title; - - -/** - * @type {string} - */ -Proj4js.Proj.prototype.units; - - -/** - * @type {string} - */ -Proj4js.Proj.prototype.srsCode; - - -/** - * @type {number} - */ -Proj4js.Proj.prototype.to_meter; - - -/** - * @nosideeffects - * @param {Proj4js.Proj} source - * @param {Proj4js.Proj} dest - * @param {Proj4js.Point|{x:number, y:number}} point - * @return {Proj4js.Point} - */ -Proj4js.transform = function(source, dest, point) {return null;}; +proj4.defs = function(name, opt_def) {}; diff --git a/package.json b/package.json index 6f5a267482..dbf6d6f482 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "mocha-phantomjs": "~3.5.0", "nomnom": "~1.6.2", "phantomjs": "~1.9.7-5", + "proj4": "~2.2.1", "sinon": "~1.10.2", "temp": "~0.7.0", "walk": "~2.3.3" diff --git a/src/ol/proj/proj.js b/src/ol/proj/proj.js index 421874a57f..bbf004ad04 100644 --- a/src/ol/proj/proj.js +++ b/src/ol/proj/proj.js @@ -14,14 +14,6 @@ goog.require('ol.extent'); goog.require('ol.sphere.NORMAL'); -/** - * Have Proj4js. - * @const - * @type {boolean} - */ -ol.HAVE_PROJ4JS = ol.ENABLE_PROJ4JS && typeof Proj4js == 'object'; - - /** * A projection as {@link ol.proj.Projection}, SRS identifier string or * undefined. @@ -62,12 +54,13 @@ ol.proj.METERS_PER_UNIT[ol.proj.Units.METERS] = 1; * Class for coordinate transforms between coordinate systems. By default, * OpenLayers ships with the ability to transform coordinates between * geographic (EPSG:4326) and web or spherical mercator (EPSG:3857) - * coordinate reference systems. + * coordinate reference systems. Any transform functions can be added with + * {@link ol.proj.addCoordinateTransforms}. * - * Additional transforms may be added by using the - * {@link http://proj4js.org/|proj4js} library. If the proj4js library is - * included, the method will work between any two coordinate - * reference systems with proj4js definitions. + * Additional transforms may be added by using the {@link http://proj4js.org/} + * library. If the proj4js library is loaded, transforms will work between any + * coordinate reference systems with proj4js definitions. These definitions can + * be obtained from {@link http://epsg.io/}. * * @constructor * @param {olx.ProjectionOptions} options Projection options. @@ -136,19 +129,6 @@ ol.proj.Projection.prototype.getExtent = function() { }; -/** - * Get the resolution of the point in degrees. For projections with degrees as - * the unit this will simply return the provided resolution. For other - * projections the point resolution is estimated by transforming the center - * pixel to EPSG:4326, measuring its width and height on the normal sphere, - * and taking the average of the width and height. - * @param {number} resolution Resolution. - * @param {ol.Coordinate} point Point. - * @return {number} Point resolution. - */ -ol.proj.Projection.prototype.getPointResolution = goog.abstractMethod; - - /** * Get the units of this projection. * @return {ol.proj.Units} Units. @@ -209,80 +189,42 @@ ol.proj.Projection.prototype.setDefaultTileGrid = function(tileGrid) { }; - /** - * @constructor - * @extends {ol.proj.Projection} - * @param {Proj4js.Proj} proj4jsProj Proj4js projection. - * @param {olx.Proj4jsProjectionOptions} options Proj4js projection options. - * @private - * @struct + * Set the validity extent for this projection. + * @param {ol.Extent} extent Extent. + * @api */ -ol.Proj4jsProjection_ = function(proj4jsProj, options) { - - var units = /** @type {ol.proj.Units} */ (proj4jsProj.units); - - var config = /** @type {olx.ProjectionOptions} */ ({ - units: units, - axisOrientation: proj4jsProj.axis - }); - goog.object.extend(config, options); - - goog.base(this, config); - - /** - * @private - * @type {Proj4js.Proj} - */ - this.proj4jsProj_ = proj4jsProj; - - /** - * @private - * @type {?ol.TransformFunction} - */ - this.toEPSG4326_ = null; - -}; -goog.inherits(ol.Proj4jsProjection_, ol.proj.Projection); - - -/** - * @inheritDoc - */ -ol.Proj4jsProjection_.prototype.getMetersPerUnit = function() { - var metersPerUnit = this.proj4jsProj_.to_meter; - if (!goog.isDef(metersPerUnit)) { - metersPerUnit = ol.proj.METERS_PER_UNIT[this.units_]; - } - return metersPerUnit; +ol.proj.Projection.prototype.setExtent = function(extent) { + this.extent_ = extent; }; /** - * @inheritDoc + * Get the resolution of the point in degrees. For projections with degrees as + * the unit this will simply return the provided resolution. For other + * projections the point resolution is estimated by transforming the center + * pixel to EPSG:4326, measuring its width and height on the normal sphere, + * and taking the average of the width and height. + * @param {number} resolution Resolution. + * @param {ol.Coordinate} point Point. + * @return {number} Point resolution. */ -ol.Proj4jsProjection_.prototype.getPointResolution = - function(resolution, point) { +ol.proj.Projection.prototype.getPointResolution = function(resolution, point) { if (this.getUnits() == ol.proj.Units.DEGREES) { return resolution; } else { // Estimate point resolution by transforming the center pixel to EPSG:4326, // measuring its width and height on the normal sphere, and taking the // average of the width and height. - if (goog.isNull(this.toEPSG4326_)) { - this.toEPSG4326_ = ol.proj.getTransformFromProjections( - this, ol.proj.getProj4jsProjectionFromCode_({ - code: 'EPSG:4326', - extent: null - })); - } + var toEPSG4326 = ol.proj.getTransformFromProjections( + this, ol.proj.get('EPSG:4326')); var vertices = [ point[0] - resolution / 2, point[1], point[0] + resolution / 2, point[1], point[0], point[1] - resolution / 2, point[0], point[1] + resolution / 2 ]; - vertices = this.toEPSG4326_(vertices, vertices, 2); + vertices = toEPSG4326(vertices, vertices, 2); var width = ol.sphere.NORMAL.haversineDistance( vertices.slice(0, 2), vertices.slice(2, 4)); var height = ol.sphere.NORMAL.haversineDistance( @@ -298,21 +240,6 @@ ol.Proj4jsProjection_.prototype.getPointResolution = }; -/** - * @return {Proj4js.Proj} Proj4js projection. - */ -ol.Proj4jsProjection_.prototype.getProj4jsProj = function() { - return this.proj4jsProj_; -}; - - -/** - * @private - * @type {Object.} - */ -ol.proj.proj4jsProjections_ = {}; - - /** * @private * @type {Object.} @@ -332,6 +259,7 @@ ol.proj.transforms_ = {}; * to transform between projections with equal meaning. * * @param {Array.} projections Projections. + * @api */ ol.proj.addEquivalentProjections = function(projections) { ol.proj.addProjections(projections); @@ -369,28 +297,14 @@ ol.proj.addEquivalentTransforms = }; -/** - * @param {ol.Proj4jsProjection_} proj4jsProjection Proj4js projection. - * @private - */ -ol.proj.addProj4jsProjection_ = function(proj4jsProjection) { - var proj4jsProjections = ol.proj.proj4jsProjections_; - var code = proj4jsProjection.getCode(); - goog.asserts.assert(!goog.object.containsKey(proj4jsProjections, code)); - proj4jsProjections[code] = proj4jsProjection; -}; - - /** * Add a Projection object to the list of supported projections. * - * @param {ol.proj.Projection} projection Projection object. + * @param {ol.proj.Projection} projection Projection instance. * @api */ ol.proj.addProjection = function(projection) { - var projections = ol.proj.projections_; - var code = projection.getCode(); - projections[code] = projection; + ol.proj.projections_[projection.getCode()] = projection; ol.proj.addTransform(projection, projection, ol.proj.cloneTransform); }; @@ -399,8 +313,9 @@ ol.proj.addProjection = function(projection) { * @param {Array.} projections Projections. */ ol.proj.addProjections = function(projections) { + var addedProjections = []; goog.array.forEach(projections, function(projection) { - ol.proj.addProjection(projection); + addedProjections.push(ol.proj.addProjection(projection)); }); }; @@ -409,9 +324,6 @@ ol.proj.addProjections = function(projections) { * FIXME empty description for jsdoc */ ol.proj.clearAllProjections = function() { - if (ol.ENABLE_PROJ4JS) { - ol.proj.proj4jsProjections_ = {}; - } ol.proj.projections_ = {}; ol.proj.transforms_ = {}; }; @@ -453,6 +365,66 @@ ol.proj.addTransform = function(source, destination, transformFn) { }; +/** + * Registers coordinate transform functions to convert coordinates between the + * source projection and the destination projection. + * + * @param {ol.proj.ProjectionLike} source Source projection. + * @param {ol.proj.ProjectionLike} destination Destination projection. + * @param {function(ol.Coordinate): ol.Coordinate} forward The forward transform + * function (that is, from the source projection to the destination + * projection) that takes a {@link ol.Coordinate} as argument and returns + * the transformed {@link ol.Coordinate}. + * @param {function(ol.Coordinate): ol.Coordinate} inverse The inverse transform + * function (that is, from the destination projection to the source + * projection) that takes a {@link ol.Coordinate} as argument and returns + * the transformed {@link ol.Coordinate}. + * @api + */ +ol.proj.addCoordinateTransforms = + function(source, destination, forward, inverse) { + var sourceProj = ol.proj.get(source); + var destProj = ol.proj.get(destination); + ol.proj.addTransform(sourceProj, destProj, + ol.proj.createTransformFromCoordinateTransform(forward)); + ol.proj.addTransform(destProj, sourceProj, + ol.proj.createTransformFromCoordinateTransform(inverse)); +}; + + +/** + * Creates a {@link ol.TransformFunction} from a simple 2D coordinate transform + * function. + * @param {function(ol.Coordinate): ol.Coordinate} transform Coordinate + * transform. + * @return {ol.TransformFunction} Transform function. + */ +ol.proj.createTransformFromCoordinateTransform = function(transform) { + return ( + /** + * @param {Array.} input Input. + * @param {Array.=} opt_output Output. + * @param {number=} opt_dimension Dimension. + * @return {Array.} Output. + */ + function(input, opt_output, opt_dimension) { + var length = input.length; + var dimension = goog.isDef(opt_dimension) ? opt_dimension : 2; + var output = goog.isDef(opt_output) ? opt_output : new Array(length); + var point, i, j; + for (i = 0; i < length; i += dimension) { + point = transform([input[i], input[i + 1]]); + output[i] = point[0]; + output[i + 1] = point[1]; + for (j = dimension - 1; j >= 2; --j) { + output[i + j] = input[i + j]; + } + } + return output; + }); +}; + + /** * Unregisters the conversion function to convert coordinates from the source * projection to the destination projection. This method is used to clean up @@ -493,16 +465,43 @@ ol.proj.get = function(projectionLike) { projection = projectionLike; } else if (goog.isString(projectionLike)) { var code = projectionLike; - projection = ol.proj.projections_[code]; - if (ol.HAVE_PROJ4JS && !goog.isDef(projection)) { - projection = ol.proj.getProj4jsProjectionFromCode_({ - code: code, - extent: null - }); - } - if (!goog.isDef(projection)) { - goog.asserts.assert(goog.isDef(projection)); - projection = null; + var projections = ol.proj.projections_; + projection = projections[code]; + if (ol.ENABLE_PROJ4JS && !goog.isDef(projection) && + goog.isFunction(proj4)) { + var def = proj4.defs(code); + if (goog.isDef(def)) { + var units = def.units; + if (!goog.isDef(units)) { + if (goog.isDef(def.to_meter)) { + units = def.to_meter.toString(); + ol.proj.METERS_PER_UNIT[units] = def.to_meter; + } + } + projection = new ol.proj.Projection({ + code: code, + units: units, + axisOrientation: def.axis + }); + ol.proj.addProjection(projection); + var currentCode, currentDef, currentProj, proj4Transform; + for (currentCode in projections) { + currentDef = proj4.defs(currentCode); + if (goog.isDef(currentDef)) { + currentProj = ol.proj.get(currentCode); + if (currentDef === def) { + ol.proj.addEquivalentProjections([currentProj, projection]); + } else { + proj4Transform = proj4(currentCode, code); + ol.proj.addCoordinateTransforms(currentProj, projection, + proj4Transform.forward, proj4Transform.inverse); + } + } + } + } else { + goog.asserts.assert(goog.isDef(projection)); + projection = null; + } } } else { projection = null; @@ -511,32 +510,6 @@ ol.proj.get = function(projectionLike) { }; -/** - * @param {olx.Proj4jsProjectionOptions} options Proj4js projection options. - * @private - * @return {ol.Proj4jsProjection_} Proj4js projection. - */ -ol.proj.getProj4jsProjectionFromCode_ = function(options) { - var code = options.code; - var proj4jsProjections = ol.proj.proj4jsProjections_; - var proj4jsProjection = proj4jsProjections[code]; - if (!goog.isDef(proj4jsProjection)) { - var proj4jsProj = new Proj4js.Proj(code); - var srsCode = proj4jsProj.srsCode; - proj4jsProjection = proj4jsProjections[srsCode]; - if (!goog.isDef(proj4jsProjection)) { - var config = /** @type {olx.Proj4jsProjectionOptions} */ - (goog.object.clone(options)); - config.code = srsCode; - proj4jsProjection = new ol.Proj4jsProjection_(proj4jsProj, config); - proj4jsProjections[srsCode] = proj4jsProjection; - } - proj4jsProjections[code] = proj4jsProjection; - } - return proj4jsProjection; -}; - - /** * Checks if two projections are the same, that is every coordinate in one * projection does represent the same geographic point as the same coordinate in @@ -596,61 +569,6 @@ ol.proj.getTransformFromProjections = goog.object.containsKey(transforms[sourceCode], destinationCode)) { transform = transforms[sourceCode][destinationCode]; } - if (ol.HAVE_PROJ4JS && !goog.isDef(transform)) { - var proj4jsSource; - if (sourceProjection instanceof ol.Proj4jsProjection_) { - proj4jsSource = sourceProjection; - } else { - proj4jsSource = - ol.proj.getProj4jsProjectionFromCode_({ - code: sourceCode, - extent: null - }); - } - var sourceProj4jsProj = proj4jsSource.getProj4jsProj(); - var proj4jsDestination; - if (destinationProjection instanceof ol.Proj4jsProjection_) { - proj4jsDestination = destinationProjection; - } else { - proj4jsDestination = - ol.proj.getProj4jsProjectionFromCode_({ - code: destinationCode, - extent: null - }); - } - var destinationProj4jsProj = proj4jsDestination.getProj4jsProj(); - transform = - /** - * @param {Array.} input Input coordinate values. - * @param {Array.=} opt_output Output array of coordinates. - * @param {number=} opt_dimension Dimension. - * @return {Array.} Output coordinate values. - */ - function(input, opt_output, opt_dimension) { - var length = input.length, - dimension = opt_dimension > 1 ? opt_dimension : 2, - output = opt_output; - if (!goog.isDef(output)) { - if (dimension > 2) { - // preserve values beyond second dimension - output = input.slice(); - } else { - output = new Array(length); - } - } - goog.asserts.assert(output.length % dimension === 0); - var proj4jsPoint; - for (var i = 0; i < length; i += dimension) { - proj4jsPoint = new Proj4js.Point(input[i], input[i + 1]); - proj4jsPoint = Proj4js.transform( - sourceProj4jsProj, destinationProj4jsProj, proj4jsPoint); - output[i] = proj4jsPoint.x; - output[i + 1] = proj4jsPoint.y; - } - return output; - }; - ol.proj.addTransform(sourceProjection, destinationProjection, transform); - } if (!goog.isDef(transform)) { goog.asserts.assert(goog.isDef(transform)); transform = ol.proj.identityTransform; @@ -749,15 +667,3 @@ ol.proj.transformWithProjections = sourceProjection, destinationProjection); return transformFn(point); }; - - -/** - * @param {olx.Proj4jsProjectionOptions} options Proj4js projection options. - * @return {ol.proj.Projection} Proj4js projection. - * @api - */ -ol.proj.configureProj4jsProjection = function(options) { - goog.asserts.assert(!goog.object.containsKey( - ol.proj.proj4jsProjections_, options.code)); - return ol.proj.getProj4jsProjectionFromCode_(options); -}; diff --git a/test/index.html b/test/index.html index 09681d5542..e8400cbaef 100644 --- a/test/index.html +++ b/test/index.html @@ -13,6 +13,7 @@ + - diff --git a/test/spec/ol/format/wfsformat.test.js b/test/spec/ol/format/wfsformat.test.js index 87e4873da0..411842bf2f 100644 --- a/test/spec/ol/format/wfsformat.test.js +++ b/test/spec/ol/format/wfsformat.test.js @@ -6,6 +6,7 @@ describe('ol.format.WFS', function() { var features, feature; before(function(done) { + proj4.defs('urn:x-ogc:def:crs:EPSG:4326', proj4.defs('EPSG:4326')); afterLoadText('spec/ol/format/wfs/topp-states-wfs.xml', function(xml) { try { var config = { @@ -54,6 +55,10 @@ describe('ol.format.WFS', function() { describe('when parsing FeatureCollection', function() { var response; before(function(done) { + proj4.defs('EPSG:28992', '+proj=sterea +lat_0=52.15616055555555 ' + + '+lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 ' + + '+ellps=bessel +towgs84=565.417,50.3319,465.552,-0.398957,0.343988,' + + '-1.8774,4.0725 +units=m +no_defs'); afterLoadText('spec/ol/format/wfs/boundedBy.xml', function(xml) { try { diff --git a/test/spec/ol/proj/proj.test.js b/test/spec/ol/proj/proj.test.js index 9cceae83d7..07efbcca59 100644 --- a/test/spec/ol/proj/proj.test.js +++ b/test/spec/ol/proj/proj.test.js @@ -128,28 +128,30 @@ describe('ol.proj', function() { }); it('allows new Proj4js projections to be defined', function() { - Proj4js.defs['EPSG:21781'] = + proj4.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'; + '+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs'); var point = ol.proj.transform([7.439583333333333, 46.95240555555556], 'EPSG:4326', 'EPSG:21781'); expect(point[0]).to.roughlyEqual(600072.300, 1); expect(point[1]).to.roughlyEqual(200146.976, 1); + delete proj4.defs['EPSG:21781']; }); it('caches the new Proj4js projections given their srsCode', function() { - Proj4js.defs['EPSG:21781'] = + proj4.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'; + '+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs'); var code = 'urn:ogc:def:crs:EPSG:21781'; var srsCode = 'EPSG:21781'; + proj4.defs(code, proj4.defs(srsCode)); var proj = ol.proj.get(code); - expect(ol.proj.proj4jsProjections_.hasOwnProperty(code)).to.be.ok(); - expect(ol.proj.proj4jsProjections_.hasOwnProperty(srsCode)).to.be.ok(); var proj2 = ol.proj.get(srsCode); - expect(proj2).to.be(proj); + expect(ol.proj.equivalent(proj2, proj)).to.be(true); + delete proj4.defs[code]; + delete proj4.defs[srsCode]; }); it('numerically estimates point scale at the equator', function() { @@ -186,11 +188,9 @@ describe('ol.proj', function() { describe('ol.proj.getTransformFromProjections()', function() { - var sm = ol.proj.get('GOOGLE'); - var gg = ol.proj.get('EPSG:4326'); - it('returns a transform function', function() { - var transform = ol.proj.getTransformFromProjections(sm, gg); + var transform = ol.proj.getTransformFromProjections(ol.proj.get('GOOGLE'), + ol.proj.get('EPSG:4326')); expect(typeof transform).to.be('function'); var output = transform([-12000000, 5000000]); @@ -200,7 +200,8 @@ describe('ol.proj', function() { }); it('works for longer arrays', function() { - var transform = ol.proj.getTransformFromProjections(sm, gg); + var transform = ol.proj.getTransformFromProjections(ol.proj.get('GOOGLE'), + ol.proj.get('EPSG:4326')); expect(typeof transform).to.be('function'); var output = transform([-12000000, 5000000, -12000000, 5000000]); @@ -318,18 +319,14 @@ describe('ol.proj', function() { describe('ol.proj.Projection.prototype.getMetersPerUnit()', function() { beforeEach(function() { - Proj4js.defs['EPSG:26782'] = + proj4.defs('EPSG:26782', '+proj=lcc +lat_1=29.3 +lat_2=30.7 +lat_0=28.66666666666667 ' + '+lon_0=-91.33333333333333 +x_0=609601.2192024384 +y_0=0 ' + - '+ellps=clrk66 +datum=NAD27 +to_meter=0.3048006096012192 +no_defs'; - ol.proj.configureProj4jsProjection({ - code: 'EPSG:26782' - }); + '+ellps=clrk66 +datum=NAD27 +to_meter=0.3048006096012192 +no_defs'); }); afterEach(function() { - ol.proj.proj4jsProjections_ = {}; - delete Proj4js.defs['EPSG:26782']; + delete proj4.defs['EPSG:26782']; }); it('returns value in meters', function() { @@ -344,26 +341,6 @@ describe('ol.proj', function() { }); - describe('ol.proj.configureProj4jsProjection()', function() { - - beforeEach(function() { - ol.proj.proj4jsProjections_ = {}; - }); - - it('returns a configured projection', function() { - var extent = [485869.5728, 76443.1884, 837076.5648, 299941.7864]; - var epsg21781 = ol.proj.configureProj4jsProjection({ - code: 'EPSG:21781', - extent: extent - }); - expect(epsg21781.getCode()).to.eql('EPSG:21781'); - expect(epsg21781.getExtent()).to.be(extent); - expect(epsg21781.getUnits()).to.be(ol.proj.Units.METERS); - expect(epsg21781.isGlobal()).to.not.be(); - }); - - }); - });