【问题标题】:Can I draw a curved dashed line in Google Maps Android?我可以在 Google Maps Android 中画一条弯曲的虚线吗?
【发布时间】:2017-04-09 10:28:42
【问题描述】:

在具有弯曲虚线的浏览器中的谷歌地图中,​​如下所示:

但是当我在自己的 Android 项目中实现谷歌地图时,并没有显示这一行

如何画这条线?

【问题讨论】:

  • 我认为这是可能的,我在这里找到了可以做到的jsfiddle。但问题是它使用Google Maps Javascript API 使其工作。如需更多信息,请尝试查看此SO question,如果它可以帮助您。
  • @KENdi 非常感谢,Google Maps Android API 中的直线虚线可以实现,但弯曲的虚线只出现在 Javascript API 中。我检查了 iOS、Android 上的谷歌地图应用程序,它没有任何曲线,我想这是不可能的(或者太难画了)

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


【解决方案1】:

您可以在两点之间实现弯曲的虚线折线。为此,您可以使用具有 SphericalUtil 类的Google Maps Android API Utility Library,并在代码中应用一些数学来创建折线。

你必须在你的 gradle 中包含实用程序库

compile 'com.google.maps.android:android-maps-utils:0.5'

请查看我的示例 Activity 和函数 showCurvedPolyline (LatLng p1, LatLng p2, double k),它在两点之间构造虚曲线折线。最后一个参数 k 定义折线的曲率,它可以是 >0 和

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

private GoogleMap mMap;
private LatLng sydney1;
private LatLng sydney2;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;

    mMap.getUiSettings().setZoomControlsEnabled(true);

    // Add a marker in Sydney and move the camera
    sydney1 = new LatLng(-33.904438,151.249852);
    sydney2 = new LatLng(-33.905823,151.252422);

    mMap.addMarker(new MarkerOptions().position(sydney1)
            .draggable(false).visible(true).title("Marker in Sydney 1"));
    mMap.addMarker(new MarkerOptions().position(sydney2)
            .draggable(false).visible(true).title("Marker in Sydney 2"));

    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney1, 16F));

    this.showCurvedPolyline(sydney1,sydney2, 0.5);
}

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

}

您可以从 GitHub 下载包含完整代码的示例项目

https://github.com/xomena-so/so43305664

只需在app/src/debug/res/values/google_maps_api.xml 中将我的 API 密钥替换为您的即可

【讨论】:

  • 非常感谢,它解决了我的问题。对于迟到的评论,我深表歉意,因为我的这个问题的任务在很长一段时间后又重新开始了。
  • 此代码中存在一个错误,如果您使用两个非常偏远的位置,曲线将到达错误的中心位置
  • 对于非常偏远的位置,只需使用测地线折线。
  • 我尝试去掉破折号以使其坚固,但是当我这样做时,圆圈并不光滑。有任何想法吗 ?我什至加了更多的点,但同样的事情
  • 无论如何这可以在javascript中实现并响应本机?
【解决方案2】:

感谢@xomena 的出色回答。但它只有一个小错误。有时,它的弧形变得像一个圆圈。我做了一些调试,发现我们总是使用h + 90.0 作为方法的第 12 行的标题值。我们可以通过像这样更改该行来解决这个问题:

LatLng c = SphericalUtil.computeOffset(p, x, h > 40 ? h + 90.0 : h - 90.0);

从现在开始,你可能不会再遇到这个问题了。

【讨论】:

    【解决方案3】:

    当我用实线绘制曲线时,我遇到了同样的曲线弯曲问题。在互联网上搜索了几个小时并尝试了不同的解决方案。最后,我通过使用Polygon 而不是Polyline 提出了解决方案(不是 一个合适的解决方案,但可以实现目标)。我修改了上面的方法showCurvedPolyline(),画出一条平滑的曲线,曲线方向永远是向上的。下面的截图是我修改后的最终结果。

    fun drawCurveOnMap(googleMap: GoogleMap, latLng1: LatLng, latLng2: LatLng) {
    
        //Adding marker is optional here, you can move out from here.
        googleMap.addMarker(
           MarkerOptions().position(latLng1).icon(BitmapDescriptorFactory.defaultMarker()))
        googleMap.addMarker(
           MarkerOptions().position(latLng2).icon(BitmapDescriptorFactory.defaultMarker()))
        
        val k = 0.5 //curve radius
        var h = SphericalUtil.computeHeading(latLng1, latLng2)
        var d = 0.0
        val p: LatLng?
    
        //The if..else block is for swapping the heading, offset and distance 
        //to draw curve always in the upward direction
        if (h < 0) {
            d = SphericalUtil.computeDistanceBetween(latLng2, latLng1)
            h = SphericalUtil.computeHeading(latLng2, latLng1)
            //Midpoint position
            p = SphericalUtil.computeOffset(latLng2, d * 0.5, h)
        } else {
            d = SphericalUtil.computeDistanceBetween(latLng1, latLng2)
    
            //Midpoint position
            p = SphericalUtil.computeOffset(latLng1, d * 0.5, h)
        }
    
        //Apply some mathematics to calculate position of the circle center
        val x = (1 - k * k) * d * 0.5 / (2 * k)
        val r = (1 + k * k) * d * 0.5 / (2 * k)
    
        val c = SphericalUtil.computeOffset(p, x, h + 90.0)
    
        //Calculate heading between circle center and two points
        val h1 = SphericalUtil.computeHeading(c, latLng1)
        val h2 = SphericalUtil.computeHeading(c, latLng2)
    
        //Calculate positions of points on circle border and add them to polyline options
        val numberOfPoints = 1000 //more numberOfPoints more smooth curve you will get
        val step = (h2 - h1) / numberOfPoints
    
        //Create PolygonOptions object to draw on map
        val polygon = PolygonOptions()
    
        //Create a temporary list of LatLng to store the points that's being drawn on map for curve 
        val temp = arrayListOf<LatLng>()
    
        //iterate the numberOfPoints and add the LatLng to PolygonOptions to draw curve
        //and save in temp list to add again reversely in PolygonOptions
        for (i in 0 until numberOfPoints) {
            val latlng = SphericalUtil.computeOffset(c, r, h1 + i * step)
            polygon.add(latlng) //Adding in PolygonOptions
            temp.add(latlng)    //Storing in temp list to add again in reverse order
        }
    
        //iterate the temp list in reverse order and add in PolygonOptions
        for (i in (temp.size - 1) downTo 1) {
            polygon.add(temp[i])
        }
    
        polygon.strokeColor(Color.BLUE)
        polygon.strokeWidth(12f)
        polygon.strokePattern(listOf(Dash(30f), Gap(50f))) //Skip if you want solid line
        googleMap.addPolygon(polygon)
    
        temp.clear() //clear the temp list
    }
    

    为什么我们要在 PolygonOptions 中以相反的顺序再次添加临时列表?

    如果我们不在PolygonOptions中逆序添加LatLnggoogleMap.addPolygon()将关闭路径,最终结果如下所示。

    提示:

    如果您希望曲线更接近圆形,请增加k 的值。喜欢k = 0.75

    【讨论】:

      【解决方案4】:

      感谢@xomena 提供上述解决方案。在大多数情况下,它都能很好地工作。但还需要一些改进:

      • k == 1 时,x 为 0,中点 (p) 与曲线中点 (c) 相同。也就是说它应该是一条直线,但是当你计算步长时,它不是零,所以最终的结果是一个半圆曲线,这与上述条件有歧义。

      • 当曲线足够长时,假设LIMIT = 1000km,循环内h1 + i * step 中的每个计算都会对正确值产生微小的错误(我猜是由于java double 计算错误)。那么折线的起点和终点与起点和终点坐标不完全匹配。此外,折线的曲率是不可预测的,根据我的研究,原因可能是地球表面的曲率会使您基于航向的计算不正确。

      我的快速解决方法是将 step 重置为 0 if k == 1 以使其成为一条直线。对于第二个问题,如果两个点之间的距离大于 LIMIT 1000km,那么用k = 1 画一条直线对我来说会是一个更安全的选择。

      【讨论】:

      • 感谢分享。请看一下:stackoverflow.com/questions/46411266/draw-arc-polyline-in-google-map 我可以得到你最终为 showCurvedPolyline(..) 方法编写的更新解决方案
      猜你喜欢
      • 1970-01-01
      • 2017-02-23
      • 2018-07-20
      • 1970-01-01
      • 2023-02-06
      • 1970-01-01
      • 2018-09-01
      • 1970-01-01
      • 2011-07-17
      相关资源
      最近更新 更多