在 Google 地图上移动和显示行车摄像头视图

Posted

技术标签:

【中文标题】在 Google 地图上移动和显示行车摄像头视图【英文标题】:Moving and showing the driving camera view on Google maps 【发布时间】:2020-02-15 12:23:39 【问题描述】:

我已经通过谷歌地图路由添加了当前位置

        Routing routing = new Routing.Builder()
            .travelMode(Routing.TravelMode.DRIVING)
            .key(getResources().getString(R.string.google_maps_api))
            .withListener(this)
            .waypoints(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), site_location)
            .alternativeRoutes(false)
            .build();
    routing.execute();



   @Override
public void onRoutingSuccess(ArrayList<Route> route, int shortestRouteIndex) 

    if (polylines.size() > 0) 
        for (Polyline poly : polylines) 
            poly.remove();
        
    

    polylines = new ArrayList<>();
    //add route(s) to the map.
    for (int i = 0; i < route.size(); i++) 

        //In case of more than 5 alternative routes
        int colorIndex = i % COLORS.length;

        PolylineOptions polyOptions = new PolylineOptions();
        polyOptions.color(getResources().getColor(COLORS[colorIndex]));
        polyOptions.width(10 + i * 13);
        polyOptions.addAll(route.get(i).getPoints());
        Polyline polyline = googleMap.addPolyline(polyOptions);
        polylines.add(polyline);

        int distance = route.get(i).getDistanceValue();
        if (distance < 1000)
            totalKm.setText( distance+" Metres");
        else 
            totalKm.setText( (distance/1000) +" km");

        
    

    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    builder.include(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()));
    builder.include(site_marker.getPosition());
    LatLngBounds bounds = builder.build();
    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 100);
    googleMap.animateCamera(cu);


这显示行车路线,如

但我试图用缩放视图显示默认的谷歌地图驾驶图标

如何继续添加这样的地图,同时仍保留折线以显示驾驶视图。

【问题讨论】:

【参考方案1】:

Jinesh Francis 他的回答完全正确:您应该通过意图运行默认地图 Google Maps 应用程序或修改标准 MapView(或 MapFragment)。

TLDR;

如果您选择第二种方式 - 最简单的方法是使用 android Google Maps API 的标准类来创建您的示例中的视图(其他方式是创建 MapView-based custom view)。

首先仔细阅读Google Maps Platform Terms of Service的3.2.4限制滥用服务(d):

(d) 不得重新创建 Google 产品或功能。客户不会使用 服务以创建具有以下功能的产品或服务 实质上类似于或重新创造另一个人的特征 谷歌产品或服务。客户的产品或服务必须包含 Google 产品之外的实质性、独立的价值和功能 或服务。例如,客户不会: (i) 重新分发 谷歌地图核心服务或假装它们是客户的 服务; (ii) 创建谷歌地图核心服务的替代品, Google 地图或 Google 地图移动应用程序或其功能; (iii) 使用 列表或目录服务中的 Google 地图核心服务或 创建或增加广告产品; (iv) 合并来自 Directions API、Geolocation API 和 Maps SDK for Android 创建 实时导航功能与 Google Maps for Android 移动应用提供的功能。

如果您不违反服务条款,您可以使用这些步骤/任务做您想做的事:

1) 获取用户当前位置;

2) 获取最接近用户当前位置的路线路径段(因为用户位置很少恰好在路上);

3) 获取该段的方位角(方位角);

4) 显示带有路线路径和用户当前位置标记的地图,并根据路径段方位进行适当的倾斜和旋转。

任务1可以像thisAxxiss的答案一样解决:

private final LocationListener mLocationListener = new LocationListener() 
    @Override
    public void onLocationChanged(final Location location) 
        //your code here
    
;

任务 2 可以通过 PolyUtil.isLocationOnPath() 解决,就像在 that answer 中一样:

private LatLng getMarkerProjectionOnSegment(LatLng carPos, List<LatLng> segment, Projection projection) 
    LatLng markerProjection = null;

    Point carPosOnScreen = projection.toScreenLocation(carPos);
    Point p1 = projection.toScreenLocation(segment.get(0));
    Point p2 = projection.toScreenLocation(segment.get(1));
    Point carPosOnSegment = new Point();

    float denominator = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
    // p1 and p2 are the same
    if (Math.abs(denominator) <= 1E-10) 
        markerProjection = segment.get(0);
     else 
        float t = (carPosOnScreen.x * (p2.x - p1.x) - (p2.x - p1.x) * p1.x
                + carPosOnScreen.y * (p2.y - p1.y) - (p2.y - p1.y) * p1.y) / denominator;
        carPosOnSegment.x = (int) (p1.x + (p2.x - p1.x) * t);
        carPosOnSegment.y = (int) (p1.y + (p2.y - p1.y) * t);
        markerProjection = projection.fromScreenLocation(carPosOnSegment);
        
    return markerProjection;

任务 3 可以用这样的代码解决:

private float getBearing(LatLng begin, LatLng end) 
    double dLon = (end.longitude - begin.longitude);
    double x = Math.sin(Math.toRadians(dLon)) * Math.cos(Math.toRadians(end.latitude));
    double y = Math.cos(Math.toRadians(begin.latitude))*Math.sin(Math.toRadians(end.latitude))
            - Math.sin(Math.toRadians(begin.latitude))*Math.cos(Math.toRadians(end.latitude)) * Math.cos(Math.toRadians(dLon));
    double bearing = Math.toDegrees((Math.atan2(x, y)));
    return (float) bearing;

其中beginend 是当前路线路径段的起点和终点。

任务 4 可以用这样的代码来解决:

作为标记,您可以像这样使用北向箭头的矢量可绘制对象:

ic_up_arrow_circle.xml(也可以调整透明度和颜色):

<vector android: android:viewportHeight="93.934"
    android:viewportWidth="93.934"
    android:
    xmlns:android="http://schemas.android.com/apk/res/android">
    <path
        android:fillColor="#8fFF0000"
        android:pathData="m0,46.9666c0,25.939 21.028,46.967 46.967,46.967c25.939,-0 46.967,-21.028 46.967,-46.967c0,-25.939 -21.027,-46.967 -46.967,-46.967c-25.939,-0 -46.967,21.028 -46.967,46.967zM78.262,67.4396l-31.295,-16.845l-31.295,16.845l31.295,-51.614l31.295,51.614z"
    />

    <path
        android:fillColor="#FFFFFF"
        android:pathData="M78.262,67.4396l-31.295,-16.845l-31.295,16.845l31.295,-51.614l31.295,51.614z"
        />
</vector>

你可以用这样的代码把它放在地图上:

public Marker addDirectionMarker(LatLng latLng, float angle) 
    Drawable circleDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_up_arrow_in_circle);
    BitmapDescriptor markerIcon = getMarkerIconFromDrawable(circleDrawable, 150, 150);

    return mGoogleMap.addMarker(new MarkerOptions()
            .position(latLng)
            .anchor(0.5f, 0.5f)
            .rotation(angle)
            .flat(true)
            .icon(markerIcon)
    );

其中150 是标记大小(以像素为单位)。注意!您需要flat 标记以使其随地图旋转和倾斜,0.5f 用于将标记锚点精确地移动到其中心点。

然后你可以在地图上显示所有这些:

...
CameraPosition cameraPosition = new CameraPosition.Builder()
        .target(userPosition)
        .tilt(tilt)
        .zoom(zoom)
        .bearing(bearing)
        .build();
mGoogleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
...

但如果你只在屏幕中心显示用户当前位置的标记(因为GoogleMap.moveCamera() 将中心设置在.target() 处)。因此,为避免这种情况,您需要稍微向下移动地图 - 在这种情况下,用户位置标记应出现在屏幕底部。对于地图中心偏移,您需要获取当前地图中心屏幕坐标,然后更改y 坐标并获取新的屏幕中心。类似的东西:

...
LatLng mapCenter = mGoogleMap.getCameraPosition().target;
Projection projection = mGoogleMap.getProjection();
Point centerPoint = projection.toScreenLocation(mapCenter);

DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;

centerPoint.y = centerPoint.y - (int) (displayHeight / 4.5);  // move center down for approx 22%

LatLng newCenterPoint = projection.fromScreenLocation(centerPoint);

mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(newCenterPoint, zoom));
...

有了所有这些东西,对于你的路线(zoom = 15 和 tilt = 50)你应该得到类似的东西:

如您所见,路线路径并不完全在路上,因此您需要比 Directions API 响应更精确地获取路线路径点。你可以得到它,例如通过Google Maps Roads API 部分Snap to Road 其中

针对给定的 GPS 坐标集返回最适合的道路几何形状。 该服务最多需要沿路线收集 100 个 GPS 点,并且 返回一组相似的数据,其中的点捕捉到最多 车辆行驶的可能道路。

喜欢that 的回答。如果您的路线路径有多个点,您需要分成 100 个点的部分并分别处理它们(Snap to Road API 也有每个用户(IP)每天 2500 个请求和每秒 10 个请求的限制)。

正如Jaswant Singh 回答你的那样:

需要设置一个自定义标记(图标与蓝色箭头相同) 您当前的位置并将其每次移至新位置 是 onLocationChanged() 回调被调用(也动画相机到 那个新位置)。

另外,您需要根据当前用户速度选择zoomtilt 属性:当用户开车更快时tilt -&gt; 0。等等。这不是一项简单的任务。

【讨论】:

感谢您的见解,我看到了谷歌政策的一个主要问题,即通过组合来自 .... 的数据来创建实时导航。我决定通过在 a 上使用传单地图将我的项目转移到 osrm webview 而不是谷歌地图。 @Geoff 不客气!可能是您的项目“包含大量、独立的价值和功能”,但更好 - 小心。 @AndriiOmelchenko 真是个​​解释!我能够重现所有这些,而且我正在考虑在移动旋转上旋转地图。我能够从传感器获得方位角。我只是需要一些帮助。您能告诉我们一种方法,我们可以使用传感器的方位角值来路由路径段的方位角为指向前方吗? @KaranJhinga 如果这个答案很好 - 你可以投票。为了您的澄清,请创建单独的问题。【参考方案2】:

Android 没有像定向 View(图 2)那样提供 View 用于开发。如果您想使用相同的 View,您可以通过传递源位置和像这样的目的地位置。

Intent intent = new Intent(android.content.Intent.ACTION_VIEW, 
    Uri.parse("http://maps.google.com/maps?saddr=20.344,34.34&daddr=20.5666,45.345"));
startActivity(intent);

使用默认的AndroidMapView,只能在两个位置之间用标记添加折线(在许多著名的送货应用中都可以看到)。MapView支持相机变焦,您可以倾斜(倾斜)地图,但它不会给出确切的结果。

【讨论】:

【参考方案3】:

除了Jinesh的回答, 如果您仍想为开发添加该标记,则需要在当前位置设置一个自定义标记(图标与蓝色箭头相同),并在每次调用 onLocationChanged() 回调时将其移动到新位置(也将相机动画到该新位置)。 稍微倾斜地图以获得谷歌地图导航视图的确切外观,虽然您不会使用所有功能,但值得一试。

【讨论】:

以上是关于在 Google 地图上移动和显示行车摄像头视图的主要内容,如果未能解决你的问题,请参考以下文章

从当前位置移动网络行车路线

使用带有缩放功能的 UIWebView 显示 Google 地图

中国移动r611 行车记录仪多少钱

双摄像头的行车记录仪为啥总是被后面的摄像头切换掉

如何在 Worklight 应用程序上显示 Google 地图

如何在 iOS 中移动标记以及移动 Google 地图?