Projection.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/BaseTypes/Class.js
  7. * @requires OpenLayers/Util.js
  8. */
  9. /**
  10. * Namespace: OpenLayers.Projection
  11. * Methods for coordinate transforms between coordinate systems. By default,
  12. * OpenLayers ships with the ability to transform coordinates between
  13. * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)
  14. * coordinate reference systems. See the <transform> method for details
  15. * on usage.
  16. *
  17. * Additional transforms may be added by using the <proj4js at http://proj4js.org/>
  18. * library. If the proj4js library is included, the <transform> method
  19. * will work between any two coordinate reference systems with proj4js
  20. * definitions.
  21. *
  22. * If the proj4js library is not included, or if you wish to allow transforms
  23. * between arbitrary coordinate reference systems, use the <addTransform>
  24. * method to register a custom transform method.
  25. */
  26. OpenLayers.Projection = OpenLayers.Class({
  27. /**
  28. * Property: proj
  29. * {Object} Proj4js.Proj instance.
  30. */
  31. proj: null,
  32. /**
  33. * Property: projCode
  34. * {String}
  35. */
  36. projCode: null,
  37. /**
  38. * Property: titleRegEx
  39. * {RegExp} regular expression to strip the title from a proj4js definition
  40. */
  41. titleRegEx: /\+title=[^\+]*/,
  42. /**
  43. * Constructor: OpenLayers.Projection
  44. * This class offers several methods for interacting with a wrapped
  45. * pro4js projection object.
  46. *
  47. * Parameters:
  48. * projCode - {String} A string identifying the Well Known Identifier for
  49. * the projection.
  50. * options - {Object} An optional object to set additional properties
  51. * on the projection.
  52. *
  53. * Returns:
  54. * {<OpenLayers.Projection>} A projection object.
  55. */
  56. initialize: function(projCode, options) {
  57. OpenLayers.Util.extend(this, options);
  58. this.projCode = projCode;
  59. if (typeof Proj4js == "object") {
  60. this.proj = new Proj4js.Proj(projCode);
  61. }
  62. },
  63. /**
  64. * APIMethod: getCode
  65. * Get the string SRS code.
  66. *
  67. * Returns:
  68. * {String} The SRS code.
  69. */
  70. getCode: function() {
  71. return this.proj ? this.proj.srsCode : this.projCode;
  72. },
  73. /**
  74. * APIMethod: getUnits
  75. * Get the units string for the projection -- returns null if
  76. * proj4js is not available.
  77. *
  78. * Returns:
  79. * {String} The units abbreviation.
  80. */
  81. getUnits: function() {
  82. return this.proj ? this.proj.units : null;
  83. },
  84. /**
  85. * Method: toString
  86. * Convert projection to string (getCode wrapper).
  87. *
  88. * Returns:
  89. * {String} The projection code.
  90. */
  91. toString: function() {
  92. return this.getCode();
  93. },
  94. /**
  95. * Method: equals
  96. * Test equality of two projection instances. Determines equality based
  97. * soley on the projection code.
  98. *
  99. * Returns:
  100. * {Boolean} The two projections are equivalent.
  101. */
  102. equals: function(projection) {
  103. var p = projection, equals = false;
  104. if (p) {
  105. if (!(p instanceof OpenLayers.Projection)) {
  106. p = new OpenLayers.Projection(p);
  107. }
  108. if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) {
  109. equals = this.proj.defData.replace(this.titleRegEx, "") ==
  110. p.proj.defData.replace(this.titleRegEx, "");
  111. } else if (p.getCode) {
  112. var source = this.getCode(), target = p.getCode();
  113. equals = source == target ||
  114. !!OpenLayers.Projection.transforms[source] &&
  115. OpenLayers.Projection.transforms[source][target] ===
  116. OpenLayers.Projection.nullTransform;
  117. }
  118. }
  119. return equals;
  120. },
  121. /* Method: destroy
  122. * Destroy projection object.
  123. */
  124. destroy: function() {
  125. delete this.proj;
  126. delete this.projCode;
  127. },
  128. CLASS_NAME: "OpenLayers.Projection"
  129. });
  130. /**
  131. * Property: transforms
  132. * {Object} Transforms is an object, with from properties, each of which may
  133. * have a to property. This allows you to define projections without
  134. * requiring support for proj4js to be included.
  135. *
  136. * This object has keys which correspond to a 'source' projection object. The
  137. * keys should be strings, corresponding to the projection.getCode() value.
  138. * Each source projection object should have a set of destination projection
  139. * keys included in the object.
  140. *
  141. * Each value in the destination object should be a transformation function,
  142. * where the function is expected to be passed an object with a .x and a .y
  143. * property. The function should return the object, with the .x and .y
  144. * transformed according to the transformation function.
  145. *
  146. * Note - Properties on this object should not be set directly. To add a
  147. * transform method to this object, use the <addTransform> method. For an
  148. * example of usage, see the OpenLayers.Layer.SphericalMercator file.
  149. */
  150. OpenLayers.Projection.transforms = {};
  151. /**
  152. * APIProperty: defaults
  153. * {Object} Defaults for the SRS codes known to OpenLayers (currently
  154. * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,
  155. * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,
  156. * maxExtent (the validity extent for the SRS) and yx (true if this SRS is
  157. * known to have a reverse axis order).
  158. */
  159. OpenLayers.Projection.defaults = {
  160. "EPSG:4326": {
  161. units: "degrees",
  162. maxExtent: [-180, -90, 180, 90],
  163. yx: true
  164. },
  165. "CRS:84": {
  166. units: "degrees",
  167. maxExtent: [-180, -90, 180, 90]
  168. },
  169. "EPSG:900913": {
  170. units: "m",
  171. maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]
  172. }
  173. };
  174. /**
  175. * APIMethod: addTransform
  176. * Set a custom transform method between two projections. Use this method in
  177. * cases where the proj4js lib is not available or where custom projections
  178. * need to be handled.
  179. *
  180. * Parameters:
  181. * from - {String} The code for the source projection
  182. * to - {String} the code for the destination projection
  183. * method - {Function} A function that takes a point as an argument and
  184. * transforms that point from the source to the destination projection
  185. * in place. The original point should be modified.
  186. */
  187. OpenLayers.Projection.addTransform = function(from, to, method) {
  188. if (method === OpenLayers.Projection.nullTransform) {
  189. var defaults = OpenLayers.Projection.defaults[from];
  190. if (defaults && !OpenLayers.Projection.defaults[to]) {
  191. OpenLayers.Projection.defaults[to] = defaults;
  192. }
  193. }
  194. if(!OpenLayers.Projection.transforms[from]) {
  195. OpenLayers.Projection.transforms[from] = {};
  196. }
  197. OpenLayers.Projection.transforms[from][to] = method;
  198. };
  199. /**
  200. * APIMethod: transform
  201. * Transform a point coordinate from one projection to another. Note that
  202. * the input point is transformed in place.
  203. *
  204. * Parameters:
  205. * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y
  206. * properties representing coordinates in those dimensions.
  207. * source - {OpenLayers.Projection} Source map coordinate system
  208. * dest - {OpenLayers.Projection} Destination map coordinate system
  209. *
  210. * Returns:
  211. * point - {object} A transformed coordinate. The original point is modified.
  212. */
  213. OpenLayers.Projection.transform = function(point, source, dest) {
  214. if (source && dest) {
  215. if (!(source instanceof OpenLayers.Projection)) {
  216. source = new OpenLayers.Projection(source);
  217. }
  218. if (!(dest instanceof OpenLayers.Projection)) {
  219. dest = new OpenLayers.Projection(dest);
  220. }
  221. if (source.proj && dest.proj) {
  222. point = Proj4js.transform(source.proj, dest.proj, point);
  223. } else {
  224. var sourceCode = source.getCode();
  225. var destCode = dest.getCode();
  226. var transforms = OpenLayers.Projection.transforms;
  227. if (transforms[sourceCode] && transforms[sourceCode][destCode]) {
  228. transforms[sourceCode][destCode](point);
  229. }
  230. }
  231. }
  232. return point;
  233. };
  234. /**
  235. * APIFunction: nullTransform
  236. * A null transformation - useful for defining projection aliases when
  237. * proj4js is not available:
  238. *
  239. * (code)
  240. * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913",
  241. * OpenLayers.Projection.nullTransform);
  242. * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857",
  243. * OpenLayers.Projection.nullTransform);
  244. * (end)
  245. */
  246. OpenLayers.Projection.nullTransform = function(point) {
  247. return point;
  248. };
  249. /**
  250. * Note: Transforms for web mercator <-> geographic
  251. * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.
  252. * OpenLayers originally started referring to EPSG:900913 as web mercator.
  253. * The EPSG has declared EPSG:3857 to be web mercator.
  254. * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as
  255. * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.
  256. * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and
  257. * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis
  258. * order for EPSG:4326.
  259. */
  260. (function() {
  261. var pole = 20037508.34;
  262. function inverseMercator(xy) {
  263. xy.x = 180 * xy.x / pole;
  264. xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);
  265. return xy;
  266. }
  267. function forwardMercator(xy) {
  268. xy.x = xy.x * pole / 180;
  269. var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;
  270. xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));
  271. return xy;
  272. }
  273. function map(base, codes) {
  274. var add = OpenLayers.Projection.addTransform;
  275. var same = OpenLayers.Projection.nullTransform;
  276. var i, len, code, other, j;
  277. for (i=0, len=codes.length; i<len; ++i) {
  278. code = codes[i];
  279. add(base, code, forwardMercator);
  280. add(code, base, inverseMercator);
  281. for (j=i+1; j<len; ++j) {
  282. other = codes[j];
  283. add(code, other, same);
  284. add(other, code, same);
  285. }
  286. }
  287. }
  288. // list of equivalent codes for web mercator
  289. var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"],
  290. geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"],
  291. i;
  292. for (i=mercator.length-1; i>=0; --i) {
  293. map(mercator[i], geographic);
  294. }
  295. for (i=geographic.length-1; i>=0; --i) {
  296. map(geographic[i], mercator);
  297. }
  298. })();