openlayers.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*jslint white: false */
  2. /*jslint forin: true */
  3. /*global OpenLayers Drupal $ document jQuery window */
  4. /**
  5. * @file
  6. * This file holds the main javascript API for OpenLayers. It is
  7. * responsable for loading and displaying the map.
  8. *
  9. * @ingroup openlayers
  10. */
  11. /**
  12. * This is a workaround for a bug involving IE and VML support.
  13. * See the Drupal Book page describing this problem:
  14. * http://drupal.org/node/613002
  15. */
  16. document.namespaces;
  17. (function($) {
  18. Drupal.settings.openlayers = {};
  19. Drupal.settings.openlayers.maps = {};
  20. /**
  21. * Minimal OpenLayers map bootstrap.
  22. * All additional operations occur in additional Drupal behaviors.
  23. */
  24. Drupal.behaviors.openlayers = {
  25. 'attach': function(context, settings) {
  26. if (typeof(Drupal.settings.openlayers) === 'object' &&
  27. Drupal.settings.openlayers.maps &&
  28. !$(context).data('openlayers')) {
  29. $('.openlayers-map:not(.openlayers-processed)').each(function() {
  30. // By setting the stop_render variable to TRUE, this will
  31. // halt the render process. If set, one could remove this setting
  32. // then call Drupal.attachBehaviors again to get it started
  33. var map_id = $(this).attr('id');
  34. if (Drupal.settings.openlayers.maps[map_id] && Drupal.settings.openlayers.maps[map_id].stop_render != true) {
  35. var map = Drupal.settings.openlayers.maps[map_id];
  36. $(this).addClass('openlayers-processed');
  37. // Use try..catch for error handling.
  38. try {
  39. // Set OpenLayers language based on document language,
  40. // rather than browser language
  41. OpenLayers.Lang.setCode($('html').attr('lang'));
  42. $(this)
  43. // @TODO: move this into markup in theme function, doing this dynamically is a waste.
  44. .css('width', map.width)
  45. .css('height', map.height);
  46. var options = {};
  47. // This is necessary because the input JSON cannot contain objects
  48. options.projection = new OpenLayers.Projection('EPSG:' + map.projection);
  49. options.displayProjection = new OpenLayers.Projection('EPSG:' + map.displayProjection);
  50. // TODO: work around this scary code
  51. if (map.projection === '900913') {
  52. options.maxExtent = new OpenLayers.Bounds(
  53. -20037508.34, -20037508.34, 20037508.34, 20037508.34);
  54. options.units = "m";
  55. }
  56. if (map.projection === '4326') {
  57. options.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
  58. }
  59. options.maxResolution = 'auto'; // 1.40625;
  60. options.controls = [];
  61. // Change image, CSS, and proxy paths if specified
  62. if (map.image_path) {
  63. OpenLayers.ImgPath = Drupal.openlayers.relatePath(map.image_path,
  64. Drupal.settings.basePath);
  65. }
  66. if (map.css_path) {
  67. options.theme = Drupal.openlayers.relatePath(map.css_path,
  68. Drupal.settings.basePath);
  69. }
  70. if (map.proxy_host) {
  71. OpenLayers.ProxyHost = Drupal.openlayers.relatePath(map.proxy_host,
  72. Drupal.settings.basePath);
  73. }
  74. // Initialize openlayers map
  75. var openlayers = new OpenLayers.Map(map.id, options);
  76. // Run the layer addition first
  77. Drupal.openlayers.addLayers(map, openlayers);
  78. // Attach data to map DOM object
  79. $(this).data('openlayers', {'map': map, 'openlayers': openlayers});
  80. // Finally, attach behaviors
  81. Drupal.attachBehaviors(this);
  82. if ($.browser.msie) {
  83. Drupal.openlayers.redrawVectors();
  84. }
  85. }
  86. catch (e) {
  87. var errorMessage = e.name + ': ' + e.message;
  88. if (typeof console != 'undefined') {
  89. console.log(errorMessage);
  90. }
  91. else {
  92. $(this).text('Error during map rendering: ' + errorMessage);
  93. }
  94. }
  95. }
  96. });
  97. }
  98. }
  99. };
  100. /**
  101. * Collection of helper methods.
  102. */
  103. Drupal.openlayers = {
  104. // Determine path based on format.
  105. 'relatePath': function(path, basePath) {
  106. // Check for a full URL or an absolute path.
  107. if (path.indexOf('://') >= 0 || path.indexOf('/') == 0) {
  108. return path;
  109. }
  110. else {
  111. return basePath + path;
  112. }
  113. },
  114. /*
  115. * Redraw Vectors.
  116. * This is necessary because various version of IE cannot draw vectors on
  117. * $(document).ready()
  118. */
  119. 'redrawVectors': function() {
  120. $(window).load(
  121. function() {
  122. var map;
  123. for (map in Drupal.settings.openlayers.maps) {
  124. $.each($('#' + map).data('openlayers')
  125. .openlayers.getLayersByClass('OpenLayers.Layer.Vector'),
  126. function(i, layer) {
  127. layer.redraw();
  128. }
  129. );
  130. }
  131. }
  132. );
  133. },
  134. /**
  135. * Add layers to the map
  136. *
  137. * @param map Drupal settings object for the map.
  138. * @param openlayers OpenLayers Map Object.
  139. */
  140. 'addLayers': function(map, openlayers) {
  141. var sorted = [];
  142. for (var name in map.layers) {
  143. sorted.push({'name': name, 'weight': map.layers[name].weight, 'isBaseLayer': map.layers[name].isBaseLayer });
  144. }
  145. sorted.sort(function(a, b) {
  146. var x = parseInt(a.weight, 10), y = parseInt(b.weight, 10);
  147. return ((a.isBaseLayer && x < y) ? -1 : ((b.isBaseLayer || x > y) ? 1 : 0));
  148. });
  149. for (var i = 0; i < sorted.length; ++i) {
  150. var layer,
  151. name = sorted[i].name,
  152. options = map.layers[name];
  153. // Add reference to our layer ID
  154. options.drupalID = name;
  155. // Ensure that the layer handler is available
  156. if (options.layer_handler !== undefined &&
  157. Drupal.openlayers.layer[options.layer_handler] !== undefined) {
  158. var layer = Drupal.openlayers.layer[options.layer_handler](map.layers[name].title, map, options);
  159. layer.visibility = !!(!map.layer_activated || map.layer_activated[name]);
  160. if (layer.isBaseLayer === false) {
  161. layer.displayInLayerSwitcher = (!map.layer_switcher || map.layer_switcher[name]);
  162. }
  163. if (map.center.wrapdateline === '1') {
  164. // TODO: move into layer specific settings
  165. layer.wrapDateLine = true;
  166. }
  167. openlayers.addLayer(layer);
  168. }
  169. }
  170. openlayers.setBaseLayer(openlayers.getLayersBy('drupalID', map.default_layer)[0]);
  171. // Set the restricted extent if wanted.
  172. // Prevents the map from being panned outside of a specfic bounding box.
  173. if (typeof map.center.restrict !== 'undefined' && map.center.restrict.restrictextent) {
  174. openlayers.restrictedExtent = OpenLayers.Bounds.fromString(
  175. map.center.restrict.restrictedExtent);
  176. }
  177. // Zoom & center
  178. if (map.center.initial) {
  179. var center = OpenLayers.LonLat.fromString(map.center.initial.centerpoint).transform(
  180. new OpenLayers.Projection('EPSG:4326'),
  181. new OpenLayers.Projection('EPSG:' + map.projection));
  182. var zoom = parseInt(map.center.initial.zoom, 10);
  183. openlayers.setCenter(center, zoom, false, false);
  184. }
  185. },
  186. /**
  187. * Abstraction of OpenLayer's feature adding syntax to work with Drupal output.
  188. * Ideally this should be rolled into the PHP code, because we don't want to manually
  189. * parse WKT
  190. */
  191. 'addFeatures': function(map, layer, features) {
  192. var newFeatures = [];
  193. // Go through features
  194. for (var key in features) {
  195. var feature = features[key];
  196. var newFeatureObject = this.objectFromFeature(feature);
  197. // If we have successfully extracted geometry add additional
  198. // properties and queue it for addition to the layer
  199. if (newFeatureObject) {
  200. var newFeatureSet = [];
  201. // Check to see if it is a new feature, or an array of new features.
  202. if (typeof(newFeatureObject[0]) === 'undefined') {
  203. newFeatureSet[0] = newFeatureObject;
  204. }
  205. else {
  206. newFeatureSet = newFeatureObject;
  207. }
  208. // Go through new features
  209. for (var i in newFeatureSet) {
  210. var newFeature = newFeatureSet[i];
  211. // Transform the geometry if the 'projection' property is different from the map projection
  212. if (feature.projection) {
  213. if (feature.projection !== map.projection) {
  214. var featureProjection = new OpenLayers.Projection('EPSG:' + feature.projection);
  215. var mapProjection = new OpenLayers.Projection('EPSG:' + map.projection);
  216. newFeature.geometry.transform(featureProjection, mapProjection);
  217. }
  218. }
  219. // Add attribute data
  220. if (feature.attributes) {
  221. // Attributes belong to features, not single component geometries
  222. // of them. But we're creating a geometry for each component for
  223. // better performance and clustering support. Let's call these
  224. // "pseudofeatures".
  225. //
  226. // In order to identify the real feature each geometry belongs to
  227. // we then add a 'fid' parameter to the "pseudofeature".
  228. // NOTE: 'drupalFID' is only unique within a single layer.
  229. newFeature.attributes = feature.attributes;
  230. newFeature.data = feature.attributes;
  231. newFeature.drupalFID = key;
  232. }
  233. // Add style information
  234. if (feature.style) {
  235. newFeature.style = jQuery.extend({},
  236. OpenLayers.Feature.Vector.style['default'],
  237. feature.style);
  238. }
  239. // Push new features
  240. newFeatures.push(newFeature);
  241. }
  242. }
  243. }
  244. // Add new features if there are any
  245. if (newFeatures.length !== 0) {
  246. layer.addFeatures(newFeatures);
  247. }
  248. },
  249. 'getStyleMap': function(map, layername) {
  250. if (map.styles) {
  251. var stylesAdded = {};
  252. // Grab and map base styles.
  253. for (var style in map.styles) {
  254. stylesAdded[style] = new OpenLayers.Style(map.styles[style]);
  255. }
  256. // Implement layer-specific styles. First default, then select.
  257. if (map.layer_styles !== undefined && map.layer_styles[layername]) {
  258. var style = map.layer_styles[layername];
  259. stylesAdded['default'] = new OpenLayers.Style(map.styles[style]);
  260. }
  261. if (map.layer_styles_select !== undefined && map.layer_styles_select[layername]) {
  262. var style = map.layer_styles_select[layername];
  263. stylesAdded['select'] = new OpenLayers.Style(map.styles[style]);
  264. }
  265. return new OpenLayers.StyleMap(stylesAdded);
  266. }
  267. else {
  268. return new OpenLayers.StyleMap({
  269. 'default': new OpenLayers.Style({
  270. pointRadius: 5,
  271. fillColor: '#ffcc66',
  272. strokeColor: '#ff9933',
  273. strokeWidth: 4,
  274. fillOpacity: 0.5
  275. }),
  276. 'select': new OpenLayers.Style({
  277. fillColor: '#66ccff',
  278. strokeColor: '#3399ff'
  279. })
  280. });
  281. }
  282. },
  283. 'objectFromFeature': function(feature) {
  284. var wktFormat = new OpenLayers.Format.WKT();
  285. // Extract geometry either from wkt property or lon/lat properties
  286. if (feature.wkt) {
  287. return wktFormat.read(feature.wkt);
  288. }
  289. else if (feature.lon) {
  290. return wktFormat.read('POINT(' + feature.lon + ' ' + feature.lat + ')');
  291. }
  292. },
  293. /**
  294. * Add Behavior.
  295. *
  296. * This is a wrapper around adding behaviors for OpenLayers.
  297. * a module does not have to use this, but it helps cut
  298. * down on code.
  299. *
  300. * @param id
  301. * The identifier of the behavior that is attached to
  302. * the map.
  303. * @param attach
  304. * The callback function for the attach part of the
  305. * Drupal behavior.
  306. * @param detach
  307. * The callback function for the detach part of the
  308. * Drupal behavior.
  309. */
  310. 'addBehavior': function(id, attach, detach) {
  311. // Add as a Drupal behavior. Add a prefix, just to be safe.
  312. Drupal.behaviors['openlayers_auto_' + id] = {
  313. attach: function (context, settings) {
  314. var data = $(context).data('openlayers');
  315. // Ensure that there is a map and that the appropriate
  316. // behavior exists. Need "data &&" to avoid js crash
  317. // when data is empty
  318. var localBehavior = data && data.map.behaviors[id];
  319. // Ensure scope in the attach callback
  320. var that = this;
  321. if (localBehavior) {
  322. $(context).once('openlayers-' + id, function () {
  323. attach.apply(that, [data, data.map.behaviors[id], context, settings]);
  324. });
  325. }
  326. },
  327. // Maybe we need a little more handling here.
  328. detach: detach
  329. };
  330. },
  331. /**
  332. * Add Control.
  333. *
  334. * This is a wrapper around adding controls to maps. It
  335. * is not needed but saves some code.
  336. */
  337. 'addControl': function(openlayers, controlName, options) {
  338. var control = new OpenLayers.Control[controlName](options);
  339. openlayers.addControl(control);
  340. control.activate();
  341. return control;
  342. }
  343. };
  344. Drupal.openlayers.layer = {};
  345. })(jQuery);