8

如何使用 Android Maps V2 API 绘制带箭头的折线(指示方向)。在Android 地图文档中,此选项不可用。是否可以在折线上添加箭头?

4

3 回答 3

13

这是我使用与 Doug 建议的概念相同的概念开发的一个工作示例,采用 JavaScript 示例 ( http://econym.org.uk/gmap/example_arrows.htm ) 并将其转换为 Java 代码。为方便起见,它使用来自谷歌地图服务器的图像,但这些图像可能是您设计的图像,也可能是从网络上抓取并存储在本地应用程序中。为了演示,我在主线程上下载这些,但如果在实时应用程序中使用,请不要这样做!

与 JavaScript 示例的主要区别在于,您必须将箭头图像投影到大小为四倍的较大图像上,根据从 A 到 B 的方位计算出将图像转换到的位置,最后将该图像居中通过为图标添加另一个带有较大图像的锚定标记来现有的 B 标记。

首先添加你的折线:

PolylineOptions polylines = new PolylineOptions();

LatLng from = new LatLng(f.getLatitude(), f.getLongitude());
LatLng to = new LatLng(t.getLatitude(), t.getLongitude());

polylines.add(from, to).color(polyColor).width(2);

mMap.addPolyline(polylines);

DrawArrowHead(mMap, from, to);

然后添加你的箭头:

private final double degreesPerRadian = 180.0 / Math.PI;

private void DrawArrowHead(GoogleMap mMap, LatLng from, LatLng to){
    // obtain the bearing between the last two points
    double bearing = GetBearing(from, to);

    // round it to a multiple of 3 and cast out 120s
    double adjBearing = Math.round(bearing / 3) * 3;
    while (adjBearing >= 120) {
        adjBearing -= 120;
    }

    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy); 

    // Get the corresponding triangle marker from Google        
    URL url;
    Bitmap image = null;

    try {
        url = new URL("http://www.google.com/intl/en_ALL/mapfiles/dir_" + String.valueOf((int)adjBearing) + ".png");
        try {
            image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    if (image != null){

        // Anchor is ratio in range [0..1] so value of 0.5 on x and y will center the marker image on the lat/long
        float anchorX = 0.5f;
        float anchorY = 0.5f;

        int offsetX = 0;
        int offsetY = 0;

        // images are 24px x 24px
        // so transformed image will be 48px x 48px

        //315 range -- 22.5 either side of 315
        if (bearing >= 292.5 && bearing < 335.5){
            offsetX = 24;
            offsetY = 24;
        }
        //270 range
        else if (bearing >= 247.5 && bearing < 292.5){
            offsetX = 24;
            offsetY = 12;
        }
        //225 range
        else if (bearing >= 202.5 && bearing < 247.5){
            offsetX = 24;
            offsetY = 0;
        }
        //180 range
        else if (bearing >= 157.5 && bearing < 202.5){
            offsetX = 12;
            offsetY = 0;
        }
        //135 range
        else if (bearing >= 112.5 && bearing < 157.5){
            offsetX = 0;
            offsetY = 0;
        }
        //90 range
        else if (bearing >= 67.5 && bearing < 112.5){
            offsetX = 0;
            offsetY = 12;
        }
        //45 range
        else if (bearing >= 22.5 && bearing < 67.5){
            offsetX = 0;
            offsetY = 24;
        }
        //0 range - 335.5 - 22.5
        else {
            offsetX = 12;
            offsetY = 24;
        }

        Bitmap wideBmp;
        Canvas wideBmpCanvas;
        Rect src, dest;

        // Create larger bitmap 4 times the size of arrow head image
        wideBmp = Bitmap.createBitmap(image.getWidth() * 2, image.getHeight() * 2, image.getConfig());

        wideBmpCanvas = new Canvas(wideBmp); 

        src = new Rect(0, 0, image.getWidth(), image.getHeight());
        dest = new Rect(src); 
        dest.offset(offsetX, offsetY); 

        wideBmpCanvas.drawBitmap(image, src, dest, null);

        mMap.addMarker(new MarkerOptions()
        .position(to)
        .icon(BitmapDescriptorFactory.fromBitmap(wideBmp))
        .anchor(anchorX, anchorY));
    }
}

private double GetBearing(LatLng from, LatLng to){
    double lat1 = from.latitude * Math.PI / 180.0;
    double lon1 = from.longitude * Math.PI / 180.0;
    double lat2 = to.latitude * Math.PI / 180.0;
    double lon2 = to.longitude * Math.PI / 180.0;

    // Compute the angle.
    double angle = - Math.atan2( Math.sin( lon1 - lon2 ) * Math.cos( lat2 ), Math.cos( lat1 ) * Math.sin( lat2 ) - Math.sin( lat1 ) * Math.cos( lat2 ) * Math.cos( lon1 - lon2 ) );

    if (angle < 0.0)
        angle += Math.PI * 2.0;

    // And convert result to degrees.
    angle = angle * degreesPerRadian;

    return angle;
}
于 2013-08-15T11:27:41.937 回答
4

2017 年 2 月 15 日发布的 Android Maps API v2开始,您现在可以在 Android Maps API v2 的折线末端添加自定义线帽,这可以是箭头。

来自Line caps文档:

以下代码段为端盖指定了一个自定义位图:

mPolyline.setEndCap(
        new CustomCap(BitmapDescriptorFactory.fromResource(R.drawable.arrow),
                16));

当您使用自定义位图时,您应该以像素为单位指定参考笔划宽度。API 相应地缩放位图。参考笔划宽度是您在为帽子设计位图图像时使用的笔划宽度,位于图像的原始尺寸。默认参考笔画宽度为 10 像素。提示:要确定参考笔划宽度,请在图像编辑器中以 100% 放大打开位图图像,并绘制相对于图像的所需线条笔划宽度。

如果您用于BitmapDescriptorFactory.fromResource()创建位图,请确保使用与密度无关的资源 ( nodpi )。

谷歌关于相关已解决问题的帖子

我们添加了使用自定义位图自定义折线的起点和终点的功能。使用它,您将能够向折线添加箭头。

在此处的形状指南中查看有关线帽的信息: https ://developers.google.com/maps/documentation/android-api/shapes#line_caps

在此处查看新的折线和多边形教程中的示例: https ://developers.google.com/maps/documentation/android-api/polygon-tutorial#add_custom_styling_to_your_polyline

请参阅此处的发行说明: https ://developers.google.com/maps/documentation/android-api/releases#february_15_2017

请参阅此处的博客文章: https ://maps-apis.googleblog.com/2017/02/styling-and-custom-data-for-polylines.html

于 2017-08-03T20:34:00.610 回答
1

我没有尝试过,但我认为使用标记应该是可能的。您将需要创建一系列(例如 20 个)小箭头,每个箭头指向不同的方向,18、36、54 度等。然后在构建折线时,或者可能更好,在构建折线后作为单独的过程,以您想要的任何间距遍历所有坐标,并在每个选定点使用 locationn.getbearing 获取方位,并从该方位确定用作标记的方向箭头并在该点放置适当的标记

于 2013-04-17T18:32:53.453 回答