安卓地图V2 - 将相机动画移动以包含大多数标记

4
我有一组来自Web服务的点需要在地图上显示。对于大多数情况,我已经有了一个很好的解决方案,使用众所周知的LatLngBounds.Builder、CameraUpdateFactory.newLatLngBounds和map.animateCamera。但是在某些情况下会出现问题:当这些点太远时,地图会以最大缩放级别将中心设置为这些点的重心。例如:我在法国有10个点,在夏威夷有2个点。地图在最小缩放级别上更或多或少地居中于加勒比海。因此,在屏幕上我什么也看不到,用户必须滚动才能看到实际存在的东西。
所以我的问题是:
有没有办法让地图缩小到足以看到所有点(这将是首选)
或者:过滤掉那些只有少数点距离大多数点很远的情况,并选择一组点进行缩放(在我的例子中,我将选择对法国的10个点进行缩放,忽略夏威夷的那些点)。
4个回答

6
将所有标记的LatLng放入列表中,并将它们传递给此方法。在newLatLngBounds(bounds, 50)的最后一行,50代表地图边缘和每个侧面上最外层标记之间的填充。
public void centerIncidentRouteOnMap(List<LatLng> copiedPoints) {
        double minLat = Integer.MAX_VALUE;
        double maxLat = Integer.MIN_VALUE;
        double minLon = Integer.MAX_VALUE;
        double maxLon = Integer.MIN_VALUE;
        for (LatLng point : copiedPoints) {
            maxLat = Math.max(point.latitude, maxLat);
            minLat = Math.min(point.latitude, minLat);
            maxLon = Math.max(point.longitude, maxLon);
            minLon = Math.min(point.longitude, minLon);
        }
        final LatLngBounds bounds = new LatLngBounds.Builder().include(new LatLng(maxLat, maxLon)).include(new LatLng(minLat, minLon)).build();
        mapFragment.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
    }

如果他对此有问题,请让他提供一些LatLng点来重现问题,但我无法重现它。 - Omar HossamEldin
这里有4个地点:法国的巴黎、里昂、马赛和夏威夷的檀香山。我已经按照您提到的做了,但期望地图在最小缩放级别时以墨西哥为中心。因此,没有任何标记会显示出来。相反,我想忽略檀香山并仅计算法国的范围。 - Vincent Mimoun-Prat
@3amoura并不是要无礼或者什么的,只是想让你知道而已,看来我是对的。 - cYrixmorten
@MarvinLabs 对不起,我一开始没有理解你的问题,我的意思是如果在你的例子中有像“檀香山”这样的远程标记,你想要忽略的部分。 - Omar HossamEldin
@cYrixmorten 没有什么问题,朋友 ;) 没关系,在理解问题后,我认为你的答案应该有效。 - Omar HossamEldin
@3amoura 很好 :) 希望如此,至少它对我的目的起作用,但当然在重写代码后可能会出现错误。 - cYrixmorten

4

我发现之前的代码中有错误,于是决定重新编写。

我以前做过类似的事情,当时有大约4500个标记,并希望选择那些距离特定位置一定距离的标记。我将那段代码进行了泛化,使其适用于任何类型的标记。

所有标记使用getSurroundingMarkers选择的标记

我将要发布的代码包含两种方法:

selectLowDistanceMarkers

测量每个标记之间的距离,并仅选择那些与其他标记之间的距离不远的标记。由于每个标记之间的比较和之后的检查,这需要O(n+n^2)的运行时间。

getSurroundingMarkers

如果您已经知道要缩放到的位置,则此方法与上述方法相同。该方法的CPU负荷要小得多,因为它只需要对所有标记进行O(n)次运行并将它们与给定位置进行比较。

private List<Marker> selectLowDistanceMarkers(List<Marker> markers,
        int maxDistanceMeters) {

    List<Marker> acceptedMarkers = new ArrayList<Marker>();

    if (markers == null) return acceptedMarkers;

    Map<Marker, Float> longestDist = new HashMap<Marker, Float>();

    for (Marker marker1 : markers) {

        // in this for loop we remember the max distance for each marker
        // think of a map with a flight company's routes from an airport
        // these lines is drawn for each airport
        // marker1 being the airport and marker2 destinations

        for (Marker marker2 : markers) {
            if (!marker1.equals(marker2)) {
                float distance = distBetween(marker1.getPosition(),
                        marker2.getPosition());
                if (longestDist.containsKey(marker1)) {
                    // possible we have a longer distance
                    if (distance > longestDist.get(marker1))
                        longestDist.put(marker1, distance);
                } else {
                    // first distance
                    longestDist.put(marker1, distance);
                }
            }
        }
    }


    // examine the distances collected
    for (Marker marker: longestDist.keySet()) {
        if (longestDist.get(marker) <= maxDistanceMeters) acceptedMarkers.add(marker);
    }

    return acceptedMarkers;
}

private List<Marker> getSurroundingMarkers(List<Marker> markers,
        LatLng origin, int maxDistanceMeters) {
    List<Marker> surroundingMarkers = surroundingMarkers = new ArrayList<Marker>();
    if (markers == null) return surroundingMarkers ;


        for (Marker marker : markers) {

            double dist = distBetween(origin, marker.getPosition());

            if (dist < getHydrantsLoadradius()) {
                surroundingMarkers.add(marker);
            }
        }


    return surroundingMarkers;
}

private float distBetween(LatLng pos1, LatLng pos2) {
    return distBetween(pos1.latitude, pos1.longitude, pos2.latitude,
            pos2.longitude);
}

/** distance in meters **/
private float distBetween(double lat1, double lng1, double lat2, double lng2) {
    double earthRadius = 3958.75;
    double dLat = Math.toRadians(lat2 - lat1);
    double dLng = Math.toRadians(lng2 - lng1);
    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
            + Math.cos(Math.toRadians(lat1))
            * Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2)
            * Math.sin(dLng / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double dist = earthRadius * c;

    int meterConversion = 1609;

    return (float) (dist * meterConversion);
}

在使用上述任何一种过滤算法后,再次使用众所周知的LatLngBounds来确定需要缩放的程度。


会尽力回复您,感谢反馈 :) - Vincent Mimoun-Prat
很酷 :) 我刚刚添加了更多的文本来解释我的想法。 - cYrixmorten
我刚发现了一个错误,等我纠正完之后再继续。 - cYrixmorten
完成,如果有问题请报告。 - cYrixmorten
我最终使用了自己的算法,但是你的算法给了我一些想法。作为你的算法优化,你不必将所有标记与所有标记进行比较,你的循环可以像我的算法那样,降低算法的复杂度。谢谢。 - Vincent Mimoun-Prat
我自己没有使用过昂贵的那个,因为我知道我想要放大的位置。看起来你找到了一个好的解决方案,很高兴我能帮助 :) 今天发现了这个,对于某些情况似乎也很有前途:https://dev59.com/J3bZa4cB1Zd3GeqPCCDG - cYrixmorten

0
Display display = getWindowManager().getDefaultDisplay(); 
        Point size = new Point();
        display.getSize(size);
        int ancho = size.x;
        int alto =size.y;
List<LatLng> copiedPoints = new ArrayList<LatLng>();
        copiedPoints.add(origin);
        copiedPoints.add(dest);

centerIncidentRouteOnMap(copiedPoints, ancho, alto);

....

public void centerIncidentRouteOnMap(List<LatLng> copiedPoints, int ancho, int alto) {
    double minLat = Integer.MAX_VALUE;
    double maxLat = Integer.MIN_VALUE;
    double minLon = Integer.MAX_VALUE;
    double maxLon = Integer.MIN_VALUE;
    for (LatLng point : copiedPoints) {
        maxLat = Math.max(point.latitude, maxLat);
        minLat = Math.min(point.latitude, minLat);
        maxLon = Math.max(point.longitude, maxLon);
        minLon = Math.min(point.longitude, minLon);
    }
    final LatLngBounds bounds = new LatLngBounds.Builder().include(new LatLng(maxLat, maxLon)).include(new LatLng(minLat, minLon)).build();
    map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds,ancho, alto, 50));
}

0

基于cYrixmorten的一些想法,我简化了问题,因为我知道地图可以容纳至少4000公里的表面。所以这里是构建忽略网络摄像头列表的函数(然后我只需忽略该网络摄像头用于相机边界计算,但仍添加标记,以便在用户移动时出现在地图上)。

private List<Webcam> buildIgnoredWebcamsList(List<Webcam> webcams) {
    if (webcams == null || webcams.size() < 2) return Lists.newArrayList();

    int webcamCount = webcams.size();

    // Number of conflicts (distance > 4000 km) for the camera at index #
    float averageConflictCount = 0;
    int[] conflictCount = new int[webcamCount];
    Arrays.fill(conflictCount, 0);

    // Find number of conflicts between camera pairs
    float[] distance = new float[1];

    for (int i = 0; i < webcamCount - 1; ++i) {
        Webcam a = webcams.get(i);

                    // We don't have to start from 0, compare a and b only once
        for (int j = i + 1; j < webcamCount; ++j) {
            Webcam b = webcams.get(j);
            Location.distanceBetween(a.getLatitude(), a.getLongitude(), b.getLatitude(), b.getLongitude(), distance);

            // We have a conflict between a and b if they are more than 4000km away
            if (distance[0] > 4000 * 1000) {
                conflictCount[i] += 1;
                conflictCount[j] += 1;
                averageConflictCount += 2;
            }
        }
    }
    averageConflictCount /= webcamCount;

    // Exclude all webcams with a number of conflicts greater than the average
    List<Webcam> ignoredCamerasForBounds = Lists.newArrayList();

    for (int i = 0; i < webcamCount; ++i) {
        if (conflictCount[i] > averageConflictCount) {
            ignoredCamerasForBounds.add(webcams.get(i));
        }
    }

    return ignoredCamerasForBounds;
}

今天发现:虽然未经测试,但似乎Android Maps Extensions可以做各种聪明的事情。其中之一是根据标记之间的距离定义集群并动态加载标记。如果它像看起来那样容易,那么选择最大的集群并放大它就变得毫不费力了。 - cYrixmorten
我已经使用了Clusterkraf这个很好的库,但是它并没有解决问题,因为它的算法是基于像素距离计算集群。理想情况下,我们基本上需要计算集群,并只缩放到最密集的集群。我的解决方案现在可以解决问题,而且相当廉价,如果需要,我以后还会做得更好。 - Vincent Mimoun-Prat
是的,我也会坚持使用我的getSurroundingMarkers函数。这只是让你意识到它的存在,因为直到今天我才知道它。顺便说一句,双重for循环的想法不错,这样时间复杂度只有O(n+nlog(n)),比之前好多了 :) - cYrixmorten

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