【问题标题】:Android Maps v2 - animate camera to include most markersAndroid Maps v2 - 动画相机以包含大多数标记
【发布时间】:2015-04-28 22:56:37
【问题描述】:

我有一组来自 web 服务的点需要显示在地图上。

我目前的解决方案适用于大多数情况,使用众所周知的LatLngBounds.BuilderCameraUpdateFactory.newLatLngBoundsmap.animateCamera

我有一些情况会出现问题:当点太远时,地图以这些点的重心为中心以最大缩放级别为中心。例如:我在法国有10分,在夏威夷有2分。地图以最小缩放级别或多或少地以加勒比海为中心。因此在屏幕上我什么都没有显示,用户必须滚动才能真正看到那里的东西。

所以我的问题是:

有没有办法让地图缩小到足够远,以便我可以看到所有点(这是首选)

或者:这将是过滤掉那些只有几个点与大多数点相距甚远的情况并选择一组点进行放大的最佳方法(在我的示例中,我会选择放大 10点在法国,忘记夏威夷的)。

【问题讨论】:

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


    【解决方案1】:

    将标记的所有 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 点来重现问题,但我无法重现。
    • 这里有 4 个点:巴黎、里昂、马赛(法国)和檀香山(夏威夷)。现在,我做了你提到的事情,预计地图将以最小缩放级别以墨西哥为中心。所以没有一个标记是可见的。相反,我想忽略檀香山,只计算法国的界限。
    • @3amoura 不是粗鲁之类的,只是想让你知道,看来我是对的
    • @MarvinLabs 抱歉,起初我没有理解您的问题,我的意思是如果您的示例中存在像“火奴鲁鲁”这样的远距离标记,您想忽略的部分
    • @cYrixmorten 没有什么我的朋友 ;) 没关系,在理解了这个问题之后,我想你的答案应该有效
    【解决方案2】:

    在我之前的代码中发现了一个错误,并决定坐下来重写它。

    我之前做过类似的事情,我有大约 4500 个标记,并想选择在特定位置一定距离内的那些。采用该代码并将其概括为与任何类型的标记一起使用。

    我将在下面发布的代码包含您可以使用的两种方法:

    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 来确定在使用上述过滤算法之一后需要缩放多少。

    【讨论】:

    • 会尽力回复您,感谢您的意见:)
    • 酷 :) 刚刚添加了一些文字来解释我的想法
    • 自己发现了一个错误,等待我纠正它
    • 已完成,如有问题请反馈
    • 我终于使用了自己的算法,但你的给了我一些想法。作为对算法的优化,您不必将所有标记与所有标记进行比较,您的循环可以像我的算法一样,这降低了算法的复杂性。谢谢。
    【解决方案3】:

    根据 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 地图扩展程序可以做各种聪明的事情。其中之一是根据标记之间的距离动态定义集群并加载标记。如果它看起来很简单,那么突然选择最大的集群并放大它是不费吹灰之力的。
    • 我已经使用了 clusterkraf,很好的库,但没有解决问题,因为他们的算法基于像素距离计算集群。理想情况下,我们确实需要计算集群并且只放大人口最多的集群。我的解决方案现在可以解决问题,而且非常便宜,如果需要,我会在以后做得更好。
    • 是的,我也会坚持我的,getSurroundingMarkers 就是.. 这也只是为了让你意识到这一点,直到今天我才知道。顺便说一句,双 forloop 的好主意,这使得它只有 O(n+nlog(n)),无论如何,要好得多:)
    【解决方案4】:
    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));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      相关资源
      最近更新 更多