geomap-0.4.5.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * GeoMap v0.4.5
  3. * https://github.com/x6doooo/GeoMap
  4. *
  5. * Copyright 2013 Dx. Yang
  6. * Released under the MIT license
  7. */
  8. (function($, undefined){
  9. var version = "0.4.5"
  10. var convertor = {
  11. "xmin": 360,
  12. "xmax": 0,
  13. "ymin": 180,
  14. "ymax": 0,
  15. /*!Private
  16. 让阿拉斯加地区在地图右侧显示
  17. */
  18. "formatPoint": function(p){
  19. return [
  20. (p[0] < -168.5 ? p[0] + 360 : p[0]) + 170,
  21. 90 - p[1]
  22. ];
  23. },
  24. "makePoint": function(p){
  25. var self = this,
  26. point = self.formatPoint(p),
  27. x = point[0],
  28. y = point[1];
  29. if(self.xmin > x) self.xmin = x;
  30. if(self.xmax < x) self.xmax = x;
  31. if(self.ymin > y) self.ymin = y;
  32. if(self.ymax < y) self.ymax = y;
  33. return [x, y];
  34. },
  35. "Point": function(coordinates){
  36. coordinates = this.makePoint(coordinates);
  37. return coordinates.join(',');
  38. },
  39. "LineString": function(coordinates){
  40. var str = '',
  41. self = this,
  42. i = 0,
  43. len = coordinates.length,
  44. point;
  45. for( ; i < len; i++){
  46. point = self.makePoint(coordinates[i]);
  47. if(i == 0){
  48. str = 'M' + point.join(',');
  49. }else{
  50. str = str + 'L' + point.join(',');
  51. }
  52. }
  53. return str;
  54. },
  55. "Polygon": function(coordinates){
  56. var str = '',
  57. i = 0,
  58. len = coordinates.length;
  59. for(; i < len; i++){
  60. str = str + convertor.LineString(coordinates[i]) + 'z';
  61. }
  62. return str;
  63. },
  64. "MultiPoint": function(coordinates){
  65. var arr = [],
  66. i = 0,
  67. len = coordinates.length;
  68. for(; i < len; i++){
  69. arr.push(convertor.Point(coordinates[i]));
  70. }
  71. return arr;
  72. },
  73. "MultiLineString": function(coordinates){
  74. var str = '',
  75. i = 0,
  76. len = coordinates.length;
  77. for(; i < len; i++){
  78. str += convertor.LineString(coordinates[i]);
  79. }
  80. return str;
  81. },
  82. "MultiPolygon": function(coordinates){
  83. var str = '',
  84. i = 0,
  85. len = coordinates.length;
  86. for(; i < len; i++){
  87. str += convertor.Polygon(coordinates[i]);
  88. }
  89. return str;
  90. }
  91. };
  92. function json2path(json){
  93. var
  94. shapes = json.features,
  95. shapeType,
  96. shapeCoordinates,
  97. str,
  98. geometries,
  99. pathArray = [],
  100. i, j,
  101. len, len2,
  102. val,
  103. shape;
  104. convertor.xmin = 360;
  105. convertor.xmax = 0;
  106. convertor.ymin = 180;
  107. convertor.ymax = 0;
  108. for(i = 0, len = shapes.length; i < len; i++){
  109. shape = shapes[i];
  110. if(shape.type == 'Feature'){
  111. pushApath(shape.geometry, shape);
  112. }else if(shape.type = 'GeometryCollection'){
  113. geometries = shape.geometries;
  114. for(j = 0, len2 = geometries.length; j < len2; j++){
  115. val = geometries[j];
  116. pushApath(val, val);
  117. }
  118. }
  119. }
  120. function pushApath(gm, shape){
  121. shapeType = gm.type;
  122. shapeCoordinates = gm.coordinates;
  123. str = convertor[shapeType](shapeCoordinates);
  124. pathArray.push({
  125. type: shapeType,
  126. path: str,
  127. properties: shape.properties,
  128. id: shape.id
  129. });
  130. }
  131. return pathArray;
  132. }
  133. var GeoMap = function(cfg){
  134. var self = this,
  135. defaultCfg = {
  136. container: 'body',
  137. offset: null,
  138. scale: null,
  139. mapStyle: {
  140. 'fill': '#fff',
  141. 'stroke': '#999',
  142. 'stroke-width': 0.7
  143. },
  144. crossline:{
  145. enable: false,
  146. color: '#ccc'
  147. },
  148. background:'#fff'
  149. };
  150. $.extend(true, defaultCfg, cfg);
  151. self.container = $(defaultCfg.container);
  152. if(self.container.length == 0){
  153. throw new Error('map container is not defined!');
  154. }
  155. self.width = defaultCfg.width || self.container.width();
  156. self.height = defaultCfg.height || self.container.height();
  157. self.left = self.container.offset().left;
  158. self.top = self.container.offset().top;
  159. self.canvas = new Raphael(self.container.get(0), self.width, self.height);
  160. self.shapes = self.canvas.set();
  161. self.config = defaultCfg;
  162. self.paths = null;
  163. };
  164. GeoMap.prototype = {
  165. load: function(json){
  166. this.paths = json2path(json);
  167. },
  168. render: function(){
  169. var self = this,
  170. shapes = self.shapes,
  171. paths = self.paths,
  172. canvas = self.canvas,
  173. config = self.config,
  174. style = config.mapStyle,
  175. offset = config.offset,
  176. scale = config.scale,
  177. background = config.background,
  178. crossline = config.crossline,
  179. width = self.width,
  180. height = self.height,
  181. left = self.left + 5,
  182. top = self.top + 7,
  183. mapleft = convertor.xmin,
  184. maptop = convertor.ymin,
  185. mapwidth = convertor.xmax - convertor.xmin,
  186. mapheight = convertor.ymax - convertor.ymin,
  187. aPath = null, linehead, linex, liney, back, i, len, currentPath;
  188. if(!scale){
  189. var temx = width / mapwidth,
  190. temy = height / mapheight;
  191. temx > temy ? temx = temy : temy = temx;
  192. temx = temy * 0.73;
  193. self.config.scale = scale = {
  194. x: temx,
  195. y: temy
  196. };
  197. }
  198. if(!offset){
  199. self.config.offset = offset = {
  200. x: mapleft,
  201. y: maptop
  202. };
  203. }
  204. back = canvas.rect(mapleft, maptop, mapwidth, mapheight).scale(scale.x, scale.y, 0, 0).attr({
  205. 'fill': background, 'stroke-width': 0
  206. });
  207. linehead = 'M' + (mapleft) + ',' + (maptop);
  208. linex = linehead + 'H' + convertor.xmax * scale.x;
  209. liney = linehead + 'V' + convertor.ymax * scale.y;
  210. self.crosslineX = canvas.path(linex).attr({'stroke': crossline.color, 'stroke-width': '1px'}).hide();
  211. self.crosslineY = canvas.path(liney).attr({'stroke': crossline.color, 'stroke-width': '1px'}).hide();
  212. for(i = 0, len = paths.length; i < len; i++){
  213. currentPath = paths[i];
  214. if(currentPath.type == 'point' || currentPath.type == 'MultiPoint'){
  215. //TODO
  216. }else{
  217. aPath = canvas.path(currentPath.path).data({'properties': currentPath.properties, 'id': currentPath.id});
  218. }
  219. shapes.push(aPath);
  220. }
  221. canvas.setViewBox(offset.x, offset.y, width, height, false);
  222. shapes.attr(style).scale(scale.x, scale.y, mapleft, maptop);
  223. //TODO: crossline的位置计算,由于要做一点偏移,所以偏移量要考虑到缩放问题,类似getGeoPosition方法,计算出图上偏移的实际值
  224. if(crossline.enable === true){
  225. shapes.mouseover(function(){
  226. showCrossLine();
  227. }).mousemove(function(e){
  228. moveCrossLine(e);
  229. }).mouseout(function(){
  230. hideCrossLine();
  231. });
  232. back.mouseover(function(){
  233. showCrossLine();
  234. }).mousemove(function(e){
  235. moveCrossLine(e);
  236. }).mouseout(function(){
  237. hideCrossLine();
  238. });
  239. }
  240. function showCrossLine(){
  241. self.crosslineX.toFront().show();
  242. self.crosslineY.toFront().show();
  243. }
  244. function moveCrossLine(e){
  245. var pos = getEventPos(e);
  246. self.crosslineX.transform('T0,'+pos.y);
  247. self.crosslineY.transform('T'+ pos.x + ',0');
  248. }
  249. function hideCrossLine(){
  250. self.crosslineX.hide();
  251. self.crosslineY.hide();
  252. }
  253. function getEventPos(e){
  254. return {
  255. x: parseInt(e.pageX - left) + 0.4,
  256. y: parseInt(e.pageY - top) + 0.4
  257. };
  258. }
  259. },
  260. /*!
  261. 将平面上的一个点的坐标,转换成实际经纬度坐标
  262. */
  263. getGeoPosition: function(p){
  264. var x1 = p[0],
  265. y1 = p[1],
  266. m = this.shapes[0].matrix,
  267. x, y,
  268. a = m.a,
  269. b = m.b,
  270. c = m.c,
  271. d = m.d,
  272. e = m.e,
  273. f = m.f;
  274. y = (y1 - f - x1 / a * b + e / a * b)/(d - c / a * b);
  275. x = (x1 - e - y * c) / a;
  276. y = 90 - y;
  277. x = x - 170;
  278. x = x > 180 ? x - 360 : x;
  279. return [x, y];
  280. },
  281. geo2pos: function(p){
  282. var self = this,
  283. matrixTrans = self.shapes[0].matrix;
  284. p = convertor.makePoint([p.x, p.y]);
  285. //通过matrix去计算点变换后的坐标
  286. p[0] = matrixTrans.x(p[0], p[1]);
  287. p[1] = matrixTrans.y(p[0], p[1]);
  288. return p;
  289. },
  290. setPoint: function(p){
  291. // 点的默认样式
  292. var self = this,
  293. a = {
  294. "x": 0,
  295. "y": 0,
  296. "r": 1,
  297. "opacity": 0.5,
  298. "fill": "#238CC3",
  299. "stroke": "#238CC3",
  300. "stroke-width": 0,
  301. "stroke-linejoin": "round"
  302. },
  303. matrixTrans = self.shapes[0].matrix;
  304. p = self.geo2pos(p);
  305. $.extend(true, a, p);
  306. return self.canvas.circle(p[0], p[1], a.r).attr(a);
  307. }
  308. };
  309. //TODO: heat map
  310. GeoMap.version = version;
  311. this.GeoMap = GeoMap;
  312. })(jQuery);