6

我正在使用 Google Maps API v2 开发 Android 应用程序。我的地图上有标记,我想圈出其中一个。通过使用 Circle 和 Circle Options 类,我设法轻松地做到了这一点。但我也希望我的圆圈在缩放或取消缩放时在屏幕上保持相同的大小,就像标记一样。这意味着圆必须具有以像素为单位的恒定半径。遗憾的是,我们无法在 API v2 中设置以像素为单位的半径。

我尝试了几种解决方案,但我并不满意。

在第一个中,我只是乘以或除以半径:

@Override
public void onCameraChange(CameraPosition position) 
{
    if(previousZoom > position.zoom) {
        mSelectionCircle.setRadius(Math.abs(position.zoom - previousZoom)*2*mSelectionCircle.getRadius());
    }
    else if(previousZoom < position.zoom) {
        mSelectionCircle.setRadius(Math.abs(position.zoom - previousZoom)*mSelectionCircle.getRadius()/2);
    }

    previousZoom = position.zoom;
}

起初它似乎可以工作,但在快速缩放或用手指缩放时会产生错误的结果。此外,缩放在屏幕上清晰可见。

我的第二个解决方案使用像素米转换。这个想法是在缩放/取消缩放时重新计算以米为单位的半径,因此圆圈在屏幕上的大小是恒定的。为此,我在屏幕上获得了 Circle 的当前位置:

 Point p1 = mMap.getProjection().toScreenLocation(mSelectionCircle.getCenter());

然后我在圆的边缘创建另一个点:

 Point p2 = new Point(p1.x + radiusInPixels, p1.y);

在哪里

 int radiusInPixels = 40;

之后,我使用一个函数返回这两点之间的距离(以米为单位)。

private double convertPixelsToMeters(Point point1, Point point2) {

    double angle = Math.acos(Math.sin(point1.x) * Math.sin(point2.x)
                  + Math.cos(point1.x) * Math.cos(point2.x) * Math.cos(point1.y- point2.y));
    return angle * Math.PI * 6378100.0; // distance in meters
}

6378100 是地球的平均半径。最后,我设置了 Circle 的新半径:

  mSelectionCircle.setRadius(convertPixelsToMeters(p1, p2));

它在理论上应该有效,但我得到荒谬的半径值(10 ^ 7 m!)。转换函数可能是错误的?

那么有没有更简单的方法可以做到这一点,或者如果没有,您能否帮助我理解为什么我的第二个解决方案不起作用?

谢谢!

4

4 回答 4

4

您可能并不真正关心确切的像素大小,只是所有缩放级别和设备旋转看起来都相同。

这是一个相当简单的方法来做到这一点。绘制(并在缩放改变时重绘)一个半径为可见屏幕对角线百分比的圆。

Google Maps API v2 有一个getProjection()函数可以返回可见屏幕 4 个角的纬度/经度坐标。然后使用超级方便的Location类,您可以计算屏幕上可见的对角线的距离,并使用该对角线的百分比作为圆的半径。无论缩放比例是多少或设备旋转的方式如何,您的圆圈都将是相同的大小。

这是Java中的代码:

public Circle drawMapCircle(GoogleMap googleMap,LatLng latLng,Circle currentCircle) {

    // get 2 of the visible diagonal corners of the map (could also use farRight and nearLeft)
    LatLng topLeft = googleMap.getProjection().getVisibleRegion().farLeft;
    LatLng bottomRight = googleMap.getProjection().getVisibleRegion().nearRight;

    // use the Location class to calculate the distance between the 2 diagonal map points
    float results[] = new float[4]; // probably only need 3
    Location.distanceBetween(topLeft.latitude,topLeft.longitude,bottomRight.latitude,bottomRight.longitude,results);
    float diagonal = results[0];

    // use 5% of the diagonal for the radius (gives a 10% circle diameter)
    float radius = diagonal / 20;

    Circle circle = null;
    if (currentCircle != null) {
        // change the radius if the circle already exists (result of a zoom change)
        circle = currentCircle;

        circle.setRadius(radius);
    } else {
        // draw a new circle
        circle = googleMap.addCircle(new CircleOptions()
                .center(latLng)
                .radius(radius)
                .strokeColor(Color.BLACK)
                .strokeWidth(2)
                .fillColor(Color.LTGRAY));
    }

    return circle;
}
于 2018-03-21T23:46:12.510 回答
3

Marker改为使用自定义图标。您可以创建Bitmapand Canvas,在后者上绘制并将其用作Marker图标:

new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(bitmap))...
于 2013-05-07T20:28:00.193 回答
3

编辑:

我之前的回答不再有效。正如 Jean-Philippe Jodoin 提出的,您可以简单地使用标记并将其锚点设置为 0.5/0.5。这是一种更清洁的解决方案。

在此处粘贴建议的代码片段以供参考:

marker = mMap.addMarker(new MarkerOptions().position(latlng).anchor(0.5f, 0.5f));

老答案:

我遇到了同样的问题,找不到解决方案,所以我自己做了,我会发布,希望它对其他人有帮助。

“标记”方法对我不起作用,因为我希望圆圈以特定的纬度/经度为中心,而你不能用标记来做到这一点:如果你为标记设置了一个圆圈图标,圆圈边缘将接触纬度/lng,但圆圈不会以 lat/lng 为中心。

我创建了一个函数来计算给定纬度和相机缩放级别的圆的大小(以米为单位),然后在地图上添加一个相机侦听器,以在每次相机更改缩放级别时更新圆的大小。结果是一个圆圈大小不变(至少肉眼可见)。

这是我的代码:

public static double calculateCircleRadiusMeterForMapCircle(final int _targetRadiusDip, final double _circleCenterLatitude,
    final float _currentMapZoom) {
    //That base value seems to work for computing the meter length of a DIP
    final double arbitraryValueForDip = 156000D;
    final double oneDipDistance = Math.abs(Math.cos(Math.toRadians(_circleCenterLatitude))) * arbitraryValueForDip / Math.pow(2, _currentMapZoom);
    return oneDipDistance * (double) _targetRadiusDip;
}

public void addCircleWithConstantSize(){
    final GoogleMap googleMap = ...//Retrieve your GoogleMap object here

    //Creating a circle for the example
    final CircleOptions co = new CircleOptions();
    co.center(new LatLng(0,0));
    co.fillColor(Color.BLUE);
    final Circle circle = googleMap.addCircle(co);

    //Setting a listener on the map camera to monitor when the camera changes
    googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
        @Override
        public void onCameraMove() {
            //Use the function to calculate the radius
            final double radius = calculateCircleRadiusMeterForMapCircle(12, co.getCenter().latitude, googleMap.getCameraPosition().zoom);
            //Apply the radius to the circle
            circle.setRadius(radius);
        }
    });
}
于 2016-08-21T14:16:18.450 回答
0

正如 MaciejGórski 建议的那样,这是正确且简单的方法;但是如果你在谷歌地图中有很多标记,比如 5k 标记,它会显着降低性能。显示此问题的一些建议是:

1)让Google android map API的搜索Marker clustering utility。

2)但是,标记聚类可能不完全符合您的目的。所以你可以自己定制它。这是讨论这个问题的线程:https ://github.com/googlemaps/android-maps-utils/issues/29

对不起,我没有尝试,因为我发现使用折线可以满足我的目的(显示路径)。

希望这有帮助,Mttdat。

于 2017-10-10T07:54:59.753 回答