Android Maps v2 - 包含大多数标记的动画摄像头
我有一组来自需要在地图上显示的web服务的点。Android Maps v2 - 包含大多数标记的动画摄像头
我有一个目前的解决方案很好地工作在大多数情况下,使用众所周知的LatLngBounds.Builder
,CameraUpdateFactory.newLatLngBounds
和map.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;
}
在我之前的代码中发现一个错误,并决定坐下来重写它。
我在做过类似的事情之前,我有4500个标记,并希望选择特定位置的特定距离内的那些标记。采取了该代码,并将其推广到与任何类型的标记一起使用。
,我将在下面发布的代码包含两种方法,您可以使用:
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,以确定你需要多少使用上述的过滤算法相继放大。
将所有经纬度在列表中的标志物的并把它们传递给该方法,并在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));
}
据我所知,这有点像他已经做过的那样 – cYrixmorten
如果他遇到了这个问题,那么请让他给我提供一些LatLng点数来重现问题,但我不能重现它。 –
这里有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));
}
今天发现:未经测试,虽然看起来Android地图扩展可以做各种聪明的事情。其中之一是根据标记之间的距离和动态加载标记来定义聚类。如果它看起来那么简单,那么选择最大的群集并放大它突然变得毫不费力。 – cYrixmorten
我已经使用clusterkraf,好的库,但并没有解决问题,因为它们的算法是基于像素距离来计算簇。理想情况下,我们基本上需要计算群集,并只放大人口最多的群集。我的解决方案现在可以做到,而且价格相当便宜,如果需要,我会稍后做得更好。 –
是的,我也会坚持,我的getSurroundingMarkers也是。这也只是为了让你意识到这一点,因为我直到今天。顺便说一句,双重forloop好主意,这使得它只有,什么O(n + nlog(n)),无论如何,好多了:) – cYrixmorten