我有一个带有谷歌地图的Android地图,它可以根据用户屏幕位置从服务器动态加载标记。因此,例如,如果用户移动地图,我只需向服务器发出请求,然后发送屏幕边界,并基于此获得标记数据(id 和坐标),稍后将其解析并创建为实际指针。问题是,当用户返回到同一区域(之前创建了哪些标记)时,我仍然发出相同的请求并获取相同的数据(但显然我不会让重新创建该标记,所以我只是运行 for 循环遍历那里的所有标记)在地图上,检查地图标记 id 是否相等,服务器发送数据标记 id,如果相等,我只需中断循环)
try {
Collection<MarkerItemData> mapMarkers = algorithm.getItems();
JSONObject jsonObject = new JSONObject(strings[0]);
JSONArray respondArray = jsonObject.getJSONArray("respond");
list = new ArrayList();
for (int i = 0; i < respondArray.length(); i++) {
JSONObject station = respondArray.getJSONObject(i);
int id = station.getInt("_id");
boolean skip = false;
for (final MarkerItemData m : mapMarkers) {
if (m.getId() == id) {
skip = true;
break;
}
}
}
}
但是我不认为这种方法是最好的。我还有其他应该可行的想法(至少我认为)
那么这些想法中哪一个是最好的呢?还有其他想法吗? (对不起我的英语)
将屏幕边界发送到服务器和屏幕上当前可见标记的 id-s 是您最好的选择。但仍然存在一些麻烦的问题。如何找到屏幕边界指定范围内包含的所有标记?如果新标记到达您的服务器或某些标记被删除怎么办?在这种情况下,你能提供一个可维护的结构吗?还是你会一一测试数据库中与标记相对应的每个点,看看它是否位于范围内?考虑到所有这些,您需要找到一种方法来优化存储和查询点,即点标记的纬度和经度对。您应该使用一种常见的空间索引方法来执行空间索引。
空间索引的方法有很多,根据用例,一种方法可能比另一种方法稍好。长话短说,由于您需要在这种情况下查询范围,因此您应该实现四叉树。四叉树被称为树形数据结构,其中每个内部节点恰好有四个子节点(西北、东北、西南、东南)。如果你对这个结构没有了解,我相信你一个小时就能明白它的基础知识,但从头开始详细解释会导致我损失太多时间。因此,我跳过有关四叉树的实现细节。有几个来源已经比我更好地解释了这种结构,并且您可以轻松找到开源库。
我只会为你的四叉树提供伪Java方法来查找出现在屏幕边界范围内的所有点,不包括上一个屏幕中已经存在的点:
ArrayList<LatLng> queryRange(QuadLevel level, float[] screenBounds, ArrayList<LatLng> prevPoints) {
// Initialize a list to hold the found points
ArrayList<LatLng> pointsInRange = new ArrayList<>();
if (!quadtreeBounds.intersects(screenBounds))
return pointsInRange;
// Find the points that are in the current quad level
for (LatLng point : level.getPoints()) {
// If the current point is in screen bounds and it is not contained by prevPoints
if (point.isInRange(screenBounds)
&& !prevPoints.contains(point))
pointsInRange.add(point);
}
// If there are no children, return
if (level.hasNoChildren())
return pointsInRange;
// Else, continue to look up children
pointsInRange.addAll(queryRange(level.northwest, screenBounds, prevPoints));
pointsInRange.addAll(queryRange(level.northeast, screenBounds, prevPoints));
pointsInRange.addAll(queryRange(level.southwest, screenBounds, prevPoints));
pointsInRange.addAll(queryRange(level.southeast, screenBounds, prevPoints));
return pointsInRange;
}
扩展
HashSet<MarkerOptions>
,以便在添加 MarkerOptions
时将其添加到地图中。
class MarkerSet extends HashSet<MarkerOptions> {
@Override
public boolean add(MarkerOptions markerOptions) {
boolean didAdd = super.add(markerOptions);
if(didAdd) {
mMap.addMarker(markerOptions);
}
return didAdd;
}
}
在地图活动中维护一个 LatLngBounds 对象。此 LatLngBounds 对象将存储您已从服务器请求数据的边界。
当地图的相机移动时,检查新边界是否在当前存储的边界内,如果不请求服务器,则增加边界,否则不请求服务器。
mMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
@Override
public void onCameraMove() {
LatLngBounds currentBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
if (mBounds == null) {
mBounds = currentBounds;
// Perform server request and get markers passing in currentBounds
// Following to be performed in server request's callback
MarkerOptions markerOptions = new MarkerOptions();
// set marker options here
mMarkerSet.add(markerOptions);
} else {
if (!(mBounds.contains(currentBounds.northeast) || mBounds.contains(currentBounds.southwest))) {
mBounds = mBounds.including(currentBounds.northeast);
mBounds = mBounds.including(currentBounds.southwest);
// Perform server request and get markers passing in currentBounds
// Following to be performed in server request's callback
MarkerOptions markerOptions = new MarkerOptions();
// set marker options here
mMarkerSet.add(markerOptions);
}
}
}
});
确保您始终调用服务器请求
currentBounds
。如果当 currentBounds
和 currentBounds
重叠时设法缩短 mBounds
,则可以改进该算法。
假设
请注意,这里的假设是当相机移动时,
currentBounds
会稍微从mBounds
中掉出来。否则,不会加载新标记。
参考图像,如果用户从可见的绿色区域移动到橙色,则 mBounds
值将是蓝色矩形,因此白色区域不会被覆盖。然而,在实践中,完美地水平或垂直移动相机并不容易,这将导致调用服务器方法并加载未冒险区域的新标记。
希望这有帮助。 :)
将屏幕上的所有可见标记存储在列表或本地数据库或某个位置中,如果不存在来自服务器的请求,则迭代列表中的现有标记。