gpxtrele.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /**
  2. * @file
  3. * Contains javascript libs for gpx_trackelevator module.
  4. *
  5. * When the DOM is ready initialize function is launched to prepare
  6. * data for rendering. Then plot_track is launched to create the map
  7. * and then google core_cart is load. Finally, the elevation graph
  8. * is created by plot_elevation_graph function.
  9. *
  10. /* Expected Drupal.settings.gpx_track_elevation properties:
  11. * - points: an object with field instance & formatter data. See below for details.
  12. * - trColor: track color.
  13. * - epColor: alevation profile color.
  14. * - trstroke: track stroke.
  15. * - maptype: map type to use as default.
  16. * - enableBilink: if bidirectional link is enabled.
  17. * - trStartPoint: start points marker image.
  18. * - trEndPoint: end points marker image.
  19. * - trLastPoint: last points marker image.
  20. * - parDistance: "Distance" translation.
  21. * - parElevation: "Elevation" translation.
  22. * - parAsl: "asl" (above see level) translation.
  23. * - parLengthUnit: "m" (meter) translation.
  24. * - wpt_types: object with one method for each recognized waypoint type.
  25. * Method name is the type and its value is the associated image url.
  26. *
  27. * points object has one method for each node to show. Metod name is id-nid, where nid is the node id
  28. * points[id-nid] return an array whose elements are:
  29. * - gpx_start_point (int: how to show track start point for this formatter)
  30. * - gpx_start_point (int: how to show track start point for this formatter)
  31. * - array with the following elements:
  32. * - array with an element for each <trk> tag:
  33. * - <trk> name
  34. * - array with an element for each <trkpt>
  35. * - latitude
  36. * - longitude
  37. * - elevation
  38. * - array with an element for each waypoint
  39. * - waypoint name
  40. * - waypoint latitude
  41. * - waypoint longitude
  42. * - waypoint description
  43. * - waypoint elevation
  44. * - waypoint type
  45. */
  46. (function($) {
  47. var points = [];
  48. var bounds;
  49. var map = {};
  50. var elevations = {};
  51. var markerPoint = {};
  52. var trackNumber = {};
  53. var chart = {};
  54. var trackName = {};
  55. var StartPoints = [];
  56. var EndPoints = [];
  57. var markers_type;
  58. var trackNids;
  59. var track_points_marker = {};
  60. var DEFAULT_MARKER = "http://www.google.com/mapfiles/marker.png";
  61. var DEFAULT_START_MARKER = "http://www.google.com/mapfiles/dd-start.png";
  62. var DEFAULT_END_MARKER = "http://www.google.com/mapfiles/dd-end.png";
  63. var DEFAULT_MARKER_SIZE = 30;
  64. var GOOGLE_CHART_LOADED = false;
  65. /**
  66. * Function plot_track creates the map and the listeners and plots the track.
  67. */
  68. function plot_track(){
  69. rawWayPoints = {};
  70. rawWayPoints[nIndex] = trackNids['id-' + nIndex][2][1];
  71. var myTrack = [];
  72. var wayPoints = [];
  73. // Show the map.
  74. $('.gpxtrele-container .map-canvas').height('20em');
  75. theMap = document.getElementById('gpxtrele-container-' + nIndex).getElementsByClassName('map-canvas');
  76. map[nIndex] = new google.maps.Map(theMap[0],
  77. {
  78. draggable: true,
  79. keyboardShortcuts: false,
  80. mapTypeId: google.maps.MapTypeId[Drupal.settings.gpx_track_elevation.maptype],
  81. overviewMapControl: true,
  82. overviewMapControlOptions: false,
  83. streetViewControl: false,
  84. mapTypeControl: true,
  85. zomcontrol: true,
  86. });
  87. // Prepare the mouse move marker.
  88. markerPoint[nIndex] = new google.maps.Marker({
  89. position: new google.maps.LatLng(0,0),
  90. map:map[nIndex],
  91. visible: true,
  92. opacity: 0,
  93. icon: {
  94. path: google.maps.SymbolPath.CIRCLE,
  95. scale: 3,
  96. strokeColor: Drupal.settings.gpx_track_elevation.epColor
  97. },
  98. });
  99. // Prepare waypoints infowindow.
  100. infoBox = new google.maps.InfoWindow({});
  101. // Show waypoints.
  102. for (var i = 0; i < rawWayPoints[nIndex].length; i += 1) {
  103. var point = new google.maps.LatLng(rawWayPoints[nIndex][i][1], rawWayPoints[nIndex][i][2]);
  104. bounds.extend(point);
  105. wayPoints.push(new google.maps.Marker({
  106. map:map[nIndex],
  107. position: point,
  108. visible: true,
  109. title: rawWayPoints[nIndex][i][0],
  110. opacity: 1,
  111. icon: setMarkerImage(rawWayPoints[nIndex][i][5]),
  112. zIndex: 2
  113. }));
  114. google.maps.event.addListener(wayPoints[i], 'click', bindIndexWaypoint(rawWayPoints, nIndex, i));
  115. }
  116. map[nIndex].fitBounds(bounds);
  117. // Show last track point.
  118. for (i = 0; i < trackNumber[nIndex]; i += 1) {
  119. if (trackNids['id-' + nIndex][1] > 0) {
  120. if ((trackNids['id-' + nIndex][1] > 1) || (i == (trackNumber[nIndex] - 1))) {
  121. EndPoints.push(new google.maps.Marker({
  122. map:map[nIndex],
  123. position: points[nIndex][i][points[nIndex][i].length - 1],
  124. icon: setTrackImage((i == (trackNumber[nIndex] - 1)) ? 'last' : 'end', DEFAULT_END_MARKER),
  125. title: trackName[nIndex][i],
  126. zIndex: 1
  127. }));
  128. }
  129. }
  130. // Show first track point.
  131. if (trackNids['id-' + nIndex][0] > 0) {
  132. if ((trackNids['id-' + nIndex][0] > 1) || (i == 0)) {
  133. StartPoints.push(new google.maps.Marker({
  134. map:map[nIndex],
  135. position: points[nIndex][i][0],
  136. icon: setTrackImage('start', DEFAULT_START_MARKER),
  137. title: trackName[nIndex][i],
  138. zIndex: 3
  139. }));
  140. }
  141. }
  142. // Show tracks.
  143. myTrack.push(new google.maps.Polyline({
  144. path: points[nIndex][i],
  145. strokeColor: Drupal.settings.gpx_track_elevation.trColor,
  146. strokeOpacity: .7,
  147. strokeWeight: Drupal.settings.gpx_track_elevation.trstroke,
  148. clickable: false,
  149. }));
  150. myTrack[i].setMap(map[nIndex]);
  151. }
  152. if (Drupal.settings.gpx_track_elevation.enableBilink) {
  153. // We have to catch mousemove of the map to emulate that of the track
  154. // mousemove of the polyline would be better, but it does not return
  155. // point info for not editable polylines.
  156. google.maps.event.addListener(map[nIndex], 'mousemove', marker_on_map.bind(nIndex));
  157. function marker_on_map (evt){
  158. var nIndex = this;
  159. var minDist = 1000000000;
  160. var dist;
  161. var thePoint;
  162. for (var j = 0; j < trackNumber[nIndex]; j += 1) {
  163. for (var i = 0; i < points[nIndex][j].length; i += 1) {
  164. dist = google.maps.geometry.spherical.computeDistanceBetween(points[nIndex][j][i], evt.latLng);
  165. if (dist < minDist) {
  166. minDist = dist;
  167. thePoint = [j,i];
  168. }
  169. }
  170. }
  171. // In order to emulate the "track mousemove" we need to be close
  172. // to the track. Unfortunately the term "close to" depends on the
  173. // zoom level, so we have to check the zoom based distance.
  174. var zoom = 591658 / Math.pow(2, map[nIndex].getZoom() - 1);
  175. if (zoom / minDist > 1) {
  176. markerPoint[nIndex].setPosition(points[nIndex][thePoint[0]][thePoint[1]]);
  177. markerPoint[nIndex].setOpacity(1);
  178. chart[nIndex][thePoint[0]].setSelection([{row:thePoint[1],column:1}]);
  179. }
  180. else {
  181. for (var k = 0; k < trackNumber[nIndex]; k += 1) {
  182. chart[nIndex][k].setSelection(null);
  183. }
  184. markerPoint[nIndex].setOpacity(0);
  185. }
  186. }
  187. }
  188. /**
  189. * Function bindIndexWaypoint shows the waypoints InfoWindow.
  190. *
  191. * @param i
  192. * index of the waypoint to be shown.
  193. */
  194. function bindIndexWaypoint(rawWayPoints, nIndex, i) {
  195. return (function(){
  196. var inboxInfo = '';
  197. if (this.title != '') {
  198. inboxInfo = '<b>' + this.title + '</b><br>';
  199. }
  200. if (rawWayPoints[nIndex][i][4] != '') {
  201. inboxInfo += Drupal.settings.gpx_track_elevation.parElevation + ': ' + round10(rawWayPoints[nIndex][i][4]) + '<br>';
  202. }
  203. if (rawWayPoints[nIndex][i][3] != '') {
  204. inboxInfo += rawWayPoints[nIndex][i][3] + '<br>';
  205. }
  206. infoBox.setContent(inboxInfo);
  207. infoBox.open(map[nIndex],this);
  208. });
  209. }
  210. }
  211. /**
  212. * Function plot_elevation_graph creates elevation profile(s).
  213. */
  214. function plot_elevation_graph() {
  215. nIndex = this;
  216. var data = [];
  217. chart[nIndex] = [];
  218. for (var j = 0; j < trackNumber[nIndex]; j += 1) {
  219. var divId = 'elevation_chart' + j;
  220. $('#gpxtrele-container-' + nIndex + ' .elevation_chart').append('<div id="elevation_chart-' + nIndex + '-' + j + '"></div>');
  221. data.push(new google.visualization.DataTable());
  222. data[j].addColumn('string', Drupal.settings.gpx_track_elevation.parDistance);
  223. data[j].addColumn('number', Drupal.settings.gpx_track_elevation.parElevation);
  224. data[j].addColumn({'type': 'string', 'role': 'tooltip', 'p': {'html': true}});
  225. for (var i = 1; i < elevations[nIndex][j].length; i++) {
  226. data[j].addRow([elevations[nIndex][j][i][0] + '', elevations[nIndex][j][i][1], Drupal.settings.gpx_track_elevation.parDistance + ': <b>' + elevations[nIndex][j][i][0] + '</b> ' + Drupal.settings.gpx_track_elevation.parLengthUnit + '<br> ' + Drupal.settings.gpx_track_elevation.parElevation + ': <b>' + elevations[nIndex][j][i][1] + '</b> ' + Drupal.settings.gpx_track_elevation.parLengthUnit + ' ' + Drupal.settings.gpx_track_elevation.parAsl]);
  227. }
  228. chart[nIndex].push(new google.visualization.AreaChart(document.getElementById('elevation_chart-' + nIndex + '-' + j)));
  229. document.getElementById('elevation_chart-' + nIndex + '-' + j).parentNode.style.display = 'block';
  230. document.getElementById('elevation_chart-' + nIndex + '-' + j).style.display = 'block';
  231. google.visualization.events.addListener(chart[nIndex][j], 'onmouseover', show_marker.bind([parseInt(nIndex), j]));
  232. google.visualization.events.addListener(chart[nIndex][j], 'onmouseout', hide_marker.bind([parseInt(nIndex), j]));
  233. chart[nIndex][j].draw(data[j],{
  234. height: 200,
  235. legend: 'none',
  236. title: trackName[nIndex][j],
  237. titleX: Drupal.settings.gpx_track_elevation.parDistance,
  238. titleY: Drupal.settings.gpx_track_elevation.parElevation,
  239. hAxis: {showTextEvery: Math.round(points[nIndex][j].length / 10)},
  240. colors: [Drupal.settings.gpx_track_elevation.epColor],
  241. tooltip: {isHtml: true, trigger: 'both'},
  242. });
  243. }
  244. /**
  245. * Function show_marker shows a marker on the map.
  246. *
  247. * @param pnt
  248. * position where marker has to be shown.
  249. */
  250. function hide_marker (pnt) {
  251. markerPoint[this[0]].setOpacity(0);
  252. }
  253. /**
  254. * Function show_marker shows a marker on the map.
  255. *
  256. * @param pnt
  257. * position where marker has to be shown.
  258. */
  259. function show_marker (pnt) {
  260. markerPoint[this[0]].setPosition(points[this[0]][this[1]][pnt.row]);
  261. markerPoint[this[0]].setOpacity(1);
  262. }
  263. }
  264. /**
  265. * Function round10 rounds a number to the nearest 10.
  266. *
  267. * @param number
  268. * number to be rounded.
  269. *
  270. * @return
  271. * rounded number.
  272. */
  273. function round10(number) {
  274. return Math.round(Math.round(number / 10) * 10);
  275. }
  276. function setMarkerImage (wpt) {
  277. var selected_marker;
  278. if ((wpt != '') && (markers_type) && (wpt in markers_type)) {
  279. selected_marker = {
  280. url: markers_type[wpt],
  281. scaledSize: new google.maps.Size(DEFAULT_MARKER_SIZE, DEFAULT_MARKER_SIZE),
  282. anchor: new google.maps.Point(DEFAULT_MARKER_SIZE / 2, DEFAULT_MARKER_SIZE / 2),
  283. }
  284. }
  285. else if ((markers_type) && ('default' in markers_type)) {
  286. selected_marker = {
  287. url: markers_type['default'],
  288. scaledSize: new google.maps.Size(DEFAULT_MARKER_SIZE, DEFAULT_MARKER_SIZE),
  289. anchor: new google.maps.Point(DEFAULT_MARKER_SIZE / 2, DEFAULT_MARKER_SIZE / 2),
  290. }
  291. }
  292. else {
  293. selected_marker = DEFAULT_MARKER;
  294. }
  295. return selected_marker;
  296. }
  297. function setTrackImage (pointType, defaultImage) {
  298. defaultImage = defaultImage || DEFAULT_MARKER;
  299. if (pointType in track_points_marker) {
  300. selected_marker = track_points_marker[pointType] || defaultImage;
  301. }
  302. else {
  303. selected_marker = defaultImage;
  304. }
  305. return selected_marker;
  306. }
  307. Drupal.behaviors.gpx_track_elevation = {
  308. attach: function (context, settings) {
  309. $('.gpxtrele-container', context).once('gpxtrele-container', function () {
  310. track_points_marker.start = Drupal.settings.gpx_track_elevation.trStartPoint;
  311. track_points_marker.end = Drupal.settings.gpx_track_elevation.trEndPoint;
  312. track_points_marker.last = Drupal.settings.gpx_track_elevation.trLastPoint;
  313. markers_type = Drupal.settings.gpx_track_elevation.wpt_types;
  314. bounds = new google.maps.LatLngBounds();
  315. trackNids = Drupal.settings.gpx_track_elevation.points;
  316. nIndex = this.id.substring(19);
  317. index = 'id-' + nIndex;
  318. trackPoints = trackNids[index][2][0];
  319. trackNumber[nIndex] = trackPoints.length;
  320. nIndex = index.substring(3);
  321. points[nIndex] = [];
  322. trackName[nIndex] = [];
  323. elevations[nIndex] = [];
  324. for (var j = 0; j < trackNumber[nIndex]; j += 1) {
  325. trackName[nIndex][j] = trackPoints[j][0];
  326. var totalDistance = 0;
  327. points[nIndex][j] = [];
  328. elevations[nIndex][j] = [];
  329. var point = new google.maps.LatLng(trackPoints[j][1][0][0], trackPoints[j][1][0][1]);
  330. elevations[nIndex][j].push(['0', trackPoints[j][1][0][2]]);
  331. points[nIndex][j].push(point);
  332. bounds.extend(point);
  333. for (var i = 1; i < trackPoints[j][1].length; i += 1) {
  334. point = new google.maps.LatLng(trackPoints[j][1][i][0], trackPoints[j][1][i][1]);
  335. points[nIndex][j].push(point);
  336. totalDistance += google.maps.geometry.spherical.computeDistanceBetween(points[nIndex][j][i - 1], points[nIndex][j][i]);
  337. bounds.extend(point);
  338. elevations[nIndex][j].push([round10(totalDistance) + '', Math.round(trackPoints[j][1][i][2])]);
  339. }
  340. }
  341. plot_track(nIndex);
  342. google.load('visualization', '1', {packages: ['corechart'], callback: plot_elevation_graph.bind(nIndex)});
  343. });
  344. }
  345. };
  346. }(jQuery));