Android Maps v2 - 包含大多数标记的动画摄像头

问题描述:

我有一组来自需要在地图上显示的web服务的点。Android Maps v2 - 包含大多数标记的动画摄像头

我有一个目前的解决方案很好地工作在大多数情况下,使用众所周知的LatLngBounds.BuilderCameraUpdateFactory.newLatLngBoundsmap.animateCamera

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

所以我的问题是:

有没有办法让地图缩小远远不够,这样我可以看到所有的点(会者优先)

或者:这将是最好的方法来过滤那些只有几个点距离大多数很远的情况,并选择一组放大点(在我的例子中,我会选择放大法国的10个点并忘记夏威夷的点)。

基于cYrixmorten的一些想法,我简化了这个问题,因为我知道地图可以容纳至少4000km的表面。因此,这里是建立被忽略的摄像头列表的功能(然后我简单地忽略用于摄像头界限计算的摄像头,但是如果用户移动,仍然添加标记以使其位于地图上)。

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; 
} 
+0

今天发现:未经测试,虽然看起来Android地图扩展可以做各种聪明的事情。其中之一是根据标记之间的距离和动态加载标记来定义聚类。如果它看起来那么简单,那么选择最大的群集并放大它突然变得毫不费力。 – cYrixmorten

+0

我已经使用clusterkraf,好的库,但并没有解决问题,因为它们的算法是基于像素距离来计算簇。理想情况下,我们基本上需要计算群集,并只放大人口最多的群集。我的解决方案现在可以做到,而且价格相当便宜,如果需要,我会稍后做得更好。 –

+0

是的,我也会坚持,我的getSurroundingMarkers也是。这也只是为了让你意识到这一点,因为我直到今天。顺便说一句,双重forloop好主意,这使得它只有,什么O(n + nlog(n)),无论如何,好多了:) – cYrixmorten

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

我在做过类似的事情之前,我有4500个标记,并希望选择特定位置的特定距离内的那些标记。采取了该代码,并将其推广到与任何类型的标记一起使用。

All markersSelected markers with getSurroundingMarkers

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

selectLowDistanceMarkers每个标记之间

测量距离,只有选择那些不与任何其他标记都有很长的距离。这需要O(n + n^2)运行时间,因为之后每个标记与检查之间进行比较。

getSurroundingMarkers

如果你已经知道你想放大到一个postition,则此方法与上面相同。这种方法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,以确定你需要多少使用上述的过滤算法相继放大。

+0

会尽量回复你,感谢输入:) –

+0

酷:)只是增加了一些更多的文字来解释我的想法 – cYrixmorten

+0

只是看上一个错误我自己,直到我纠正它 – cYrixmorten

将所有经纬度在列表中的标志物的并把它们传递给该方法,并在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)); 
    } 
+1

据我所知,这有点像他已经做过的那样 – cYrixmorten

+0

如果他遇到了这个问题,那么请让他给我提供一些LatLng点数来重现问题,但我不能重现它。 –

+0

这里有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)); 
}