我有一个数据库,它存储了很多标记,我正在Android上开发一个包含Google Maps Android V2的应用程序。
我一直在搜索一种方法,不加载存储在这个数据库中的所有标记,但只有那些在屏幕上可见,但没有找到,我怎么能这样做?
答案 0 :(得分:0)
我认为没有办法批量执行此操作,您必须逐个操作每个标记。在屏幕上获取地图区域的边界:Android Google Maps get Boundary Co-ordinates,然后使用LatLngBounds
检查每个标记的LatLng
,看看是否要显示它。
答案 1 :(得分:0)
有一种方法可以找到可见区域中所有标记的位置。为此,您必须遍历所有LatLng值。
for(int i=0; i < latLngListsize(); i++)
mGoogleMap.getProjection().getVisibleRegion().latLngBounds.contains(new LatLng(latLngListsize.get(i).getLatitude(), latLngListsize.get(i).getLongitude()));
希望这对您有帮助!
答案 2 :(得分:0)
所以真正的问题是您想要调整查询,以过滤掉您在地图上看不到的内容,从而不必加载或遍历它们。
由于这不必具有很高的精度,因此可以使用geohash以及其经/纬度存储标记点。
Geohash确实很容易在查询中使用以限制记录,因为它是坐标的顺序字符串表示形式。
例如,如果您有两个标记记录,它们的地理哈希分别为2j3l45d6和2j3lyt76,并且您在该点周围的半径为60km,那么您可以设置以下查询:
SELECT * FROM points WHERE geohash LIKE "2j3%"
Geohash并不是那么精确,例如,在3到2之间,可见半径从78km跃升到630km,但是对于大多数合理的地图相机缩放级别而言,这足以限制您要查询的点数。
更新:
我是来这里寻找类似问题的解决方案的,在写完此书之后,就走了并实施了它。这是第一次破解,但是我想我会与有兴趣尝试该代码的人共享该代码。
这里有一些额外的代码,例如能够以米为单位指定返回半径,这并不是严格要求的,但是我在其他地方使用了。
第一步是选择一个GeoHash库。由于我使用的是Kotlin,因此我选择了Android-Kotlin-Geohash,但是几乎所有库都可以使用。
接下来,我为VisibleRegion类设置了几个扩展,这是您从Android上的GoogleMap对象获得的扩展:
/**
* Returns a geohash based on the point, with precision great enough to cover the radius.
*/
fun VisibleRegion.visibleRadiusGeohash(point: LatLng): GeoHash? {
return GeoHash(
lat = point.latitude,
lon = point.longitude,
charsCount = when (calculateVisibleRadius(UnitOfMeasure.KILOMETER)) {
in 0.0..0.019 -> 8
in 0.02..0.076 -> 7
in 0.077..0.61 -> 6
in 0.62..2.4 -> 5
in 2.5..20.0 -> 4
in 21.0..78.0 -> 3
in 79.0..630.0 -> 2
else -> 1 // 2,500km and greater
}
)
}
/**
* @param units one of UnitOfMeasure.KILOMETER or UnitOfMeasure.METER.
* Any other unit will throw an IllegalArgumentException.
*
* This code adapted from https://stackoverflow.com/a/49413106/300236
*/
@Throws(IllegalArgumentException::class)
fun VisibleRegion.calculateVisibleRadius(units: UnitOfMeasure): Double {
val diameter = FloatArray(1)
Location.distanceBetween(
(farLeft.latitude + nearLeft.latitude) / 2,
farLeft.longitude,
(farRight.latitude + nearRight.latitude) / 2,
farRight.longitude,
diameter
)
// visible radius in meters is the diameter / 2, and /1000 for Km:
return when (units) {
UnitOfMeasure.KILOMETER -> (diameter[0] / 2 / 1000).toDouble()
UnitOfMeasure.METER -> (diameter[0] / 2).toDouble()
else -> throw IllegalArgumentException("Valid units are KILOMETER and METER only")
}
}
由于我使用的是MVVM,因此我建立了具有一些可观察属性的视图模型。一个是我要使用的过滤器,另一个是我要在地图上绘制的点的列表。请注意,对于过滤器,null是一个完全有效的值,它将导致所有标记均被加载:
val markerFilter: MutableLiveData<String?> = MutableLiveData<String?>(null)
val markers: LiveData<List<Mark>> =
Transformations.switchMap(markerFilter) { geoHash ->
liveData(Dispatchers.IO) {
emitSource(markRepo.loadMarkerList(geohash = geoHash))
}
}
如果您不熟悉ViewModel,那么只要“ markerFilter”发生更改,该代码就会告诉“标记”列表进行更新。
接下来,我需要知道摄影机何时移动且地图视口发生变化,因此在我的地图视图片段中,我监听地图移动事件。
override fun onCameraMove() {
mapView?.let { map ->
val visibleRegion: VisibleRegion = map.projection.visibleRegion
val centreOfVisibleRegion = visibleRegion.latLngBounds.center
val geohash = visibleRegion.visibleRadiusGeohash(centreOfVisibleRegion)
model. markerFilter.postValue(geohash.toString())
}
}
(通过这段代码,我可以看到一个很好的重构可以简化此过程,但是直到我返回并执行之前,它都可以工作)
...,当然,我需要观察标记列表,以便在列表更改时可以将其添加到地图中。
model.markers.observe(viewLifecycleOwner, Observer { list ->
list?.let {addMarkersToMap(it)}
})
最后一点是设置存储库以运行查询,因此我的MarkerRepository类如下所示:
suspend fun loadMarkerList(geohash: String?): LiveData<List<Mark>> {
return withContext(io) {
dao.loadMarkerList(geohash ?: "")
}
}
请注意,我没有传递空过滤器值,如果为空,则将其设置为空字符串,因此生成的LIKE准则将起作用。
...,我的MarkerDao看起来像:
@Query("SELECT * FROM marker WHERE geohash LIKE :filter || '%' ORDER BY geohash")
fun loadMarkerList(filter: String): LiveData<List<Mark>>
希望这能给其他人足够的解决方案,让他们自己尝试一下。