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