使用ngCordova制作"aroundMe"样式的动态方向箭头

37

我希望创建与我在移动应用程序AroundMe Mobile App中看到的完全相同的指南针/箭头,它根据我的手机位置准确地指向地图上的一个点,并在我移动手机时更新箭头。

我非常烦恼,无法确切理解如何做到这一点,也找不到任何解释一点的指南或教程。

我在网上找到了一个方位函数,并创建了一个指令来围绕它进行操作:

app.directive('arrow', function () {

    function bearing(lat1, lng1, lat2, lng2) {
      var dLon = (lng2 - lng1);
      var y = Math.sin(dLon) * Math.cos(lat2);
      var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
      var rad = Math.atan2(y, x);
      var brng = toDeg(rad);
      return (brng + 360) % 360;
    }

    function toRad(deg) {
      return deg * Math.PI / 180;
    }

    function toDeg(rad) {
      return rad * 180 / Math.PI;
    }

    return {
    restrict: 'E',
    link: function (scope, element, attrs) {
      var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng);
      element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
    }
  };

});

看起来箭头朝着一个方向更新了,但不幸的是这不是正确的方向,因为它没有使用移动设备的 magneticHeading 位置进行计算。

所以我添加了 ngCordova 插件用于设备方向,以获取 magneticHeading ,现在我不确定应该如何在 bearing 函数中使用它。

  $cordovaDeviceOrientation.getCurrentHeading().then(function(result) {
    var magneticHeading = result.magneticHeading;
    var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng, magneticHeading);
    element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
  });

我尝试将其添加到返回语句中:

return (brng - heading) % 360;
或者:
return (heading - ((brng + 360) % 360));

使用监视器实现此代码时,我看到箭头移动了,但位置不准确...例如,从我的位置和引脚指向的位置上,箭头应该指向N,但它指向E

我一直在网上搜索,但找不到任何教程/问题来查找lat/lng点magnetingHeading之间的方位角。

也许我接近解决方案,但我无法独自前进。

我还试图搜索数学公式,但即使理解和实现它也是非常困难的。

希望您能帮助。


我不知道这个 plunker 是否对您有所帮助:http://plnkr.co/edit/WC9Kspe54tlod5EOpndD - beaver
@beaver 感谢您的评论。它仅使用纬度和经度,而不是使用移动位置... :( - Ayeye Brazo
也许这篇帖子可以帮到你... 或者这篇 - beaver
@AyeyeBrazo,您可能更喜欢这个链接,并希望您能找到解决方案:https://forum.ionicframework.com/t/compass-rotation-with-ionic/14517 - N.Raval
navigator.geolocation.watchPosition(function(position){ var heading = position.coords.heading;}); - StackSlave
当它总是指向E时,为什么不像return (brng - heading - 90) % 360;那样减去90度呢? - coyer
1个回答

1
很难对这个问题给出简单的答案,因为很多取决于实际的图形表示。例如,在 rotate(0deg) 的方向上指向哪里。
我可以解释你找到的公式,这可能有助于你自己解决问题。困难的部分如下:
  var dLon = (lng2 - lng1);
  var y = Math.sin(dLon) * Math.cos(lat2);
  var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
  var rad = Math.atan2(y, x);

这里展示的是Haversine公式(https://en.wikipedia.org/wiki/Haversine_formula)。通常情况下,我们可以使用两组坐标计算它们之间的角度。但是,当涉及到纬度和经度时,这种方法行不通,因为地球不是一个平面。前三行是Haversine公式,结果(xy)是单位圆(https://en.wikipedia.org/wiki/Unit_circle)上的坐标。
接下来要计算的是从该单位圆上的点到中心的角度。我们可以使用反正切来完成。Javascript有一个辅助函数atan2,可以简化此过程。结果就是您的位置指向圆心的角度。换句话说,是您的位置朝向感兴趣的点。结果以弧度为单位,需要转换为度数。(https://en.wikipedia.org/wiki/Atan2)
在平面坐标系中,简化后的版本如下:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));

bearingElement.css('transform', 'rotate(' + rotation + 'deg)');

其中poi是兴趣点,you是你的位置。

为了补偿您自己的旋转,您需要减去自己的旋转。在上面的示例中,结果将变为:

var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));

rotation -= you.rotation;

bearingElement.css('transform', 'rotate(' + rotation + 'deg)');

我已经在 Plunckr 上创建了一个示例,您可以看到一个简单的平面坐标系。您可以移动和旋转 youpoint of interest。'you' 内部的箭头将始终指向 poi,即使您旋转了 'you'。这是因为我们会补偿自己的旋转。

https://plnkr.co/edit/OJBGWsdcWp3nAkPk4lpC?p=preview

请在Plunckr中注意,“零”位置始终位于北部。检查您的应用程序以查看您的“零”位置。相应地进行调整,您的脚本将正常工作。
希望这可以帮助您 :-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接