使用 GPS 数据找出特定区域的用户

Find out if the user in specific region using GPS data

我想知道特定地区的用户是否使用GPS数据并考虑准确信息以减少错误,因为如果用户肯定不在该地区,程序会提示警报。

GPS 传感器 return 纬度、经度和精度(以米为单位),我可以使用这些数据画一个圆:

135.500908,34.661964,30.0

有坐标数组依次表示特定区域:

135.500350,34.667011
135.506101,34.666853
135.505972,34.663076
135.505135,34.663111
135.504942,34.662387
135.504084,34.662440
135.504062,34.663146
135.502968,34.663217
135.502689,34.663764
135.502431,34.664205
135.502110,34.664646
135.501680,34.665105
135.501509,34.665246
135.500844,34.665229
135.500371,34.665511

我的想法是通过使用线-圆碰撞检测算法来找出多边形与圆是否有任何碰撞,但它在我的代码中看起来有问题,而且由于以下原因我似乎无法直接使用该信息radius/degree, 谁能帮帮我?或者让我知道是否有更简单的解决方案?

    public static boolean possiblyInside(List<Double> arrayX, List<Double> arrayY, double locationX, double locationY, double locationAccuracy) {
    if (arrayX.size() != arrayY.size()) {
        throw new IllegalArgumentException("Array length not equal");
    }

    boolean anyCircleLineIntersection = false;
    if (arrayX.size() > 1) {
        for (int i = 0; i < arrayX.size(); i++) {
            double p1x = i == 0 ? arrayX.get(arrayX.size() - 1) : arrayX.get(i - 1);
            double p1y = i == 0 ? arrayY.get(arrayY.size() - 1) : arrayY.get(i - 1);
            double p2x = arrayX.get(i);
            double p2y = arrayY.get(i);

            if (circleLineIntersection(p1x, p1y, p2x, p2y, locationX, locationY, locationAccuracy)) {
                anyCircleLineIntersection = true;
                break;
            }
        }
    }

    return anyCircleLineIntersection;
}

private static boolean circleLineIntersection(double p1X, double p1Y, double p2X, double p2Y, double centerX, double centerY, double locationAccuracy) {
    double rad = (180 / Math.PI);
    double r = (locationAccuracy / 1000);

    p1X = p1X * rad;
    p1Y = p1Y * rad;
    p2X = p2X * rad;
    p2Y = p2Y * rad;
    centerX = centerX * rad;
    centerY = centerY * rad;

    // Transform to local coordinates
    double localP1X = p1X - centerX;
    double localP1Y = p1Y - centerY;
    double localP2X = p2X - centerX;
    double localP2Y = p2Y - centerY;

    // Pre-calculate this value. We use it often
    double pDiffX = localP2X - localP1X;
    double pDiffY = localP2Y - localP1Y;

    double a = (pDiffX) * (pDiffX) + (pDiffY) * (pDiffY);
    double b = 2 * ((pDiffX * localP1X) + (pDiffY * localP1Y));
    double c = (localP1X * localP1X) + (localP1Y * localP1Y) - (r * r);

    double delta = b * b - (4 * a * c);
    return delta >= 0.0;
}

有一种方法叫做Geofencing。 Google 已经为您提供了这样的功能。而且您不必处理所有这些复杂的计算。

您可以在用户进入特定区域/离开特定区域/在特定区域停留一段时间后触发事件。或者你可以做出不同的组合。

Here 是一篇介绍如何使用地理围栏的文章。它由 4 篇独立的文章组成。

感谢 Todor Kostov 的回答。

我知道 Android 提供了地理围栏 API,但由于其实现和限制,它并不完全适合我的情况,我想将算法与 [=42] 同步=] 版本应用程序也是如此。 (连我都知道算法不如iOS或Android提供的好,而且看起来也有点傻)。

最后我是这样解决问题的:

  1. 确保当前位置不在多边形内(使用点在多边形算法)

  2. 遍历区域多边形的所有线段,找出 最近坐标(PointA)到当前位置(PointB)

  3. 计算PointA和PointB之间的距离,换算成米(X)

  4. 如果 X > 位置精度(也以米为单位),则用户肯定不在特定区域

p.s。我不擅长数学和地理定位,如有错误请指出

public static boolean possiblyInside(List<Double> arrayX, List<Double> arrayY, double locationX, double locationY, double locationAccuracy) {
    if (arrayX.size() != arrayY.size()) {
        throw new IllegalArgumentException("Array length not equal");
    }

    if (arrayX.size() < 3) {
        return false;
    }

    double minimumDistance = Double.MAX_VALUE;
    for (int i = 0; i < arrayX.size(); i++) {
        double p1x = i == 0 ? arrayX.get(arrayX.size() - 1) : arrayX.get(i - 1);
        double p1y = i == 0 ? arrayY.get(arrayY.size() - 1) : arrayY.get(i - 1);
        double p2x = arrayX.get(i);
        double p2y = arrayY.get(i);

        Coordinate closest = getClosestPointOnLine(p1x, p1y, p2x, p2y, locationX, locationY);
        double currentDistance = distanceMeterBetweenPoints(closest.latitude, closest.longitude, locationX, locationY);
        if (currentDistance < minimumDistance) {
            minimumDistance = currentDistance;
        }
    }

    return (minimumDistance <= locationAccuracy);
}

private static Coordinate getClosestPointOnLine(double sx1, double sy1, double sx2, double sy2, double px, double py) {
    double xDelta = sx2 - sx1;
    double yDelta = sy2 - sy1;

    if ((xDelta == 0) && (yDelta == 0)) {
        throw new IllegalArgumentException("Line start equals line end");
    }

    double u = ((px - sx1) * xDelta + (py - sy1) * yDelta) / (xDelta * xDelta + yDelta * yDelta);

    final Coordinate closestPoint;
    if (u < 0.0) {
        closestPoint = new Coordinate(sx1, sy1);
    } else if (u > 1.0) {
        closestPoint = new Coordinate(sx2, sy2);
    } else  {
        closestPoint = new Coordinate((int) Math.round(sx1 + u * xDelta), (int) Math.round(sy1 + u * yDelta));
    }

    return closestPoint;
}

public static double distanceMeterBetweenPoints(double aX, double aY, double bX, double bY) {
    double rad = Math.PI / 180;
    int r = 6371;

    double dLat = (aX - bX) * rad;
    double dLng = (aY - bY) * rad;

    double x = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(aX * rad) * Math.cos(bX * rad) * Math.pow(Math.sin(dLng / 2), 2);
    double y = 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x));

    return r * y * 1000;
}