【问题标题】:How to get buffer polygon coordinates (latitude and longitude) in iOS?如何在 iOS 中获取缓冲区多边形坐标(纬度和经度)?
【发布时间】:2016-09-25 21:01:46
【问题描述】:

我在地图上用一组坐标创建了一个多边形。 我需要帮助制作一个在原始多边形边界之外具有给定距离的缓冲多边形。

所以我需要一种使用这种算法的方法,其中我将一组坐标作为输入传递,并且应该得到一组缓冲的坐标作为输出。

我尝试通过使用 ios 的 arcgis 库和 AGSGeometryEngine 的 bufferGeometry 方法来实现这一点,但问题是,这是紧密耦合的,只能使用他们的 GIS 地图,但我使用的是不同地图的 Mapbox。所以我想要一种通用方法,它可以独立于地图解决我的问题。

【问题讨论】:

  • 用你迄今为止所做的和你尝试过的来更新你的问题。说明您确切需要什么帮助。
  • @rmaddy:在大多数不同的 Map API 中都有用于绘制多边形的方法,我们只需传递纬度和经度坐标数组,其余部分由这些函数处理,但在这里我需要一个将执行考虑给出其缓冲坐标的坐标的通用算法。我在 Mapbox APIs 中没有找到。

标签: ios swift coordinates polygon arcgis


【解决方案1】:

我在我的应用程序中遇到了同样的问题,终于在this site的帮助下找到了解决方案

我是一名安卓开发者,我的代码可能对你没用,但核心概念是一样的。

  1. 首先我们需要借助两个点 LatLng 点找到线的方位。(我已经使用 computeDistanceAndBearing(double lat1, double lon1,double lat2, double lon2)函数)

  1. 现在要获得某个点的缓冲,我们需要给出缓冲距离、LatLng 点和方位(我从 computeDistanceAndBearing 函数获得)。(我已经使用 computeDestinationAndBearing (double lat1, double lon1,double brng, double dist) 函数)。从单个 LatLng 点,我们通过以一定距离产生它们的方位来获得两个点。

  1. 现在我们需要找到两个点的兴趣点来获得我们想要的缓冲。为此,请记住采用另一条线的新获取点和方位,并与另一条线相同。这有助于获得具有所需缓冲的新交点。(我已在我的函数 computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2) 中完成此操作)

  1. 对所有多边形点执行此操作,然后获得新的点,将它们连接起来以进行缓冲。

这是我在我的 android 定位应用程序中所做的方式,即

这是我的代码 //computeDistanceAndBearing(double lat1, double lon1, 双 lat2, 双 lon2)

public static double[] computeDistanceAndBearing(double lat1, double lon1,
                                                 double lat2, double lon2) {
    // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
    // using the "Inverse Formula" (section 4)
    double results[] = new double[3];
    int MAXITERS = 20;
    // Convert lat/long to radians
    lat1 *= Math.PI / 180.0;
    lat2 *= Math.PI / 180.0;
    lon1 *= Math.PI / 180.0;
    lon2 *= Math.PI / 180.0;

    double a = 6378137.0; // WGS84 major axis
    double b = 6356752.3142; // WGS84 semi-major axis
    double f = (a - b) / a;
    double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);

    double L = lon2 - lon1;
    double A = 0.0;
    double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
    double U2 = Math.atan((1.0 - f) * Math.tan(lat2));

    double cosU1 = Math.cos(U1);
    double cosU2 = Math.cos(U2);
    double sinU1 = Math.sin(U1);
    double sinU2 = Math.sin(U2);
    double cosU1cosU2 = cosU1 * cosU2;
    double sinU1sinU2 = sinU1 * sinU2;

    double sigma = 0.0;
    double deltaSigma = 0.0;
    double cosSqAlpha = 0.0;
    double cos2SM = 0.0;
    double cosSigma = 0.0;
    double sinSigma = 0.0;
    double cosLambda = 0.0;
    double sinLambda = 0.0;

    double lambda = L; // initial guess
    for (int iter = 0; iter < MAXITERS; iter++) {
        double lambdaOrig = lambda;
        cosLambda = Math.cos(lambda);
        sinLambda = Math.sin(lambda);
        double t1 = cosU2 * sinLambda;
        double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
        double sinSqSigma = t1 * t1 + t2 * t2; // (14)
        sinSigma = Math.sqrt(sinSqSigma);
        cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
        sigma = Math.atan2(sinSigma, cosSigma); // (16)
        double sinAlpha = (sinSigma == 0) ? 0.0 : cosU1cosU2 * sinLambda
                / sinSigma; // (17)
        cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
        cos2SM = (cosSqAlpha == 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2
                / cosSqAlpha; // (18)

        double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
        A = 1 + (uSquared / 16384.0) * // (3)
                (4096.0 + uSquared * (-768 + uSquared * (320.0 - 175.0 * uSquared)));
        double B = (uSquared / 1024.0) * // (4)
                (256.0 + uSquared * (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
        double C = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
        double cos2SMSq = cos2SM * cos2SM;
        deltaSigma = B
                * sinSigma
                * // (6)
                (cos2SM + (B / 4.0)
                        * (cosSigma * (-1.0 + 2.0 * cos2SMSq) - (B / 6.0) * cos2SM
                        * (-3.0 + 4.0 * sinSigma * sinSigma)
                        * (-3.0 + 4.0 * cos2SMSq)));

        lambda = L
                + (1.0 - C)
                * f
                * sinAlpha
                * (sigma + C * sinSigma
                * (cos2SM + C * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)

        double delta = (lambda - lambdaOrig) / lambda;
        if (Math.abs(delta) < 1.0e-12) {
            break;
        }
    }

    double distance = (b * A * (sigma - deltaSigma));
    results[0] = distance;
    if (results.length > 1) {
        double initialBearing = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2
                - sinU1 * cosU2 * cosLambda);
        initialBearing *= 180.0 / Math.PI;
        results[1] = initialBearing;
        if (results.length > 2) {
            double finalBearing = Math.atan2(cosU1 * sinLambda, -sinU1 * cosU2
                    + cosU1 * sinU2 * cosLambda);
            finalBearing *= 180.0 / Math.PI;
            results[2] = finalBearing;
        }
    }

    return results;
}

//computeDestinationAndBearing(double lat1, double lon1,double brng, double dist)

public static double[] computeDestinationAndBearing(double lat1, double lon1,
                                                    double brng, double dist) {
    double results[] = new double[3];
    double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
    // ellipsiod
    double s = dist;
    double alpha1 = toRad(brng);
    double sinAlpha1 = Math.sin(alpha1);
    double cosAlpha1 = Math.cos(alpha1);

    double tanU1 = (1 - f) * Math.tan(toRad(lat1));
    double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
    double sigma1 = Math.atan2(tanU1, cosAlpha1);
    double sinAlpha = cosU1 * sinAlpha1;
    double cosSqAlpha = 1 - sinAlpha * sinAlpha;
    double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
    double A = 1 + uSq / 16384
            * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
    double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
    double sinSigma = 0, cosSigma = 0, deltaSigma = 0, cos2SigmaM = 0;
    double sigma = s / (b * A), sigmaP = 2 * Math.PI;

    while (Math.abs(sigma - sigmaP) > 1e-12) {
        cos2SigmaM = Math.cos(2 * sigma1 + sigma);
        sinSigma = Math.sin(sigma);
        cosSigma = Math.cos(sigma);
        deltaSigma = B
                * sinSigma
                * (cos2SigmaM + B
                / 4
                * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6
                * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma)
                * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
        sigmaP = sigma;
        sigma = s / (b * A) + deltaSigma;
    }

    double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
    double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
            (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
    double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1
            * sinSigma * cosAlpha1);
    double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
    double L = lambda
            - (1 - C)
            * f
            * sinAlpha
            * (sigma + C * sinSigma
            * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
    double lon2 = (toRad(lon1) + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise
    // to
    // -180...+180

    double revAz = Math.atan2(sinAlpha, -tmp); // final bearing, if required

    results[0] = toDegrees(lat2);
    results[1] = toDegrees(lon2);
    results[2] = toDegrees(revAz);
    return results;
}

private static double toRad(double angle) {
    return angle * Math.PI / 180;
}

private static double toDegrees(double radians) {
    return radians * 180 / Math.PI;
}

//computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2)

    public static LatLng computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2) {
    double lat1 = toRad(p1.latitude), lng1 = toRad(p1.longitude);
    double lat2 = toRad(p2.latitude), lng2 = toRad(p2.longitude);
    double brng13 = toRad(brng1), brng23 = toRad(brng2);
    double dlat = lat2 - lat1, dlng = lng2 - lng1;
    double delta12 = 2 * Math.asin(Math.sqrt(Math.sin(dlat / 2) * Math.sin(dlat / 2)
            + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlng / 2) * Math.sin(dlng / 2)));

    if (delta12 == 0) return null;


    double initBrng1 = Math.acos((Math.sin(lat2) - Math.sin(lat1) * Math.cos(delta12)) / (Math.sin(delta12) * Math.cos(lat1)));

    double initBrng2 = Math.acos((Math.sin(lat1) - Math.sin(lat2) * Math.cos(delta12)) / (Math.sin(delta12) * Math.cos(lat2)));

    double brng12 = Math.sin(lng2 - lng1) > 0 ? initBrng1 : 2 * Math.PI - initBrng1;
    double brng21 = Math.sin(lng2 - lng1) > 0 ? 2 * Math.PI - initBrng2 : initBrng2;


    double alpha1 = (brng13 - brng12 + Math.PI) % (2 * Math.PI) - Math.PI; 
    double alpha2 = (brng21 - brng23 + Math.PI) % (2 * Math.PI) - Math.PI; 

    double alpha3 = Math.acos(-Math.cos(alpha1) * Math.cos(alpha2) + Math.sin(alpha1) * Math.sin(alpha2) * Math.cos(delta12));
    double delta13 = Math.atan2(Math.sin(delta12) * Math.sin(alpha1) * Math.sin(alpha2), Math.cos(alpha2) + Math.cos(alpha1) * Math.cos(alpha3));
    double lat3 = Math.asin(Math.sin(lat1) * Math.cos(delta13) + Math.cos(lat1) * Math.sin(delta13) * Math.cos(brng13));
    double dlng13 = Math.atan2(Math.sin(brng13) * Math.sin(delta13) * Math.cos(lat1), Math.cos(delta13) - Math.sin(lat1) * Math.sin(lat3));
    double lng3 = lng1 + dlng13;

    return new LatLng(toDegrees(lat3), (toDegrees(lng3) + 540) % 360 - 180); 
}

我会建议您浏览上述网站并获得知识,因为我也做过同样的事情。

希望这可能会有所帮助,我知道它不在 ios 中,但概念与我通过更改 javascript 代码完成我的项目相同。

干杯!!!

【讨论】:

  • 嘿 Ravikant,我正在尝试使用此解决方案,但有点卡在第三点。您能否提供有关在函数 computeIntersectionPoint 中传递什么的更多详细信息。
  • 在这种情况下,computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2) 在这种情况下,您可以借助两条生产线找到交点。 - 得到绿线和红线交点的纬度,得到 2 latlng 1 n 2。 - 两点 1 n 2 的交点并使用函数。就是这样。
  • 感谢您的回复,我对这个轴承概念有点陌生。我仍然不明白我必须在 computeIntersectionPoint 函数中传递什么。我可以向您展示我是如何尝试实现这一点的。你能分享你的 emil id 吗,或者发邮件给我rana.ranvijay@weboniselab.com
【解决方案2】:

我的要求与此类似。我最终为此编写了自己的算法。 https://github.com/RanaRanvijaySingh/PolygonBuffer
你只需要使用这条线

double distance = 0.0001;
List bufferedPolygonList = AreaBuffer.buffer(pointList, distance);

它为您提供距原始多边形给定距离的缓冲多边形点列表。

【讨论】:

    【解决方案3】:

    我建议使用 Turf.js 库进行缓冲和许多基本的 gis 操作。您将能够从返回的路径中检索每条边。对于几何缓冲区,它易于使用,重量很轻,并且对于我使用 MapBox.js 或传单的应用程序来说没有任何问题。
    更多详情:Turf.js Buffer

    但是,如果您正在寻找可能有问题的测地线距离缓冲区。我会使用 Arcgis Javascript API

    【讨论】:

    • 你可以在iOS上体面地使用JS,无论如何这个问题清楚地询问了iOS。
    【解决方案4】:

    看看BOOST 这是一个大型 C++ 库,您可以找到几乎所有内容的库/源代码,例如斜接、圆形、方形等不同类型的缓冲方法。

    只需安装最新版本的 Boost,我猜现在是 1.58.0,然后看看 BOOST/Geometry/Strategies/Cartesian/buffer[Something]-Square/Miter/Round

    这是一个很好的document

    您需要将大地坐标 (lat/long) 转换为笛卡尔 (x/y) 并使用 Boost 库并反转转换。您根本不需要使用 ArcGIS 或任何其他 GIS 库。

    【讨论】:

    • 问题被标记为 swift。不客观 c
    • @Ccr 没关系,在 Swift 和 Objective-C 中使用 C++ 库非常普遍,在做大型项目时不可避免。并且为了记录,它不是 Objective-C,它是一个 C++ 库。
    • @zich 正如 Ccr 正确指出的那样,您不能(轻松地..)在 iOS 上使用 C++ 绘图方法。正确的方法是 CoreGraphics 或类似的。
    猜你喜欢
    • 2018-08-27
    • 2013-01-16
    • 2015-02-18
    • 2016-07-03
    • 1970-01-01
    • 1970-01-01
    • 2015-02-27
    • 2020-05-04
    • 1970-01-01
    相关资源
    最近更新 更多