在谷歌地图中绘制弧线折线

12

如何在Google地图上绘制弧形折线?

我已经使用这段代码创建了曲线折线。

以下是绘制曲线折线的方法

private void showCurvedPolyline (LatLng p1, LatLng p2, double k) {
    //Calculate distance and heading between two points
    double d = SphericalUtil.computeDistanceBetween(p1,p2);
    double h = SphericalUtil.computeHeading(p1, p2);

    //Midpoint position
    LatLng p = SphericalUtil.computeOffset(p1, d*0.5, h);

    //Apply some mathematics to calculate position of the circle center
    double x = (1-k*k)*d*0.5/(2*k);
    double r = (1+k*k)*d*0.5/(2*k);

    LatLng c = SphericalUtil.computeOffset(p, x, h + 90.0);

    //Polyline options
    PolylineOptions options = new PolylineOptions();
    List<PatternItem> pattern = Arrays.<PatternItem>asList(new Dash(30), new Gap(20));

    //Calculate heading between circle center and two points
    double h1 = SphericalUtil.computeHeading(c, p1);
    double h2 = SphericalUtil.computeHeading(c, p2);

    //Calculate positions of points on circle border and add them to polyline options
    int numpoints = 100;
    double step = (h2 -h1) / numpoints;

    for (int i=0; i < numpoints; i++) {
        LatLng pi = SphericalUtil.computeOffset(c, r, h1 + i * step);
        options.add(pi);
    }

    //Draw polyline
    mMap.addPolyline(options.width(10).color(Color.MAGENTA).geodesic(false).pattern(pattern));
}

输出

1. 如果我使用 this.showCurvedPolyline(latLng1, latLng2, 0.1);,那么会得到:

enter image description here

正如您在上面的图像中所看到的,我们非常接近达到我们的目标,但不知道为什么它无法连接到另一个端点。

2. 如果我使用this.showCurvedPolyline(latLng1, latLng2, 1);,然后得到:

enter image description here

3. 如果我使用LatLng c = SphericalUtil.computeOffset(p, x, h - 90.0);,那么得到的是什么:

enter image description here

注意: 我不想要这么大的圆形,真的我不想要那么高。

以下是我想要的弧形,如下图所示。

enter image description here

这是我用来在两个 地理位置 之间添加曲线折线的 代码

private void addCurvedPolyLine() {

        LatLng latLng1 = new LatLng(40.7128, 74.0059); // New York
        LatLng latLng2 = new LatLng(51.5074, 0.1278); // London

        Marker marker1 = mMap.addMarker(new MarkerOptions().position(latLng1).title("Start"));
        Marker marker2 = mMap.addMarker(new MarkerOptions().position(latLng2).title("End"));

        LatLngBounds.Builder builder = new LatLngBounds.Builder();

        builder.include(marker1.getPosition());
        builder.include(marker2.getPosition());

        LatLngBounds bounds = builder.build();
        int padding = 0; // offset from edges of the map in pixels
        CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);
        mMap.moveCamera(cu);
        mMap.animateCamera(cu);

        this.showCurvedPolyline(latLng1, latLng2, 0.1);

    }

他提供了一个解决方案,使用k=1而不是k==1,可以在此处查看他的快速修复中的第二个问题:https://dev59.com/zqDia4cB1Zd3GeqPIsDp#46398634。 - Lalit Fauzdar
@LalitSinghFauzdar,我在上面的方法中没有找到k==1,你能告诉我需要在哪里进行更改吗? - Sonali
请查看此函数调用:this.showCurvedPolyline(sydney1, sydney2, 0.5); 这里的0.5是k的值,请尝试将其更改为1。 - Lalit Fauzdar
抱歉,我忘记告诉你我已经尝试过使用1.0和1,但是问题依旧存在:this.showCurvedPolyline(latLng2, latLng1, 1); - Sonali
正如他们所提到的:“最后一个参数k定义了折线的曲率,它可以是>0且<=1。在我的示例中,我使用了k = 0.5”,你尝试将该值更改为小于先前值的0.2或0.3了吗?此外,将0.5更改为1有任何变化吗? - Lalit Fauzdar
@LalitSinghFauzdar 请看一下上面,我刚刚更新了一些东西。 - Sonali
4个回答

8
我在另一个问题中提出的解决方案是针对非常小的曲线折线。例如,当您使用Directions API路线时,起点和终点被捕捉到道路上,但原始请求中的真实起点和终点是建筑物的位置,因此您可以使用这条曲线虚线将道路和建筑物连接起来。
对于像您的示例中那样的大距离,我认为您可以直接使用API中可用的大地线折线。您可以将折线选项的大地线属性设置为true。
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;

    mMap.getUiSettings().setZoomControlsEnabled(true);

    LatLng latLng1 = new LatLng(40.7128, 74.0059); // New York
    LatLng latLng2 = new LatLng(51.5074, 0.1278); // London

    Marker marker1 = mMap.addMarker(new MarkerOptions().position(latLng1).title("Start"));
    Marker marker2 = mMap.addMarker(new MarkerOptions().position(latLng2).title("End"));

    List<PatternItem> pattern = Arrays.<PatternItem>asList(new Dash(30), new Gap(20));
    PolylineOptions popt = new PolylineOptions().add(latLng1).add(latLng2)
       .width(10).color(Color.MAGENTA).pattern(pattern)
       .geodesic(true);
    mMap.addPolyline(popt);

    LatLngBounds.Builder builder = new LatLngBounds.Builder();

    builder.include(marker1.getPosition());
    builder.include(marker2.getPosition());

    LatLngBounds bounds = builder.build();
    int padding = 150; // offset from edges of the map in pixels
    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);
    mMap.moveCamera(cu);
    mMap.animateCamera(cu);
}  

在我的截图中展示了生成的折线

输入图片描述

希望这能有所帮助!


2
嘿,我按照你说的做了,但是没有帮助,它仍然显示一条直线。 - Ari

3
这是我正在使用的路由对象,在@xomena的一些改进之后。

Route.java

我的更改:
  • 使用heading - 90.0而不是heading + 90.0
  • 重命名变量,使其成为常量(仍在弄清代码的某些部分正在做什么...)
  • 用我的新逻辑重写

基本上,只有当起点和终点之间的距离小于1000公里时,我才会绘制曲线路线。对于更长的距离,正如我在here中提到的那样,在循环内部的h1 + i * step部分由于每个迭代器中的double计算误差而产生了一些小错误,使得最终路线没有正确放置。

我的安全选择是绘制直线飞行路线,但我的建议是不要使用k = 1,因为这不是性能效率。我编写了另一种方法,只需将起点和终点添加到Route中,跳过所有其他复杂计算。

我将尝试在未来解决这个长曲线路线问题,现在,这个解决方案仍然适合我的问题。

编辑:

至今为止,这是我针对第二个问题的解决方案:

  • 更改 heading + 90heading - 90 没有解决问题,所以不要这么做
  • 相反,像这样更改 step 变量的计算:

    double step = Math.toDegrees(Math.atan(distance / 2 / midPerpendicularLength)) * 2 / DEFAULT_CURVE_POINTS;


谢谢分享,您能否分享一个Activity类的代码以及我们需要在build.gradle中添加的内容,因为我遇到了“无法解析符号'DirectionApiResponse'”的问题...如果可能的话,请像@xomena为我们所做的那样上传一个演示源代码到Github...因此今天我们能够对它进行讨论,并且您是第二个帮助继续此讨论的人 - 非常感谢。 - Sonali
实际上,我想回答这个问题以赚取100个悬赏点 :) https://stackoverflow.com/questions/46103680/map-and-moving-marker-using-google-maps-api(因此我使用纽约和伦敦作为两个地理位置),我想要实现已经实现的相同的东西,但是用弧形。 - Sonali
我刚刚使用了-90并上传了截图,请检查,但问题仍未解决。 - Sonali
你可以忽略 DirectionApiResponse 部分并将其删除。你确定只改变了 -90 而没有其他的吗?因为这看起来很奇怪。 - Tam Huynh
是的,你可以在你那边尝试,我只是想得到这些地理位置:LatLng latLng1 = new LatLng(40.7128, 74.0059); // 纽约
LatLng latLng2 = new LatLng(51.5074, 0.1278); // 伦敦
请分享相应的更新后的代码......如你所知,我已经尽力了...
- Sonali
显示剩余2条评论

2

嗨,你需要用这个方法更改你的showCurvePolyline方法。

    private void showCurvedPolyline (LatLng p1, LatLng p2, double k) {
    //Calculate distance and heading between two points
    double d = 0;


if (d == 0)
        d= SphericalUtil.computeDistanceBetween(origin, dest);



double h = SphericalUtil.computeHeading(p1, p2);

    // Calculate midpoint position
    LatLng midPoint = SphericalUtil.computeOffset(p1, d / 2, h);




    //Apply some mathematics to calculate position of the circle center
    double x = (1-k*k)*d*0.5/(2*k);
    double r = (1+k*k)*d*0.5/(2*k);

    LatLng c = SphericalUtil.computeOffset(p, x, h + 90.0);

    //Polyline options
    PolylineOptions options = new PolylineOptions();
    List<PatternItem> pattern = Arrays.<PatternItem>asList(new Dash(30), new Gap(20));

    //Calculate heading between circle center and two points
    double h1 = SphericalUtil.computeHeading(c, p1);
    double h2 = SphericalUtil.computeHeading(c, p2);

// Calculate position of the curve center point
    double sqrCurvature = DEFAULT_CURVE_ROUTE_CURVATURE * DEFAULT_CURVE_ROUTE_CURVATURE;
    double extraParam = distance / (4 * DEFAULT_CURVE_ROUTE_CURVATURE);
    double midPerpendicularLength = (1 - sqrCurvature) * extraParam;
    double r = (1 + sqrCurvature) * extraParam;

    LatLng midCurvePoint = SphericalUtil.computeOffset(midPoint, midPerpendicularLength, heading - 90.0);

    // Calculate heading between circle center and two points
    double headingToOrigin = SphericalUtil.computeHeading(midCurvePoint, origin);
    double headingToDest = SphericalUtil.computeHeading(midCurvePoint, dest);
    // Calculate positions of points on the curve
    double step = (headingToDest - headingToOrigin) / DEFAULT_CURVE_POINTS;

    List<LatLng> points = new ArrayList<>();
    for (int i = 0; i < DEFAULT_CURVE_POINTS; ++i) {
        points.add(SphericalUtil.computeOffset(midCurvePoint, r, headingToOrigin + i * step));
    }


    for (int i=0; i <points; i++) {
        LatLng pi = SphericalUtil.computeOffset(c, r, h1 + i * step);
        options.add(pi);
    }

    //Draw polyline
    mMap.addPolyline(options.width(10).color(Color.MAGENTA).geodesic(false).pattern(pattern));
}

然后你可以绘制arcPolyLine。它在之前答案提供的文件中提供。

1
嗨,Sonali,请看一下这个答案。 - Sardar Khan
1
你的代码中有一些未定义的变量,比如DEFAULT_CURVE_ROUTE_CURVATURE。 - Yunus Emre

2
使用Google地图投影API在覆盖视图上绘制折线是绘制弧形的另一种方法。查看此示例

enter image description here

"Original Answer"翻译成中文是"最初的回答"。

我们如何使用Flutter实现这个功能?如果有人知道,请告诉我。 - gsm

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