了解特定区域的用户是否使用GPS数据

时间:2017-01-03 09:27:39

标签: java android gps geolocation collision-detection

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

GPS传感器返回纬度,经度和精度(以米为单位),我可以使用这些数据绘制一个圆圈:

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

序列中的坐标数组代表特定区域:

    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;
}

我的想法是通过使用线圆碰撞检测算法找出多边形与圆圈是否有任何碰撞,但是我的代码看起来有些不对劲,而且由于radius /我似乎无法直接使用该信息学位,任何人都可以帮助我吗?或者让我知道是否有更简单的解决方案?

__attribute__((constructor))
__attribute__((destructor))
__attribute__((constructor (PRIORITY)))
__attribute__((destructor (PRIORITY)))

2 个答案:

答案 0 :(得分:0)

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

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

Here是一篇关于如何使用Geofencing的文章。它由4个独立的文章组成。

答案 1 :(得分:0)

感谢Todor Kostov的回答。

我知道Android提供了Geofencing API,但由于其实现和限制,它不适合我的情况,我也希望将该算法与iOS版本应用同步。 (即使我知道算法并不像iOS或Android那样好,而且看起来也有些愚蠢)。

最后,我用这种方式解决了问题:

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

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

  3. 计算PointA和PointB之间的距离,将其转换为米(X)

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

  5. 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;
    }