【问题标题】:Calculating points for bezier curve for map计算地图贝塞尔曲线的点
【发布时间】:2013-04-17 09:59:34
【问题描述】:

我正在计算与地理位置点之间的贝塞尔曲线所需的点,以便在 Google Maps V2 上绘制线。我的问题是,如果必须在“太平洋上”画线,它就不能正常工作,例如起点在东京,终点在温哥华。这些点的计算方向错误(地球周围的东边)而不是西边。横跨大西洋或亚洲的位置点被计算和绘制正确。

我的代码或我的想法中的错误在哪里?

计算代码如下:

public static ArrayList<LatLng> bezier(LatLng p1, LatLng p2, double arcHeight, double skew, boolean up){
    ArrayList<LatLng> list = new ArrayList<LatLng>();
    try {
        if(p1.longitude > p2.longitude){
            LatLng tmp = p1;
            p1 = p2;
            p2 = tmp;
        }

        LatLng c = midPoint(p1, p2, 0);
        Log.v(TAG, "P1: " + p1.toString());
        Log.v(TAG, "P2: " + p2.toString());
        Log.v(TAG, "C: " + c.toString());

        double cLat = c.latitude;
        double cLon = c.longitude;


         //add skew and arcHeight to move the midPoint
        if(Math.abs(p1.longitude - p2.longitude) < 0.0001){
            if(up){
                cLon -= arcHeight;
            }else{
                cLon += arcHeight;
                cLat += skew;
            }
        }else{
            if(up){
                cLat += arcHeight;
            }else{
                cLat -= arcHeight;
                cLon += skew;
            }
        }

        list.add(p1);
         //calculating points for bezier
        double tDelta = 1.0/10;
        for (double t = 0;  t <= 1.0; t+=tDelta) {
            double oneMinusT = (1.0-t);
            double t2 = Math.pow(t, 2);
            double lon = oneMinusT * oneMinusT * p1.longitude
                        + 2 * oneMinusT * t * cLon
                        + t2 * p2.longitude;
            double lat = oneMinusT * oneMinusT * p1.latitude
                        + 2 * oneMinusT * t * cLat
                         + t2 * p2.latitude;
            Log.v(TAG, "t: " + t + "[" + lat +"|" + lon + "]");
            list.add(new LatLng(lat, lon));
        }

        list.add(p2);
    } catch (Exception e) {
        Log.e(TAG, "bezier", e);
    }
    return list;
}

这里是 logcat 的输出和计算的点;

P1: lat/lng: (35.76472,140.38639)
P2: lat/lng: (49.19489,-123.17923)
C: lat/lng: (53.760800330485814,-178.27615766444313)
t: 0.0[35.76472|140.38639]
t: 0.1[39.17431615948745|80.39147522040025]
t: 0.2[42.12467250575547|27.871749947378213]
t: 0.3[44.61578903880404|-17.172785819066128]
t: 0.4[46.647665758633195|-54.7421320789327]
t: 0.5[48.22030266524291|-84.83628883222157]
t: 0.6[49.333699758633195|-107.4552560789327]
t: 0.7[49.98785703880404|-122.59903381906611]
t: 0.7[50.18277450575546|-130.2676220526218]
t: 0.8[49.918452159487444|-130.46102077959978]
t: 0.9[49.19489|-123.17923000000002]

这是地图的截图:

【问题讨论】:

    标签: android google-maps google-maps-android-api-2 bezier android-maps-v2


    【解决方案1】:

    我决定将地理位置转换为cartesian coordinate system,然后进行计算。这行得通。

    以下是更改:

    //inside bezier(...)
    
    CartesianCoordinates cart1 = new CartesianCoordinates(p1);
    CartesianCoordinates cart2 = new CartesianCoordinates(p2);
    CartesianCoordinates cart3 = new CartesianCoordinates(cLat, cLon);
    
    for (double t = 0; t <= 1.0; t += tDelta) {
        double oneMinusT = (1.0 - t);
        double t2 = Math.pow(t, 2);
    
        double y = oneMinusT * oneMinusT * cart1.y + 2 * t * oneMinusT * cart3.y + t2 * cart2.y;
        double x = oneMinusT * oneMinusT * cart1.x + 2 * t * oneMinusT * cart3.x + t2 * cart2.x;
        double z = oneMinusT * oneMinusT * cart1.z + 2 * t * oneMinusT * cart3.z + t2 * cart2.z;
        LatLng control = CartesianCoordinates.toLatLng(x, y, z);
        if (Config.DEBUG) 
            Log.v(TAG, "t: " + t + control.toString());
        list.add(control);
    }
    

    使用笛卡尔坐标:

    private static class CartesianCoordinates {
    private static final int R = 6371; // approximate radius of earth
    double x;
    double y;
    double z;
    
    public CartesianCoordinates(LatLng p) {
        this(p.latitude, p.longitude);
    }
    
    public CartesianCoordinates(double lat, double lon) {
        double _lat = Math.toRadians(lat);
        double _lon = Math.toRadians(lon);
    
        x = R * Math.cos(_lat) * Math.cos(_lon);
        y = R * Math.cos(_lat) * Math.sin(_lon);
        z = R * Math.sin(_lat);
    }
    
    public static LatLng toLatLng(double x, double y, double z){
            return new LatLng(Math.toDegrees(Math.asin(z / R)), Math.toDegrees(Math.atan2(y, x)));
        }
    }
    

    计算两个坐标中点的方法(可能不是100%完美的数学正确):

    private static LatLng midPoint(LatLng p1, LatLng p2) throws IllegalArgumentException{
    
        if(p1 == null || p2 == null)
            throw new IllegalArgumentException("two points are needed for calculation");
    
        double lat1;
        double lon1;
        double lat2;
        double lon2;
    
        //convert to radians
        lat1 = Math.toRadians(p1.latitude);
        lon1 = Math.toRadians(p1.longitude);
        lat2 = Math.toRadians(p2.latitude);
        lon2 = Math.toRadians(p2.longitude);
    
        double x1 = Math.cos(lat1) * Math.cos(lon1);
        double y1 = Math.cos(lat1) * Math.sin(lon1);
        double z1 = Math.sin(lat1);
    
        double x2 = Math.cos(lat2) * Math.cos(lon2);
        double y2 = Math.cos(lat2) * Math.sin(lon2);
        double z2 = Math.sin(lat2);
    
        double x = (x1 + x2)/2;
        double y = (y1 + y2)/2;
        double z = (z1 + z2)/2;
    
        double lon = Math.atan2(y, x);
        double hyp = Math.sqrt(x*x + y*y);
    
        // HACK: 0.9 and 1.1 was found by trial and error; this is probably *not* the right place to apply mid point shifting
        double lat = Math.atan2(.9*z, hyp); 
        if(lat>0) lat = Math.atan2(1.1*z, hyp);
    
        if(Config.DEBUG)
            Log.v(TAG, Math.toDegrees(lat) + " " + Math.toDegrees(lon));
    
        return new LatLng(Math.toDegrees(lat),  Math.toDegrees(lon));
    }
    

    【讨论】:

    • 您可能希望将此设置为正确答案,这样其他人就不会考虑尝试回答了。
    • 您介意提供 midPoint 方法吗?我正在尝试实现非常接近此的目标:)
    猜你喜欢
    • 2011-08-03
    • 1970-01-01
    • 2016-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多