基于leaflet.js实现的轨迹回放

左手的ㄟ右手 2023-07-24 02:13 123阅读 0赞

先看下效果图: (注意,这里是GIF图片,看起来会有些卡顿,但真实情况是很流畅的)
在这里插入图片描述
js文件们:(自己创建一个js文件,复制进去)
markerTrack.js:

  1. L.Marker.addInitHook(function () {
  2. this.moveOptions = {
  3. origin: null,
  4. timer: null,
  5. done: 0,
  6. path: null,
  7. length: 0
  8. };
  9. this.setSpeed = function (speed) {
  10. this.moveOptions.speed = isNaN(parseFloat(speed)) || parseFloat(speed) <= 0 ? 200 : parseFloat(speed);
  11. };
  12. this.getSpeed = function () {
  13. return this.moveOptions.speed;
  14. };
  15. this.moveAlong = function (path, speed) {
  16. path = path instanceof L.Polyline ? path : new L.Polyline(path);
  17. this.moveOptions.path = path;
  18. this.moveOptions.length = L.GeometryUtil.length(path);
  19. this.moveOptions.speed = isNaN(parseFloat(speed)) || parseFloat(speed <= 0) ? 200 : parseFloat(speed);
  20. this._move();
  21. };
  22. this.pauseMove = function () {
  23. clearInterval(this.moveOptions.timer);
  24. this.moveOptions.timer = null;
  25. };
  26. this.resumeMove = function () {
  27. this._move();
  28. };
  29. this.stopMove = function () {
  30. this.pauseMove();
  31. this.moveOptions.done = 0;
  32. };
  33. this._move = function () {
  34. if (this.moveOptions.timer) return;
  35. let _t = this;
  36. this.moveOptions.timer = setInterval(function () {
  37. let done = _t.moveOptions.done;
  38. done += _t.moveOptions.speed / 1000 * 20;
  39. let radio = done / _t.moveOptions.length;
  40. radio >= 1 ? (radio = 0, done = 0) : true;
  41. _t.moveOptions.done = done;
  42. let p = L.GeometryUtil.interpolateOnLine(_t._map, _t.moveOptions.path, radio);
  43. _t.setLatLng(p.latLng);
  44. let pre_p = _t.moveOptions.path.getLatLngs()[p.predecessor];
  45. if (pre_p) {
  46. let passed = _t.moveOptions.path.getLatLngs().slice(0, p.predecessor + 1);
  47. passed.push(p.latLng);
  48. _t.fire('update_position', { path: passed});
  49. let deg = L.GeometryUtil.computeAngle(_t._map.project(pre_p), _t._map.project(p.latLng))
  50. _t._icon.style.transformOrigin = '50% 50%';
  51. _t._icon.style.transform += ' rotateZ(' + deg + 'deg)';
  52. }
  53. }, 20);
  54. }
  55. });

em.geometryutil.js:

  1. // Packaging/modules magic dance.
  2. (function (factory) {
  3. factory(window.L)
  4. }(function (L) {
  5. "use strict";
  6. L.Polyline._flat = L.LineUtil.isFlat || L.Polyline._flat || function (latlngs) {
  7. // true if it's a flat array of latlngs; false if nested
  8. return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
  9. };
  10. /** * @fileOverview Geometry utilities for distances and linear referencing. * @name L.GeometryUtil */
  11. L.GeometryUtil = L.extend(L.GeometryUtil || { }, {
  12. /** Shortcut function for planar distance between two {L.LatLng} at current zoom. @tutorial distance-length @param {L.Map} map map to be used for this method @param {L.LatLng} latlngA geographical point A @param {L.LatLng} latlngB geographical point B @returns {Number} planar distance */
  13. distance: function (map, latlngA, latlngB) {
  14. return map.latLngToLayerPoint(latlngA).distanceTo(map.latLngToLayerPoint(latlngB));
  15. },
  16. /** Shortcut function for planar distance between a {L.LatLng} and a segment (A-B). @param {L.Map} map map to be used for this method @param {L.LatLng} latlng - The position to search @param {L.LatLng} latlngA geographical point A of the segment @param {L.LatLng} latlngB geographical point B of the segment @returns {Number} planar distance */
  17. distanceSegment: function (map, latlng, latlngA, latlngB) {
  18. var p = map.latLngToLayerPoint(latlng),
  19. p1 = map.latLngToLayerPoint(latlngA),
  20. p2 = map.latLngToLayerPoint(latlngB);
  21. return L.LineUtil.pointToSegmentDistance(p, p1, p2);
  22. },
  23. /** Shortcut function for converting distance to readable distance. @param {Number} distance distance to be converted @param {String} unit 'metric' or 'imperial' @returns {String} in yard or miles */
  24. readableDistance: function (distance, unit) {
  25. var isMetric = (unit !== 'imperial'),
  26. distanceStr;
  27. if (isMetric) {
  28. // show metres when distance is < 1km, then show km
  29. if (distance > 1000) {
  30. distanceStr = (distance / 1000).toFixed(2) + ' km';
  31. }
  32. else {
  33. distanceStr = Math.ceil(distance) + ' m';
  34. }
  35. }
  36. else {
  37. distance *= 1.09361;
  38. if (distance > 1760) {
  39. distanceStr = (distance / 1760).toFixed(2) + ' miles';
  40. }
  41. else {
  42. distanceStr = Math.ceil(distance) + ' yd';
  43. }
  44. }
  45. return distanceStr;
  46. },
  47. /** Returns true if the latlng belongs to segment A-B @param {L.LatLng} latlng - The position to search @param {L.LatLng} latlngA geographical point A of the segment @param {L.LatLng} latlngB geographical point B of the segment @param {?Number} [tolerance=0.2] tolerance to accept if latlng belongs really @returns {boolean} */
  48. belongsSegment: function(latlng, latlngA, latlngB, tolerance) {
  49. tolerance = tolerance === undefined ? 0.2 : tolerance;
  50. var hypotenuse = latlngA.distanceTo(latlngB),
  51. delta = latlngA.distanceTo(latlng) + latlng.distanceTo(latlngB) - hypotenuse;
  52. return delta/hypotenuse < tolerance;
  53. },
  54. /** * Returns total length of line * @tutorial distance-length * * @param {L.Polyline|Array<L.Point>|Array<L.LatLng>} coords Set of coordinates * @returns {Number} Total length (pixels for Point, meters for LatLng) */
  55. length: function (coords) {
  56. var accumulated = L.GeometryUtil.accumulatedLengths(coords);
  57. return accumulated.length > 0 ? accumulated[accumulated.length-1] : 0;
  58. },
  59. /** * Returns a list of accumulated length along a line. * @param {L.Polyline|Array<L.Point>|Array<L.LatLng>} coords Set of coordinates * @returns {Array<Number>} Array of accumulated lengths (pixels for Point, meters for LatLng) */
  60. accumulatedLengths: function (coords) {
  61. if (typeof coords.getLatLngs == 'function') {
  62. coords = coords.getLatLngs();
  63. }
  64. if (coords.length === 0)
  65. return [];
  66. var total = 0,
  67. lengths = [0];
  68. for (var i = 0, n = coords.length - 1; i< n; i++) {
  69. total += coords[i].distanceTo(coords[i+1]);
  70. lengths.push(total);
  71. }
  72. return lengths;
  73. },
  74. /** Returns the closest point of a {L.LatLng} on the segment (A-B) @tutorial closest @param {L.Map} map map to be used for this method @param {L.LatLng} latlng - The position to search @param {L.LatLng} latlngA geographical point A of the segment @param {L.LatLng} latlngB geographical point B of the segment @returns {L.LatLng} Closest geographical point */
  75. closestOnSegment: function (map, latlng, latlngA, latlngB) {
  76. var maxzoom = map.getMaxZoom();
  77. if (maxzoom === Infinity)
  78. maxzoom = map.getZoom();
  79. var p = map.project(latlng, maxzoom),
  80. p1 = map.project(latlngA, maxzoom),
  81. p2 = map.project(latlngB, maxzoom),
  82. closest = L.LineUtil.closestPointOnSegment(p, p1, p2);
  83. return map.unproject(closest, maxzoom);
  84. },
  85. /** Returns the closest latlng on layer. Accept nested arrays @tutorial closest @param {L.Map} map map to be used for this method @param {Array<L.LatLng>|Array<Array<L.LatLng>>|L.PolyLine|L.Polygon} layer - Layer that contains the result @param {L.LatLng} latlng - The position to search @param {?boolean} [vertices=false] - Whether to restrict to path vertices. @returns {L.LatLng} Closest geographical point or null if layer param is incorrect */
  86. closest: function (map, layer, latlng, vertices) {
  87. var latlngs,
  88. mindist = Infinity,
  89. result = null,
  90. i, n, distance, subResult;
  91. if (layer instanceof Array) {
  92. // if layer is Array<Array<T>>
  93. if (layer[0] instanceof Array && typeof layer[0][0] !== 'number') {
  94. // if we have nested arrays, we calc the closest for each array
  95. // recursive
  96. for (i = 0; i < layer.length; i++) {
  97. subResult = L.GeometryUtil.closest(map, layer[i], latlng, vertices);
  98. if (subResult.distance < mindist) {
  99. mindist = subResult.distance;
  100. result = subResult;
  101. }
  102. }
  103. return result;
  104. } else if (layer[0] instanceof L.LatLng
  105. || typeof layer[0][0] === 'number'
  106. || typeof layer[0].lat === 'number') { // we could have a latlng as [x,y] with x & y numbers or {lat, lng}
  107. layer = L.polyline(layer);
  108. } else {
  109. return result;
  110. }
  111. }
  112. // if we don't have here a Polyline, that means layer is incorrect
  113. // see https://github.com/makinacorpus/.GeometryUtil/issues/23
  114. if (! ( layer instanceof L.Polyline ) )
  115. return result;
  116. // deep copy of latlngs
  117. latlngs = JSON.parse(JSON.stringify(layer.getLatLngs().slice(0)));
  118. // add the last segment for L.Polygon
  119. if (layer instanceof L.Polygon) {
  120. // add the last segment for each child that is a nested array
  121. var addLastSegment = function(latlngs) {
  122. if (L.Polyline._flat(latlngs)) {
  123. latlngs.push(latlngs[0]);
  124. } else {
  125. for (var i = 0; i < latlngs.length; i++) {
  126. addLastSegment(latlngs[i]);
  127. }
  128. }
  129. };
  130. addLastSegment(latlngs);
  131. }
  132. // we have a multi polygon / multi polyline / polygon with holes
  133. // use recursive to explore and return the good result
  134. if ( ! L.Polyline._flat(latlngs) ) {
  135. for (i = 0; i < latlngs.length; i++) {
  136. // if we are at the lower level, and if we have a L.Polygon, we add the last segment
  137. subResult = L.GeometryUtil.closest(map, latlngs[i], latlng, vertices);
  138. if (subResult.distance < mindist) {
  139. mindist = subResult.distance;
  140. result = subResult;
  141. }
  142. }
  143. return result;
  144. } else {
  145. // Lookup vertices
  146. if (vertices) {
  147. for(i = 0, n = latlngs.length; i < n; i++) {
  148. var ll = latlngs[i];
  149. distance = L.GeometryUtil.distance(map, latlng, ll);
  150. if (distance < mindist) {
  151. mindist = distance;
  152. result = ll;
  153. result.distance = distance;
  154. }
  155. }
  156. return result;
  157. }
  158. // Keep the closest point of all segments
  159. for (i = 0, n = latlngs.length; i < n-1; i++) {
  160. var latlngA = latlngs[i],
  161. latlngB = latlngs[i+1];
  162. distance = L.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB);
  163. if (distance <= mindist) {
  164. mindist = distance;
  165. result = L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
  166. result.distance = distance;
  167. }
  168. }
  169. return result;
  170. }
  171. },
  172. /** Returns the closest layer to latlng among a list of layers. @tutorial closest @param {L.Map} map map to be used for this method @param {Array<L.ILayer>} layers Set of layers @param {L.LatLng} latlng - The position to search @returns {object} ``{layer, latlng, distance}`` or ``null`` if list is empty; */
  173. closestLayer: function (map, layers, latlng) {
  174. var mindist = Infinity,
  175. result = null,
  176. ll = null,
  177. distance = Infinity;
  178. for (var i = 0, n = layers.length; i < n; i++) {
  179. var layer = layers[i];
  180. if (layer instanceof L.LayerGroup) {
  181. // recursive
  182. var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
  183. if (subResult.distance < mindist) {
  184. mindist = subResult.distance;
  185. result = subResult;
  186. }
  187. } else {
  188. // Single dimension, snap on points, else snap on closest
  189. if (typeof layer.getLatLng == 'function') {
  190. ll = layer.getLatLng();
  191. distance = L.GeometryUtil.distance(map, latlng, ll);
  192. }
  193. else {
  194. ll = L.GeometryUtil.closest(map, layer, latlng);
  195. if (ll) distance = ll.distance; // Can return null if layer has no points.
  196. }
  197. if (distance < mindist) {
  198. mindist = distance;
  199. result = { layer: layer, latlng: ll, distance: distance};
  200. }
  201. }
  202. }
  203. return result;
  204. },
  205. /** Returns the n closest layers to latlng among a list of input layers. @param {L.Map} map - map to be used for this method @param {Array<L.ILayer>} layers - Set of layers @param {L.LatLng} latlng - The position to search @param {?Number} [n=layers.length] - the expected number of output layers. @returns {Array<object>} an array of objects ``{layer, latlng, distance}`` or ``null`` if the input is invalid (empty list or negative n) */
  206. nClosestLayers: function (map, layers, latlng, n) {
  207. n = typeof n === 'number' ? n : layers.length;
  208. if (n < 1 || layers.length < 1) {
  209. return null;
  210. }
  211. var results = [];
  212. var distance, ll;
  213. for (var i = 0, m = layers.length; i < m; i++) {
  214. var layer = layers[i];
  215. if (layer instanceof L.LayerGroup) {
  216. // recursive
  217. var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
  218. results.push(subResult);
  219. } else {
  220. // Single dimension, snap on points, else snap on closest
  221. if (typeof layer.getLatLng == 'function') {
  222. ll = layer.getLatLng();
  223. distance = L.GeometryUtil.distance(map, latlng, ll);
  224. }
  225. else {
  226. ll = L.GeometryUtil.closest(map, layer, latlng);
  227. if (ll) distance = ll.distance; // Can return null if layer has no points.
  228. }
  229. results.push({ layer: layer, latlng: ll, distance: distance});
  230. }
  231. }
  232. results.sort(function(a, b) {
  233. return a.distance - b.distance;
  234. });
  235. if (results.length > n) {
  236. return results.slice(0, n);
  237. } else {
  238. return results;
  239. }
  240. },
  241. /** * Returns all layers within a radius of the given position, in an ascending order of distance. @param {L.Map} map map to be used for this method @param {Array<ILayer>} layers - A list of layers. @param {L.LatLng} latlng - The position to search @param {?Number} [radius=Infinity] - Search radius in pixels @return {object[]} an array of objects including layer within the radius, closest latlng, and distance */
  242. layersWithin: function(map, layers, latlng, radius) {
  243. radius = typeof radius == 'number' ? radius : Infinity;
  244. var results = [];
  245. var ll = null;
  246. var distance = 0;
  247. for (var i = 0, n = layers.length; i < n; i++) {
  248. var layer = layers[i];
  249. if (typeof layer.getLatLng == 'function') {
  250. ll = layer.getLatLng();
  251. distance = L.GeometryUtil.distance(map, latlng, ll);
  252. }
  253. else {
  254. ll = L.GeometryUtil.closest(map, layer, latlng);
  255. if (ll) distance = ll.distance; // Can return null if layer has no points.
  256. }
  257. if (ll && distance < radius) {
  258. results.push({ layer: layer, latlng: ll, distance: distance});
  259. }
  260. }
  261. var sortedResults = results.sort(function(a, b) {
  262. return a.distance - b.distance;
  263. });
  264. return sortedResults;
  265. },
  266. /** Returns the closest position from specified {LatLng} among specified layers, with a maximum tolerance in pixels, providing snapping behaviour. @tutorial closest @param {L.Map} map map to be used for this method @param {Array<ILayer>} layers - A list of layers to snap on. @param {L.LatLng} latlng - The position to snap @param {?Number} [tolerance=Infinity] - Maximum number of pixels. @param {?boolean} [withVertices=true] - Snap to layers vertices or segment points (not only vertex) @returns {object} with snapped {LatLng} and snapped {Layer} or null if tolerance exceeded. */
  267. closestLayerSnap: function (map, layers, latlng, tolerance, withVertices) {
  268. tolerance = typeof tolerance == 'number' ? tolerance : Infinity;
  269. withVertices = typeof withVertices == 'boolean' ? withVertices : true;
  270. var result = L.GeometryUtil.closestLayer(map, layers, latlng);
  271. if (!result || result.distance > tolerance)
  272. return null;
  273. // If snapped layer is linear, try to snap on vertices (extremities and middle points)
  274. if (withVertices && typeof result.layer.getLatLngs == 'function') {
  275. var closest = L.GeometryUtil.closest(map, result.layer, result.latlng, true);
  276. if (closest.distance < tolerance) {
  277. result.latlng = closest;
  278. result.distance = L.GeometryUtil.distance(map, closest, latlng);
  279. }
  280. }
  281. return result;
  282. },
  283. /** Returns the Point located on a segment at the specified ratio of the segment length. @param {L.Point} pA coordinates of point A @param {L.Point} pB coordinates of point B @param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive. @returns {L.Point} the interpolated point. */
  284. interpolateOnPointSegment: function (pA, pB, ratio) {
  285. return L.point(
  286. (pA.x * (1 - ratio)) + (ratio * pB.x),
  287. (pA.y * (1 - ratio)) + (ratio * pB.y)
  288. );
  289. },
  290. /** Returns the coordinate of the point located on a line at the specified ratio of the line length. @param {L.Map} map map to be used for this method @param {Array<L.LatLng>|L.PolyLine} latlngs Set of geographical points @param {Number} ratio the length ratio, expressed as a decimal between 0 and 1, inclusive @returns {Object} an object with latLng ({LatLng}) and predecessor ({Number}), the index of the preceding vertex in the Polyline (-1 if the interpolated point is the first vertex) */
  291. interpolateOnLine: function (map, latLngs, ratio) {
  292. latLngs = (latLngs instanceof L.Polyline) ? latLngs.getLatLngs() : latLngs;
  293. var n = latLngs.length;
  294. if (n < 2) {
  295. return null;
  296. }
  297. // ensure the ratio is between 0 and 1;
  298. ratio = Math.max(Math.min(ratio, 1), 0);
  299. if (ratio === 0) {
  300. return {
  301. latLng: latLngs[0] instanceof L.LatLng ? latLngs[0] : L.latLng(latLngs[0]),
  302. predecessor: -1
  303. };
  304. }
  305. if (ratio == 1) {
  306. return {
  307. latLng: latLngs[latLngs.length -1] instanceof L.LatLng ? latLngs[latLngs.length -1] : L.latLng(latLngs[latLngs.length -1]),
  308. predecessor: latLngs.length - 2
  309. };
  310. }
  311. // project the LatLngs as Points,
  312. // and compute total planar length of the line at max precision
  313. var maxzoom = map.getMaxZoom();
  314. if (maxzoom === Infinity)
  315. maxzoom = map.getZoom();
  316. var pts = [];
  317. var lineLength = 0;
  318. for(var i = 0; i < n; i++) {
  319. pts[i] = map.project(latLngs[i], maxzoom);
  320. if(i > 0)
  321. lineLength += pts[i-1].distanceTo(pts[i]);
  322. }
  323. var ratioDist = lineLength * ratio;
  324. // follow the line segments [ab], adding lengths,
  325. // until we find the segment where the points should lie on
  326. var cumulativeDistanceToA = 0, cumulativeDistanceToB = 0;
  327. for (var i = 0; cumulativeDistanceToB < ratioDist; i++) {
  328. var pointA = pts[i], pointB = pts[i+1];
  329. cumulativeDistanceToA = cumulativeDistanceToB;
  330. cumulativeDistanceToB += pointA.distanceTo(pointB);
  331. }
  332. if (pointA == undefined && pointB == undefined) { // Happens when line has no length
  333. var pointA = pts[0], pointB = pts[1], i = 1;
  334. }
  335. // compute the ratio relative to the segment [ab]
  336. var segmentRatio = ((cumulativeDistanceToB - cumulativeDistanceToA) !== 0) ? ((ratioDist - cumulativeDistanceToA) / (cumulativeDistanceToB - cumulativeDistanceToA)) : 0;
  337. var interpolatedPoint = L.GeometryUtil.interpolateOnPointSegment(pointA, pointB, segmentRatio);
  338. return {
  339. latLng: map.unproject(interpolatedPoint, maxzoom),
  340. predecessor: i-1
  341. };
  342. },
  343. /** Returns a float between 0 and 1 representing the location of the closest point on polyline to the given latlng, as a fraction of total line length. (opposite of L.GeometryUtil.interpolateOnLine()) @param {L.Map} map map to be used for this method @param {L.PolyLine} polyline Polyline on which the latlng will be search @param {L.LatLng} latlng The position to search @returns {Number} Float between 0 and 1 */
  344. locateOnLine: function (map, polyline, latlng) {
  345. var latlngs = polyline.getLatLngs();
  346. if (latlng.equals(latlngs[0]))
  347. return 0.0;
  348. if (latlng.equals(latlngs[latlngs.length-1]))
  349. return 1.0;
  350. var point = L.GeometryUtil.closest(map, polyline, latlng, false),
  351. lengths = L.GeometryUtil.accumulatedLengths(latlngs),
  352. total_length = lengths[lengths.length-1],
  353. portion = 0,
  354. found = false;
  355. for (var i=0, n = latlngs.length-1; i < n; i++) {
  356. var l1 = latlngs[i],
  357. l2 = latlngs[i+1];
  358. portion = lengths[i];
  359. if (L.GeometryUtil.belongsSegment(point, l1, l2, 0.0001)) {
  360. portion += l1.distanceTo(point);
  361. found = true;
  362. break;
  363. }
  364. }
  365. if (!found) {
  366. throw "Could not interpolate " + latlng.toString() + " within " + polyline.toString();
  367. }
  368. return portion / total_length;
  369. },
  370. /** Returns a clone with reversed coordinates. @param {L.PolyLine} polyline polyline to reverse @returns {L.PolyLine} polyline reversed */
  371. reverse: function (polyline) {
  372. return L.polyline(polyline.getLatLngs().slice(0).reverse());
  373. },
  374. /** Returns a sub-part of the polyline, from start to end. If start is superior to end, returns extraction from inverted line. @param {L.Map} map map to be used for this method @param {L.PolyLine} polyline Polyline on which will be extracted the sub-part @param {Number} start ratio, expressed as a decimal between 0 and 1, inclusive @param {Number} end ratio, expressed as a decimal between 0 and 1, inclusive @returns {Array<L.LatLng>} new polyline */
  375. extract: function (map, polyline, start, end) {
  376. if (start > end) {
  377. return L.GeometryUtil.extract(map, L.GeometryUtil.reverse(polyline), 1.0-start, 1.0-end);
  378. }
  379. // Bound start and end to [0-1]
  380. start = Math.max(Math.min(start, 1), 0);
  381. end = Math.max(Math.min(end, 1), 0);
  382. var latlngs = polyline.getLatLngs(),
  383. startpoint = L.GeometryUtil.interpolateOnLine(map, polyline, start),
  384. endpoint = L.GeometryUtil.interpolateOnLine(map, polyline, end);
  385. // Return single point if start == end
  386. if (start == end) {
  387. var point = L.GeometryUtil.interpolateOnLine(map, polyline, end);
  388. return [point.latLng];
  389. }
  390. // Array.slice() works indexes at 0
  391. if (startpoint.predecessor == -1)
  392. startpoint.predecessor = 0;
  393. if (endpoint.predecessor == -1)
  394. endpoint.predecessor = 0;
  395. var result = latlngs.slice(startpoint.predecessor+1, endpoint.predecessor+1);
  396. result.unshift(startpoint.latLng);
  397. result.push(endpoint.latLng);
  398. return result;
  399. },
  400. /** Returns true if first polyline ends where other second starts. @param {L.PolyLine} polyline First polyline @param {L.PolyLine} other Second polyline @returns {bool} */
  401. isBefore: function (polyline, other) {
  402. if (!other) return false;
  403. var lla = polyline.getLatLngs(),
  404. llb = other.getLatLngs();
  405. return (lla[lla.length-1]).equals(llb[0]);
  406. },
  407. /** Returns true if first polyline starts where second ends. @param {L.PolyLine} polyline First polyline @param {L.PolyLine} other Second polyline @returns {bool} */
  408. isAfter: function (polyline, other) {
  409. if (!other) return false;
  410. var lla = polyline.getLatLngs(),
  411. llb = other.getLatLngs();
  412. return (lla[0]).equals(llb[llb.length-1]);
  413. },
  414. /** Returns true if first polyline starts where second ends or start. @param {L.PolyLine} polyline First polyline @param {L.PolyLine} other Second polyline @returns {bool} */
  415. startsAtExtremity: function (polyline, other) {
  416. if (!other) return false;
  417. var lla = polyline.getLatLngs(),
  418. llb = other.getLatLngs(),
  419. start = lla[0];
  420. return start.equals(llb[0]) || start.equals(llb[llb.length-1]);
  421. },
  422. /** Returns horizontal angle in degres between two points. @param {L.Point} a Coordinates of point A @param {L.Point} b Coordinates of point B @returns {Number} horizontal angle */
  423. computeAngle: function(a, b) {
  424. return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI);
  425. },
  426. /** Returns slope (Ax+B) between two points. @param {L.Point} a Coordinates of point A @param {L.Point} b Coordinates of point B @returns {Object} with ``a`` and ``b`` properties. */
  427. computeSlope: function(a, b) {
  428. var s = (b.y - a.y) / (b.x - a.x),
  429. o = a.y - (s * a.x);
  430. return { 'a': s, 'b': o};
  431. },
  432. /** Returns LatLng of rotated point around specified LatLng center. @param {L.LatLng} latlngPoint: point to rotate @param {double} angleDeg: angle to rotate in degrees @param {L.LatLng} latlngCenter: center of rotation @returns {L.LatLng} rotated point */
  433. rotatePoint: function(map, latlngPoint, angleDeg, latlngCenter) {
  434. var maxzoom = map.getMaxZoom();
  435. if (maxzoom === Infinity)
  436. maxzoom = map.getZoom();
  437. var angleRad = angleDeg*Math.PI/180,
  438. pPoint = map.project(latlngPoint, maxzoom),
  439. pCenter = map.project(latlngCenter, maxzoom),
  440. x2 = Math.cos(angleRad)*(pPoint.x-pCenter.x) - Math.sin(angleRad)*(pPoint.y-pCenter.y) + pCenter.x,
  441. y2 = Math.sin(angleRad)*(pPoint.x-pCenter.x) + Math.cos(angleRad)*(pPoint.y-pCenter.y) + pCenter.y;
  442. return map.unproject(new L.Point(x2,y2), maxzoom);
  443. },
  444. /** Returns the bearing in degrees clockwise from north (0 degrees) from the first L.LatLng to the second, at the first LatLng @param {L.LatLng} latlng1: origin point of the bearing @param {L.LatLng} latlng2: destination point of the bearing @returns {float} degrees clockwise from north. */
  445. bearing: function(latlng1, latlng2) {
  446. var rad = Math.PI / 180,
  447. lat1 = latlng1.lat * rad,
  448. lat2 = latlng2.lat * rad,
  449. lon1 = latlng1.lng * rad,
  450. lon2 = latlng2.lng * rad,
  451. y = Math.sin(lon2 - lon1) * Math.cos(lat2),
  452. x = Math.cos(lat1) * Math.sin(lat2) -
  453. Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
  454. var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360;
  455. return bearing >= 180 ? bearing-360 : bearing;
  456. },
  457. /** Returns the point that is a distance and heading away from the given origin point. @param {L.LatLng} latlng: origin point @param {float} heading: heading in degrees, clockwise from 0 degrees north. @param {float} distance: distance in meters @returns {L.latLng} the destination point. Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html for a great reference and examples. */
  458. destination: function(latlng, heading, distance) {
  459. heading = (heading + 360) % 360;
  460. var rad = Math.PI / 180,
  461. radInv = 180 / Math.PI,
  462. R = 6378137, // approximation of Earth's radius
  463. lon1 = latlng.lng * rad,
  464. lat1 = latlng.lat * rad,
  465. rheading = heading * rad,
  466. sinLat1 = Math.sin(lat1),
  467. cosLat1 = Math.cos(lat1),
  468. cosDistR = Math.cos(distance / R),
  469. sinDistR = Math.sin(distance / R),
  470. lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 *
  471. sinDistR * Math.cos(rheading)),
  472. lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR *
  473. cosLat1, cosDistR - sinLat1 * Math.sin(lat2));
  474. lon2 = lon2 * radInv;
  475. lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2;
  476. return L.latLng([lat2 * radInv, lon2]);
  477. },
  478. /** Returns the the angle of the given segment and the Equator in degrees, clockwise from 0 degrees north. @param {L.Map} map: map to be used for this method @param {L.LatLng} latlngA: geographical point A of the segment @param {L.LatLng} latlngB: geographical point B of the segment @returns {Float} the angle in degrees. */
  479. angle: function(map, latlngA, latlngB) {
  480. var pointA = map.latLngToContainerPoint(latlngA),
  481. pointB = map.latLngToContainerPoint(latlngB),
  482. angleDeg = Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180 / Math.PI + 90;
  483. angleDeg += angleDeg < 0 ? 360 : 0;
  484. return angleDeg;
  485. },
  486. /** Returns a point snaps on the segment and heading away from the given origin point a distance. @param {L.Map} map: map to be used for this method @param {L.LatLng} latlngA: geographical point A of the segment @param {L.LatLng} latlngB: geographical point B of the segment @param {float} distance: distance in meters @returns {L.latLng} the destination point. */
  487. destinationOnSegment: function(map, latlngA, latlngB, distance) {
  488. var angleDeg = L.GeometryUtil.angle(map, latlngA, latlngB),
  489. latlng = L.GeometryUtil.destination(latlngA, angleDeg, distance);
  490. return L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
  491. },
  492. });
  493. return L.GeometryUtil;
  494. }));

一个简单的demo,需要适当修改:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>路径轨迹回放</title>
  6. <meta charset="utf-8" />
  7. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  8. <!-- leaflet。js的基础依赖 -->
  9. <link rel="stylesheet" type="text/css" href="../leaflet/dist/leaflet.css">
  10. <script type="text/javascript" src="../leaflet/dist/leaflet.js"></script>
  11. <!-- 轨迹回放的自定义插件(依赖于leaflet。js) -->
  12. <script type="text/javascript" src="../customLib/markerTrack.js"></script>
  13. <script type="text/javascript" src="../customLib/em.geometryutil.js"></script>
  14. </head>
  15. <style> body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; } </style>
  16. <body>
  17. <div id='map'></div>
  18. <script type="text/javascript"> /* 这块代码是引入地图的,根据自己的项目来就好(我的项目里面是离线地图) */ // --start引入地图-- var url = 'http://localhost:9090/img/{z}/{x}/{y}.png'; // var url = '"http://webrd0{s}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8'; var map = L.map('map',{ center: [34.694,113.587], zoom: 6, zoomControl: false }); //将图层加载到地图上,并设置最大的聚焦还有map样式 L.tileLayer(url, { maxZoom: 18, minZoom: 3 }).addTo(map); // --end引入地图-- /** * 开始轨迹回放的代码 */ // 运动的轨迹 var polyline = L.polyline([[34.694,113.587],[38.694,113.587],[37.694,115.587]],{ color: 'red'}).addTo(map); // 轨迹运动的图标 var m = L.marker([34.694,113.587],{ icon:L.icon({ iconUrl:'img/run.png', iconAnchor: [25, 15], })}).addTo(map); //添加一条线段也记录已经路过的点 var passed=L.polyline([[]],{ color:'yellow'}).addTo(map); m.on('update_position',function (e) { //每次坐标更新。然后也更新路径 passed.setLatLngs(e.path); }); // 开始运动 m.moveAlong(polyline,300000); </script>
  19. </body>
  20. </html>

别忘点赞!

发表评论

表情:
评论列表 (有 0 条评论,123人围观)

还没有评论,来说两句吧...

相关阅读