rlayer-src.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /*
  2. RaphaelLayer, a JavaScript library for overlaying Raphael objects onto Leaflet interactive maps. http://dynmeth.github.com/RaphaelLayer
  3. (c) 2012-2013, David Howell, Dynamic Methods Pty Ltd
  4. Version 0.1.3
  5. */
  6. (function() {
  7. var R, originalR;
  8. if (typeof exports != 'undefined') {
  9. R = exports;
  10. } else {
  11. R = {};
  12. originalR = window.R;
  13. R.noConflict = function() {
  14. window.R = originalR;
  15. return R;
  16. };
  17. window.R = R;
  18. }
  19. R.version = '0.1.3';
  20. R.Layer = L.Class.extend({
  21. includes: L.Mixin.Events,
  22. initialize: function(options) {
  23. },
  24. onAdd: function (map) {
  25. this._map = map;
  26. this._map._initRaphaelRoot();
  27. this._paper = this._map._paper;
  28. this._set = this._paper.set();
  29. map.on('viewreset', this.projectLatLngs, this);
  30. this.projectLatLngs();
  31. },
  32. onRemove: function(map) {
  33. map.off('viewreset', this.projectLatLngs, this);
  34. this._map = null;
  35. this._set.forEach(function(item) {
  36. item.remove();
  37. }, this);
  38. this._set.clear();
  39. },
  40. projectLatLngs: function() {
  41. },
  42. animate: function(attr, ms, easing, callback) {
  43. this._set.animate(attr, ms, easing, callback);
  44. return this;
  45. },
  46. hover: function(f_in, f_out, icontext, ocontext) {
  47. this._set.hover(f_in, f_out, icontext, ocontext);
  48. return this;
  49. },
  50. attr: function(name, value) {
  51. this._set.attr(name, value);
  52. return this;
  53. }
  54. });
  55. L.Map.include({
  56. _initRaphaelRoot: function () {
  57. if (!this._raphaelRoot) {
  58. this._raphaelRoot = this._panes.overlayPane;
  59. this._paper = Raphael(this._raphaelRoot);
  60. this.on('moveend', this._updateRaphaelViewport);
  61. this._updateRaphaelViewport();
  62. }
  63. },
  64. _updateRaphaelViewport: function () {
  65. var p = 0.02,
  66. size = this.getSize(),
  67. panePos = L.DomUtil.getPosition(this._mapPane),
  68. min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)),
  69. max = min.add(size.multiplyBy(1 + p*2)),
  70. width = max.x - min.x,
  71. height = max.y - min.y,
  72. root = this._raphaelRoot,
  73. pane = this._panes.overlayPane;
  74. this._paper.setSize(width, height);
  75. L.DomUtil.setPosition(root, min);
  76. root.setAttribute('width', width);
  77. root.setAttribute('height', height);
  78. this._paper.setViewBox(min.x, min.y, width, height, false);
  79. }
  80. });
  81. R.Marker = R.Layer.extend({
  82. initialize: function(latlng, pathString, attr, options) {
  83. R.Layer.prototype.initialize.call(this, options);
  84. this._latlng = latlng;
  85. this._pathString = (typeof pathString == 'string' ? pathString : 'M16,3.5c-4.142,0-7.5,3.358-7.5,7.5c0,4.143,7.5,18.121,7.5,18.121S23.5,15.143,23.5,11C23.5,6.858,20.143,3.5,16,3.5z M16,14.584c-1.979,0-3.584-1.604-3.584-3.584S14.021,7.416,16,7.416S19.584,9.021,19.584,11S17.979,14.584,16,14.584z');
  86. this._attr = (typeof pathString == 'object' ? pathString : (attr ? attr : {'fill': '#000'}));
  87. },
  88. projectLatLngs: function() {
  89. if (this._path) this._path.remove();
  90. var p = this._map.latLngToLayerPoint(this._latlng);
  91. var r = Raphael.pathBBox(this._pathString);
  92. this._path = this._paper.path(this._pathString)
  93. .attr(this._attr)
  94. .translate(p.x - 1.05*r.width, p.y - 1.15*r.height)
  95. .toFront();
  96. this._set.push(this._path);
  97. }
  98. });
  99. R.Pulse = R.Layer.extend({
  100. initialize: function(latlng, radius, attr, pulseAttr, options) {
  101. R.Layer.prototype.initialize.call(this, options);
  102. this._latlng = latlng;
  103. this._radius = (typeof radius == 'number' ? radius : 6);
  104. this._attr = (typeof radius == 'object' ? radius : (typeof attr == 'object' ? attr : {'fill': '#30a3ec', 'stroke': '#30a3ec'}));
  105. this._pulseAttr = (typeof radius == 'object' ? attr : typeof pulseAttr == 'object' ? pulseAttr : {
  106. 'stroke-width': 3,
  107. 'stroke': this._attr.stroke
  108. });
  109. this._repeat = 3;
  110. },
  111. onRemove: function (map) {
  112. R.Layer.prototype.onRemove.call(this, map);
  113. if(this._marker) this._marker.remove();
  114. if(this._pulse) this._pulse.remove();
  115. },
  116. projectLatLngs: function() {
  117. if(this._marker) this._marker.remove();
  118. if(this._pulse) this._pulse.remove();
  119. var p = this._map.latLngToLayerPoint(this._latlng);
  120. this._marker = this._paper.circle(p.x, p.y, this._radius).attr(this._attr);
  121. this._pulse = this._paper.circle(p.x, p.y, this._radius).attr(this._pulseAttr);
  122. var anim = Raphael.animation({
  123. '0%': {transform: 's0.3', opacity: 1.0},
  124. '100%': {transform: 's3.0', opacity: 0.0, easing: '<'}
  125. }, 1000);
  126. this._pulse.animate(anim.repeat(this._repeat));
  127. }
  128. });
  129. R.Polyline = R.Layer.extend({
  130. initialize: function(latlngs, attr, options) {
  131. R.Layer.prototype.initialize.call(this, options);
  132. this._latlngs = latlngs;
  133. this._attr = attr || {'fill': '#000', 'stroke': '#000'};
  134. },
  135. projectLatLngs: function() {
  136. this._set.clear();
  137. if (this._path) this._path.remove();
  138. this._path = this._paper.path(this.getPathString())
  139. .attr(this._attr)
  140. .toBack();
  141. this._set.push(this._path);
  142. },
  143. getPathString: function() {
  144. for(var i=0, len=this._latlngs.length, str=''; i<len; i++) {
  145. var p = this._map.latLngToLayerPoint(this._latlngs[i]);
  146. str += (i ? 'L' : 'M') + p.x + ' ' + p.y;
  147. }
  148. return str;
  149. }
  150. });
  151. R.Polygon = R.Layer.extend({
  152. initialize: function(latlngs, attr, options) {
  153. R.Layer.prototype.initialize.call(this, options);
  154. if(latlngs.length == 1) {
  155. if(latlngs[0] instanceof Array) {
  156. latlngs = latlngs[0];
  157. }
  158. }
  159. this._latlngs = latlngs;
  160. this._attr = attr || {'fill': 'rgba(255, 0, 0, 0.5)', 'stroke': '#f00', 'stroke-width': 2};
  161. },
  162. projectLatLngs: function() {
  163. if (this._path) this._path.remove();
  164. this._path = this._paper.path(this.getPathString())
  165. .attr(this._attr)
  166. .toBack();
  167. this._set.push(this._path);
  168. },
  169. getPathString: function() {
  170. for(var i=0, len=this._latlngs.length, str=''; i<len; i++) {
  171. var p = this._map.latLngToLayerPoint(this._latlngs[i]);
  172. str += (i ? 'L' : 'M') + p.x + ' ' + p.y;
  173. }
  174. str += 'Z';
  175. return str;
  176. }
  177. });
  178. R.PolygonGlow = R.Layer.extend({
  179. initialize: function(latlngs, attr, options) {
  180. R.Layer.prototype.initialize.call(this, options);
  181. this._latlngs = latlngs;
  182. this._attr = attr || {'fill': 'rgba(255, 0, 0, 1)', 'stroke': '#f00', 'stroke-width': 3};
  183. },
  184. onRemove: function(map) {
  185. R.Layer.prototype.onRemove.call(this, map);
  186. if(this._path) this._path.remove();
  187. },
  188. projectLatLngs: function() {
  189. if (this._path) this._path.remove();
  190. this._path = this._paper.path(this.getPathString())
  191. .attr(this._attr)
  192. .toBack();
  193. var p = this._path;
  194. var fadeIn = function() {
  195. p.animate({
  196. 'fill-opacity': 0.25
  197. }, 1000, '<', fadeOut);
  198. };
  199. var fadeOut = function() {
  200. p.animate({
  201. 'fill-opacity': 1
  202. }, 1000, '<', fadeIn);
  203. };
  204. fadeOut();
  205. },
  206. getPathString: function() {
  207. for(var i=0, len=this._latlngs.length, str=''; i<len; i++) {
  208. var p = this._map.latLngToLayerPoint(this._latlngs[i]);
  209. str += (i ? 'L' : 'M') + p.x + ' ' + p.y;
  210. }
  211. str += 'Z';
  212. return str;
  213. }
  214. });
  215. R.Bezier = R.Layer.extend({
  216. initialize: function(latlngs, attr, options) {
  217. R.Layer.prototype.initialize.call(this, options);
  218. this._latlngs = latlngs;
  219. this._attr = attr;
  220. },
  221. projectLatLngs: function() {
  222. if(this._path) this._path.remove();
  223. var start = this._map.latLngToLayerPoint(this._latlngs[0]),
  224. end = this._map.latLngToLayerPoint(this._latlngs[1]),
  225. cp = this.getControlPoint(start, end);
  226. this._path = this._paper.path('M' + start.x + ' ' + start.y + 'Q' + cp.x + ' ' + cp.y + ' ' + end.x + ' ' + end.y)
  227. .attr(this._attr)
  228. .toBack();
  229. this._set.push(this._path);
  230. },
  231. getControlPoint: function(start, end) {
  232. var cp = { x: 0, y: 0 };
  233. cp.x = start.x + (end.x - [start.x]) / 2;
  234. cp.y = start.y + (end.y - [start.y]) / 2;
  235. var amp = 0;
  236. if (this.closeTo(start.x, end.x) && !this.closeTo(start.y, end.y)) {
  237. amp = (start.x - end.x) * 1 + 15 * (start.x >= end.x ? 1 : -1);
  238. cp.x = Math.max(start.x, end.x) + amp;
  239. } else {
  240. amp = (end.y - start.y) * 1.5 + 15 * (start.y < end.y ? 1 : -1);
  241. cp.y = Math.min(start.y, end.y) + amp;
  242. }
  243. return cp;
  244. },
  245. closeTo: function(a, b) {
  246. var t = 15;
  247. return (a - b > -t && a - b < t);
  248. }
  249. });
  250. R.BezierAnim = R.Layer.extend({
  251. initialize: function(latlngs, attr, cb, options) {
  252. R.Layer.prototype.initialize.call(this, options);
  253. this._latlngs = latlngs;
  254. this._attr = attr;
  255. this._cb = cb;
  256. },
  257. onRemove: function (map) {
  258. R.Layer.prototype.onRemove.call(this, map);
  259. if(this._path) this._path.remove();
  260. if(this._sub) this._sub.remove();
  261. },
  262. projectLatLngs: function() {
  263. if(this._path) this._path.remove();
  264. if(this._sub) this._sub.remove();
  265. var self = this,
  266. start = this._map.latLngToLayerPoint(this._latlngs[0]),
  267. end = this._map.latLngToLayerPoint(this._latlngs[1]),
  268. cp = this.getControlPoint(start, end),
  269. pathString="M"+start.x+" "+start.y+" L"+end.x+" "+end.y,
  270. line = this._paper.path(pathString).hide();
  271. this._paper.customAttributes.alongBezier = function(a) {
  272. var r = this.data('reverse');
  273. var len = this.data('pathLength');
  274. return {
  275. path: this.data('bezierPath').getSubpath(r ? (1-a)*len : 0, r ? len : a*len)
  276. };
  277. };
  278. var sub = this._sub = this._paper.path()
  279. .data('bezierPath', line)
  280. .data('pathLength', line.getTotalLength())
  281. .data('reverse', false)
  282. .attr({
  283. 'stroke': '#BE1E2D',
  284. 'alongBezier': 0,
  285. 'stroke-width': 2
  286. });
  287. sub.stop().animate({
  288. alongBezier: 1
  289. }, 2000, function() {
  290. //self._cb();
  291. sub.data('reverse', true);
  292. // sub.stop().animate({
  293. // 'alongBezier': 0
  294. // }, 500, function() { sub.remove(); });
  295. });
  296. },
  297. getControlPoint: function(start, end) {
  298. var cp = { x: 0, y: 0 };
  299. cp.x = start.x + (end.x - [start.x]) / 2;
  300. cp.y = start.y + (end.y - [start.y]) / 2;
  301. var amp = 0;
  302. if (this.closeTo(start.x, end.x) && !this.closeTo(start.y, end.y)) {
  303. amp = (start.x - end.x) * 1 + 15 * (start.x >= end.x ? 1 : -1);
  304. cp.x = Math.max(start.x, end.x) + amp;
  305. } else {
  306. amp = (end.y - start.y) * 1.5 + 15 * (start.y < end.y ? 1 : -1);
  307. cp.y = Math.min(start.y, end.y) + amp;
  308. }
  309. return cp;
  310. },
  311. closeTo: function(a, b) {
  312. var t = 15;
  313. return (a - b > -t && a - b < t);
  314. }
  315. });
  316. R.FeatureGroup = L.FeatureGroup.extend({
  317. initialize: function(layers, options) {
  318. L.FeatureGroup.prototype.initialize.call(this, layers, options);
  319. },
  320. animate: function(attr, ms, easing, callback) {
  321. this.eachLayer(function(layer) {
  322. layer.animate(attr, ms, easing, callback);
  323. });
  324. },
  325. onAdd: function(map) {
  326. L.FeatureGroup.prototype.onAdd.call(this,map);
  327. this._set = this._map._paper.set();
  328. for(i in this._layers) {
  329. this._set.push(this._layers[i]._set);
  330. }
  331. },
  332. hover: function(h_in, h_out, c_in, c_out) {
  333. this.eachLayer(function(layer) {
  334. layer.hover(h_in, h_out, c_in, c_out);
  335. });
  336. return this;
  337. },
  338. attr: function(name, value) {
  339. this.eachLayer(function(layer) {
  340. layer.attr(name, value);
  341. });
  342. return this;
  343. }
  344. });
  345. /*
  346. * Contains L.MultiPolyline and L.MultiPolygon layers.
  347. */
  348. (function () {
  349. function createMulti(Klass) {
  350. return R.FeatureGroup.extend({
  351. initialize: function (latlngs, options) {
  352. this._layers = {};
  353. this._options = options;
  354. this.setLatLngs(latlngs);
  355. },
  356. setLatLngs: function (latlngs) {
  357. var i = 0, len = latlngs.length;
  358. this.eachLayer(function (layer) {
  359. if (i < len) {
  360. layer.setLatLngs(latlngs[i++]);
  361. } else {
  362. this.removeLayer(layer);
  363. }
  364. }, this);
  365. while (i < len) {
  366. this.addLayer(new Klass(latlngs[i++], this._options));
  367. }
  368. return this;
  369. }
  370. });
  371. }
  372. R.MultiPolyline = createMulti(R.Polyline);
  373. R.MultiPolygon = createMulti(R.Polygon);
  374. }());
  375. R.GeoJSON = R.FeatureGroup.extend({
  376. initialize: function (geojson, options) {
  377. L.Util.setOptions(this, options);
  378. this._geojson = geojson;
  379. this._layers = {};
  380. if (geojson) {
  381. this.addGeoJSON(geojson);
  382. }
  383. },
  384. addGeoJSON: function (geojson) {
  385. var features = geojson.features,
  386. i, len;
  387. if (features) {
  388. for (i = 0, len = features.length; i < len; i++) {
  389. this.addGeoJSON(features[i]);
  390. }
  391. return;
  392. }
  393. var isFeature = (geojson.type === 'Feature'),
  394. geometry = isFeature ? geojson.geometry : geojson,
  395. layer = R.GeoJSON.geometryToLayer(geometry, this.options.pointToLayer);
  396. this.fire('featureparse', {
  397. layer: layer,
  398. properties: geojson.properties,
  399. geometryType: geometry.type,
  400. bbox: geojson.bbox,
  401. id: geojson.id,
  402. geometry: geojson.geometry
  403. });
  404. this.addLayer(layer);
  405. }
  406. });
  407. L.Util.extend(R.GeoJSON, {
  408. geometryToLayer: function (geometry, pointToLayer) {
  409. var coords = geometry.coordinates,
  410. layers = [],
  411. latlng, latlngs, i, len, layer;
  412. switch (geometry.type) {
  413. case 'Point':
  414. latlng = this.coordsToLatLng(coords);
  415. return pointToLayer ? pointToLayer(latlng) : new R.Marker(latlng);
  416. case 'MultiPoint':
  417. for (i = 0, len = coords.length; i < len; i++) {
  418. latlng = this.coordsToLatLng(coords[i]);
  419. layer = pointToLayer ? pointToLayer(latlng) : new R.Marker(latlng);
  420. layers.push(layer);
  421. }
  422. return new R.FeatureGroup(layers);
  423. case 'LineString':
  424. latlngs = this.coordsToLatLngs(coords);
  425. return new R.Polyline(latlngs);
  426. case 'Polygon':
  427. latlngs = this.coordsToLatLngs(coords, 1);
  428. return new R.Polygon(latlngs);
  429. case 'MultiLineString':
  430. latlngs = this.coordsToLatLngs(coords, 1);
  431. return new R.MultiPolyline(latlngs);
  432. case "MultiPolygon":
  433. latlngs = this.coordsToLatLngs(coords, 2);
  434. return new R.MultiPolygon(latlngs);
  435. case "GeometryCollection":
  436. for (i = 0, len = geometry.geometries.length; i < len; i++) {
  437. layer = this.geometryToLayer(geometry.geometries[i], pointToLayer);
  438. layers.push(layer);
  439. }
  440. return new R.FeatureGroup(layers);
  441. default:
  442. throw new Error('Invalid GeoJSON object.');
  443. }
  444. },
  445. coordsToLatLng: function (coords, reverse) { // (Array, Boolean) -> LatLng
  446. var lat = parseFloat(coords[reverse ? 0 : 1]),
  447. lng = parseFloat(coords[reverse ? 1 : 0]);
  448. return new L.LatLng(lat, lng, true);
  449. },
  450. coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array
  451. var latlng,
  452. latlngs = [],
  453. i, len;
  454. for (i = 0, len = coords.length; i < len; i++) {
  455. latlng = levelsDeep ?
  456. this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) :
  457. this.coordsToLatLng(coords[i], reverse);
  458. latlngs.push(latlng);
  459. }
  460. return latlngs;
  461. }
  462. });
  463. }());