leaflet.drupal.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. (function ($) {
  2. Drupal.behaviors.leaflet = {
  3. attach:function (context, settings) {
  4. $(settings.leaflet).each(function () {
  5. // skip to the next iteration if the map already exists
  6. var container = L.DomUtil.get(this.mapId);
  7. if (!container || container._leaflet_id) {
  8. return;
  9. }
  10. // load a settings object with all of our map settings
  11. var settings = {};
  12. for (var setting in this.map.settings) {
  13. settings[setting] = this.map.settings[setting];
  14. }
  15. // instantiate our new map
  16. var lMap = new L.Map(this.mapId, settings);
  17. lMap.bounds = [];
  18. // add map layers
  19. var layers = {}, overlays = {};
  20. var i = 0;
  21. for (var key in this.map.layers) {
  22. var layer = this.map.layers[key];
  23. var map_layer = Drupal.leaflet.create_layer(layer, key);
  24. //layers[key] = map_layer;
  25. // Distinguish between "base layers" and "overlays".
  26. // Fall back to "base" in case "layer_type" has not been defined in
  27. // hook_leaflet_map_info()
  28. layer.layer_type = (typeof layer.layer_type === 'undefined') ? 'base' : layer.layer_type;
  29. // As stated in http://leafletjs.com/examples/layers-control,
  30. // when using multiple base layers, only one of them should be added
  31. // to the map at instantiation, but all of them should be present in
  32. // the base layers object when creating the layers control.
  33. // See statement L.control.layers(layers, overlays) much further below.
  34. switch (layer.layer_type) {
  35. case 'overlay':
  36. //lMap.addLayer(map_layer);
  37. overlays[key] = map_layer;
  38. break;
  39. default:
  40. if (i === 0 /*|| !this.map.settings.layerControl*/) {
  41. lMap.addLayer(map_layer);
  42. i++;
  43. }
  44. layers[key] = map_layer;
  45. break;
  46. }
  47. i++;
  48. }
  49. var switchEnable = false;
  50. for (var key in layers) {
  51. if (layers[key].options.switchLayer) {
  52. layers[key].setSwitchLayer(layers[layers[key].options.switchLayer]);
  53. switchEnable = true;
  54. }
  55. }
  56. if (switchEnable) {
  57. switchManager = new SwitchLayerManager(lMap, {baseLayers: layers});
  58. }
  59. // keep an instance of leaflet layers
  60. this.map.lLayers = layers;
  61. // keep an instance of map_id
  62. this.map.map_id = this.mapId;
  63. // add features
  64. for (i = 0; i < this.features.length; i++) {
  65. var feature = this.features[i];
  66. var lFeature;
  67. // dealing with a layer group
  68. if (feature.group) {
  69. var lGroup = new L.LayerGroup();
  70. for (var groupKey in feature.features) {
  71. var groupFeature = feature.features[groupKey];
  72. lFeature = leaflet_create_feature(groupFeature, lMap);
  73. if (groupFeature.popup) {
  74. lFeature.bindPopup(groupFeature.popup);
  75. }
  76. lGroup.addLayer(lFeature);
  77. // Allow others to do something with the feature within a group.
  78. $(document).trigger('leaflet.feature', [lFeature, feature]);
  79. }
  80. // add the group to the layer switcher
  81. overlays[feature.label] = lGroup;
  82. lMap.addLayer(lGroup);
  83. }
  84. else {
  85. lFeature = leaflet_create_feature(feature, lMap);
  86. lMap.addLayer(lFeature);
  87. if (feature.popup) {
  88. lFeature.bindPopup(feature.popup);
  89. }
  90. // Allow others to do something with the feature.
  91. $(document).trigger('leaflet.feature', [lFeature, feature]);
  92. }
  93. }
  94. // add layer switcher
  95. if (this.map.settings.layerControl) {
  96. L.control.layers(layers, overlays).addTo(lMap);
  97. }
  98. // center the map
  99. var zoom = this.map.settings.zoom ? this.map.settings.zoom : this.map.settings.zoomDefault;
  100. if (this.map.center && (this.map.center.force || this.features.length === 0)) {
  101. lMap.setView(new L.LatLng(this.map.center.lat, this.map.center.lon), zoom);
  102. }
  103. else if (this.features.length > 0) {
  104. Drupal.leaflet.fitbounds(lMap);
  105. if (this.map.settings.zoom) { // or: if (zoom) ?
  106. lMap.setZoom(zoom);
  107. }
  108. }
  109. // add attribution
  110. if (this.map.settings.attributionControl && this.map.attribution) {
  111. lMap.attributionControl.setPrefix(this.map.attribution.prefix);
  112. lMap.attributionControl.addAttribution(this.map.attribution.text);
  113. }
  114. // add the leaflet map to our settings object to make it accessible
  115. this.lMap = lMap;
  116. // allow other modules to get access to the map object using jQuery's trigger method
  117. $(document).trigger('leaflet.map', [this.map, lMap]);
  118. // Destroy features so that an AJAX reload does not get parts of the old set.
  119. // Required when the View has "Use AJAX" set to Yes.
  120. this.features = null;
  121. });
  122. function leaflet_create_feature(feature, lMap) {
  123. var lFeature;
  124. switch (feature.type) {
  125. case 'point':
  126. lFeature = Drupal.leaflet.create_point(feature, lMap);
  127. break;
  128. case 'linestring':
  129. lFeature = Drupal.leaflet.create_linestring(feature, lMap);
  130. break;
  131. case 'polygon':
  132. lFeature = Drupal.leaflet.create_polygon(feature, lMap);
  133. break;
  134. case 'multipolyline':
  135. feature.multipolyline = true;
  136. // no break;
  137. case 'multipolygon':
  138. lFeature = Drupal.leaflet.create_multipoly(feature, lMap);
  139. break;
  140. case 'json':
  141. lFeature = Drupal.leaflet.create_json(feature.json, lMap);
  142. break;
  143. case 'popup':
  144. lFeature = Drupal.leaflet.create_popup(feature, lMap);
  145. break;
  146. case 'circle':
  147. lFeature = Drupal.leaflet.create_circle(feature, lMap);
  148. break;
  149. case 'circlemarker':
  150. lFeature = Drupal.leaflet.create_circlemarker(feature, lMap);
  151. break;
  152. case 'rectangle':
  153. lFeature = Drupal.leaflet.create_rectangle(feature, lMap);
  154. break;
  155. }
  156. // assign our given unique ID, useful for associating nodes
  157. if (feature.leaflet_id) {
  158. lFeature._leaflet_id = feature.leaflet_id;
  159. }
  160. var options = {};
  161. if (feature.options) {
  162. for (var option in feature.options) {
  163. options[option] = feature.options[option];
  164. }
  165. lFeature.setStyle(options);
  166. }
  167. return lFeature;
  168. }
  169. }
  170. };
  171. Drupal.leaflet = {
  172. isOldVersion: function () {
  173. return !(parseInt(L.version) >= 1); // version may start with '0' or '.'
  174. },
  175. create_layer: function (layer, key) {
  176. // Use a Zoomswitch Layer extension to enable zoom-switch option.
  177. var map_layer = new L.TileLayerZoomSwitch(layer.urlTemplate);
  178. map_layer._leaflet_id = key;
  179. if (layer.options) {
  180. for (var option in layer.options) {
  181. map_layer.options[option] = layer.options[option];
  182. }
  183. }
  184. // layers served from TileStream need this correction in the y coordinates
  185. // TODO: Need to explore this more and find a more elegant solution
  186. if (layer.type == 'tilestream') {
  187. map_layer.getTileUrl = function (tilePoint) {
  188. this._adjustTilePoint(tilePoint);
  189. var zoom = this._getZoomForUrl();
  190. return L.Util.template(this._url, L.Util.extend({
  191. s: this._getSubdomain(tilePoint),
  192. z: zoom,
  193. x: tilePoint.x,
  194. y: Math.pow(2, zoom) - tilePoint.y - 1
  195. }, this.options));
  196. };
  197. }
  198. return map_layer;
  199. },
  200. create_circle: function(circle, lMap) {
  201. var latLng = new L.LatLng(circle.lat, circle.lon);
  202. latLng = latLng.wrap();
  203. lMap.bounds.push(latLng);
  204. if (circle.radius) {
  205. // @deprecated
  206. return L.circle(latLng, circle.radius, circle.options);
  207. }
  208. return new L.Circle(latLng, circle.options);
  209. },
  210. create_circlemarker: function(circle, lMap) {
  211. var latLng = new L.LatLng(circle.lat, circle.lon);
  212. latLng = latLng.wrap();
  213. lMap.bounds.push(latLng);
  214. return new L.CircleMarker(latLng, circle.options);
  215. },
  216. create_rectangle: function(box, lMap) {
  217. var bounds = box.bounds,
  218. southWest = new L.LatLng(bounds.s, bounds.w),
  219. northEast = new L.LatLng(bounds.n, bounds.e),
  220. latLng = new L.LatLngBounds(southWest, northEast);
  221. lMap.bounds.push(latLng);
  222. return new L.Rectangle(latLng, box.settings);
  223. },
  224. create_point: function(marker, lMap) {
  225. var latLng = new L.LatLng(marker.lat, marker.lon);
  226. latLng = latLng.wrap();
  227. lMap.bounds.push(latLng);
  228. var lMarker;
  229. if (marker.html) {
  230. if (marker.html_class) {
  231. var icon = new L.DivIcon({html: marker.html, className: marker.html_class});
  232. }
  233. else {
  234. var icon = new L.DivIcon({html: marker.html});
  235. }
  236. // override applicable marker defaults
  237. if (marker.icon.iconSize) {
  238. icon.options.iconSize = new L.Point(parseInt(marker.icon.iconSize.x, 10), parseInt(marker.icon.iconSize.y, 10));
  239. }
  240. if (marker.icon.iconAnchor) {
  241. icon.options.iconAnchor = new L.Point(parseFloat(marker.icon.iconAnchor.x), parseFloat(marker.icon.iconAnchor.y));
  242. }
  243. lMarker = new L.Marker(latLng, {icon:icon});
  244. }
  245. else if (marker.icon) {
  246. var icon = new L.Icon({iconUrl: marker.icon.iconUrl});
  247. // override applicable marker defaults
  248. if (marker.icon.iconSize) {
  249. icon.options.iconSize = new L.Point(parseInt(marker.icon.iconSize.x, 10), parseInt(marker.icon.iconSize.y, 10));
  250. }
  251. if (marker.icon.iconAnchor) {
  252. icon.options.iconAnchor = new L.Point(parseFloat(marker.icon.iconAnchor.x), parseFloat(marker.icon.iconAnchor.y));
  253. }
  254. if (marker.icon.popupAnchor) {
  255. icon.options.popupAnchor = new L.Point(parseFloat(marker.icon.popupAnchor.x), parseFloat(marker.icon.popupAnchor.y));
  256. }
  257. if (marker.icon.shadowUrl !== undefined) {
  258. icon.options.shadowUrl = marker.icon.shadowUrl;
  259. }
  260. if (marker.icon.shadowSize) {
  261. icon.options.shadowSize = new L.Point(parseInt(marker.icon.shadowSize.x, 10), parseInt(marker.icon.shadowSize.y, 10));
  262. }
  263. if (marker.icon.shadowAnchor) {
  264. icon.options.shadowAnchor = new L.Point(parseInt(marker.icon.shadowAnchor.x, 10), parseInt(marker.icon.shadowAnchor.y, 10));
  265. }
  266. if (marker.icon.zIndexOffset) {
  267. icon.options.zIndexOffset = marker.icon.zIndexOffset;
  268. }
  269. if (marker.icon.className) {
  270. icon.options.className = marker.icon.className;
  271. }
  272. var options = {icon:icon};
  273. if (marker.zIndexOffset) {
  274. options.zIndexOffset = marker.zIndexOffset;
  275. }
  276. lMarker = new L.Marker(latLng, options);
  277. }
  278. else {
  279. lMarker = new L.Marker(latLng);
  280. }
  281. if (marker.label) {
  282. lMarker.options.title = marker.label;
  283. }
  284. return lMarker;
  285. },
  286. create_linestring: function(polyline, lMap) {
  287. var latlngs = [];
  288. for (var i = 0; i < polyline.points.length; i++) {
  289. var latlng = new L.LatLng(polyline.points[i].lat, polyline.points[i].lon);
  290. latlng = latlng.wrap();
  291. latlngs.push(latlng);
  292. lMap.bounds.push(latlng);
  293. }
  294. return new L.Polyline(latlngs);
  295. },
  296. create_polygon: function(polygon, lMap) {
  297. var latlngs = [];
  298. for (var i = 0; i < polygon.points.length; i++) {
  299. var latlng = new L.LatLng(polygon.points[i].lat, polygon.points[i].lon);
  300. latlng = latlng.wrap();
  301. latlngs.push(latlng);
  302. lMap.bounds.push(latlng);
  303. }
  304. return new L.Polygon(latlngs);
  305. },
  306. create_multipoly: function(multipoly, lMap) {
  307. var polygons = [];
  308. for (var x = 0; x < multipoly.component.length; x++) {
  309. var latlngs = [];
  310. var polygon = multipoly.component[x];
  311. for (var i = 0; i < polygon.points.length; i++) {
  312. var latlng = new L.LatLng(polygon.points[i].lat, polygon.points[i].lon);
  313. latlng = latlng.wrap();
  314. latlngs.push(latlng);
  315. lMap.bounds.push(latlng);
  316. }
  317. polygons.push(latlngs);
  318. }
  319. if (this.isOldVersion()) {
  320. return multipoly.multipolyline ? new L.MultiPolyline(polygons) : new L.MultiPolygon(polygons);
  321. }
  322. return multipoly.multipolyline ? new L.Polyline(polygons): new L.Polygon(polygons);
  323. },
  324. create_json:function(json, lMap) {
  325. lJSON = new L.GeoJSON(json, {
  326. onEachFeature:function (feature, layer) {
  327. var has_properties = (typeof feature.properties != 'undefined');
  328. // bind popups
  329. if (has_properties && typeof feature.properties.popup != 'undefined') {
  330. layer.bindPopup(feature.properties.popup);
  331. }
  332. for (var layer_id in layer._layers) {
  333. for (var i in layer._layers[layer_id]._latlngs) {
  334. lMap.bounds.push(layer._layers[layer_id]._latlngs[i]);
  335. }
  336. }
  337. if (has_properties && typeof feature.properties.style != 'undefined') {
  338. layer.setStyle(feature.properties.style);
  339. }
  340. if (has_properties && typeof feature.properties.leaflet_id != 'undefined') {
  341. layer._leaflet_id = feature.properties.leaflet_id;
  342. }
  343. }
  344. });
  345. return lJSON;
  346. },
  347. create_popup: function(popup) {
  348. var latLng = new L.LatLng(popup.lat, popup.lon);
  349. this.bounds.push(latLng);
  350. var lPopup = new L.Popup();
  351. lPopup.setLatLng(latLng);
  352. if (popup.content) {
  353. lPopup.setContent(popup.content);
  354. }
  355. return lPopup;
  356. },
  357. fitbounds:function (lMap) {
  358. if (lMap.bounds.length > 0) {
  359. lMap.fitBounds(new L.LatLngBounds(lMap.bounds));
  360. }
  361. }
  362. };
  363. // Zoomswitch method cribbed liberally from:
  364. // http://www.makina-corpus.org/blog/leaflet-zoom-switcher
  365. L.TileLayerZoomSwitch = L.TileLayer.extend({
  366. includes: L.Mixin.Events,
  367. options: {
  368. // switchZoomUnder: when zoom < switchZoomUnder, then switch to switchLayer
  369. switchZoomUnder: -1,
  370. // switchZoomAbove: when zoom >= switchZoomAbove, then switch to switchLayer
  371. switchZoomAbove: -1,
  372. switchLayer: null
  373. },
  374. setSwitchLayer: function (layer) {
  375. this.options.switchLayer = layer;
  376. },
  377. getSwitchZoomUnder: function () {
  378. return this.options.switchZoomUnder;
  379. },
  380. getSwitchZoomAbove: function () {
  381. return this.options.switchZoomAbove;
  382. },
  383. getSwitchLayer: function () {
  384. return this.options.switchLayer;
  385. }
  386. });
  387. L.tileLayerZoomSwitch = function (url, options) {
  388. return new L.TileLayerZoomSwitch(url, options);
  389. };
  390. /*
  391. * SwitchLayerManager is a custom class for managing base layer automatic switching according to the current zoom level
  392. */
  393. SwitchLayerManager = L.Class.extend({
  394. _map: null,
  395. options: {
  396. baseLayers: null
  397. },
  398. initialize: function (map, options) {
  399. this._map = map;
  400. L.Util.setOptions(this, options);
  401. this._map.on({
  402. 'zoomend': this._update
  403. }, this)
  404. },
  405. _update: function (e) {
  406. var zoom = this._map.getZoom();
  407. for (var i in this.options.baseLayers) {
  408. var curBL = this.options.baseLayers[i];
  409. var zoomUnder = curBL.getSwitchZoomUnder();
  410. var zoomAbove = curBL.getSwitchZoomAbove();
  411. var switchLayer = curBL.getSwitchLayer();
  412. // If layer got a switchlayer, and if layer actually displayed
  413. if (switchLayer && curBL._map != null) {
  414. //if (switchLayer) {
  415. if(zoomUnder != -1 && zoom < zoomUnder) {
  416. this._map.removeLayer(curBL);
  417. this._map.addLayer(switchLayer, false);
  418. }
  419. if(zoomAbove != -1 && zoom >= zoomAbove) {
  420. this._map.removeLayer(curBL);
  421. this._map.addLayer(switchLayer, false);
  422. }
  423. }
  424. }
  425. }
  426. });
  427. })(jQuery);