如何实现Nearby以矩形范围加载屏幕内所有标记点替代圆形
矩形范围Nearby地点查询实现方案
核心思路
放弃原有的圆形半径查询,改用地图可视区域的矩形边界范围进行查询:
- 直接获取当前屏幕对应的经纬度边界(minLat, maxLat, minLng, maxLng)
- 修改SPARQL查询语句,过滤坐标在矩形范围内的POI
- 调整Java API参数传递逻辑,适配矩形边界参数
1. 修改Java查询方法
将原方法中的radius参数替换为矩形的四个边界值,并新增重载方法适配地图边界对象:
/** * 按矩形范围获取附近地点 * * @param minLat 最小纬度 * @param maxLat 最大纬度 * @param minLng 最小经度 * @param maxLng 最大经度 * @param language 语言 * @param shouldQueryForMonuments 是否查询古迹 * @param customQuery 自定义SPARQL查询 * @return 地点列表 * @throws Exception 请求异常 */ @Nullable public List<Place> getNearbyPlacesByBounds(double minLat, double maxLat, double minLng, double maxLng, final String language, final boolean shouldQueryForMonuments, final String customQuery) throws Exception { Timber.d("Fetching nearby items in bounds: minLat=%.4f, maxLat=%.4f, minLng=%.4f, maxLng=%.4f", minLat, maxLat, minLng, maxLng); Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null)); final String wikidataQuery; if (customQuery != null) { wikidataQuery = customQuery; } else if (!shouldQueryForMonuments) { wikidataQuery = FileUtils.readFromResource("/queries/nearby_query_bounds.rq"); } else { wikidataQuery = FileUtils.readFromResource("/queries/nearby_query_monuments_bounds.rq"); } // 替换SPARQL模板中的边界参数 final String query = wikidataQuery .replace("${MIN_LAT}", String.format(Locale.ROOT, "%.4f", minLat)) .replace("${MAX_LAT}", String.format(Locale.ROOT, "%.4f", maxLat)) .replace("${MIN_LNG}", String.format(Locale.ROOT, "%.4f", minLng)) .replace("${MAX_LNG}", String.format(Locale.ROOT, "%.4f", maxLng)) .replace("${LANG}", language); final HttpUrl.Builder urlBuilder = HttpUrl .parse(sparqlQueryUrl) .newBuilder() .addQueryParameter("query", query) .addQueryParameter("format", "json"); final Request request = new Request.Builder() .url(urlBuilder.build()) .build(); final Response response = okHttpClient.newCall(request).execute(); if (response.body() != null && response.isSuccessful()) { final String json = response.body().string(); final NearbyResponse nearbyResponse = gson.fromJson(json, NearbyResponse.class); final List<NearbyResultItem> bindings = nearbyResponse.getResults().getBindings(); final List<Place> places = new ArrayList<>(); for (final NearbyResultItem item : bindings) { final Place placeFromNearbyItem = Place.from(item); placeFromNearbyItem.setMonument(shouldQueryForMonuments && item.getMonument() != null); places.add(placeFromNearbyItem); } return places; } throw new Exception(response.message()); } /** * 重载方法:按矩形范围获取附近地点(无自定义查询) */ @Nullable public List<Place> getNearbyPlacesByBounds(double minLat, double maxLat, double minLng, double maxLng, final String language, final boolean shouldQueryForMonuments) throws Exception { return getNearbyPlacesByBounds(minLat, maxLat, minLng, maxLng, language, shouldQueryForMonuments, null); } /** * 重载方法:通过Mapbox的LatLngBounds对象查询 */ @Nullable public List<Place> getNearbyPlacesByBounds(LatLngBounds bounds, String language, boolean shouldQueryForMonuments) throws Exception { return getNearbyPlacesByBounds( bounds.getSouthwest().getLatitude(), bounds.getNortheast().getLatitude(), bounds.getSouthwest().getLongitude(), bounds.getNortheast().getLongitude(), language, shouldQueryForMonuments ); }
2. 修改SPARQL查询语句
创建新的SPARQL模板文件(如nearby_query_bounds.rq),使用坐标范围过滤替代圆形查询:
SELECT ?item (SAMPLE(?location) as ?location) (SAMPLE(?label) AS ?label) (SAMPLE(?description) AS ?description) (SAMPLE(?class) AS ?class) (SAMPLE(?classLabel) AS ?classLabel) (SAMPLE(?pic) AS ?pic) (SAMPLE(?destroyed) AS ?destroyed) (SAMPLE(?endTime) AS ?endTime) (SAMPLE(?wikipediaArticle) AS ?wikipediaArticle) (SAMPLE(?commonsArticle) AS ?commonsArticle) (SAMPLE(?commonsCategory) AS ?commonsCategory) WHERE { ?item wdt:P625 ?location. # 提取坐标的纬度和经度 BIND(geof:latitude(?location) AS ?lat) BIND(geof:longitude(?location) AS ?lng) # 过滤矩形范围:纬度在MIN_LAT和MAX_LAT之间,经度在MIN_LNG和MAX_LNG之间 FILTER(?lat >= ${MIN_LAT} && ?lat <= ${MAX_LAT}) FILTER(?lng >= ${MIN_LNG} && ?lng <= ${MAX_LNG}) # 保留原有的其他查询逻辑(标签、描述等) OPTIONAL { ?item rdfs:label ?label. FILTER(LANG(?label) = "${LANG}") } OPTIONAL { ?item schema:description ?description. FILTER(LANG(?description) = "${LANG}") } # ... 其余原有OPTIONAL子句保持不变 } GROUP BY ?item
注:古迹查询的模板
nearby_query_monuments_bounds.rq只需在上述基础上添加古迹相关的过滤条件即可
3. 获取地图可视区域边界
在地图页面中,直接获取Mapbox当前可视区域的LatLngBounds,替代原有的中心点+半径计算:
// 获取当前地图的可视区域边界 LatLngBounds visibleBounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; // 调用新的查询方法 placeRepository.getNearbyPlacesByBounds(visibleBounds, "zh-CN", false) .addOnSuccessListener(places -> { // 更新地图标记点 updateMapMarkers(places); }) .addOnFailureListener(error -> { Timber.e(error, "Failed to fetch nearby places"); });
4. 替换圆形坐标计算方法
删除原有的createCircleArray方法,改用矩形边界直接生成地图覆盖物(如果需要显示查询范围):
/** * 创建矩形范围的地图覆盖物坐标 * @param bounds 地图可视区域边界 * @return 矩形的四个顶点坐标 */ public static List<com.mapbox.mapboxsdk.geometry.LatLng> createBoundsArray(LatLngBounds bounds) { List<com.mapbox.mapboxsdk.geometry.LatLng> boundsArray = new ArrayList<>(); boundsArray.add(bounds.getSouthwest()); boundsArray.add(new com.mapbox.mapboxsdk.geometry.LatLng(bounds.getSouthwest().getLatitude(), bounds.getNortheast().getLongitude())); boundsArray.add(bounds.getNortheast()); boundsArray.add(new com.mapbox.mapboxsdk.geometry.LatLng(bounds.getNortheast().getLatitude(), bounds.getSouthwest().getLongitude())); boundsArray.add(bounds.getSouthwest()); // 闭合矩形 return boundsArray; }
优势说明
- 全屏覆盖:直接匹配地图可视区域,确保标记点填满屏幕
- 性能更优:矩形查询避免了圆形查询中角落区域的无效数据请求,减少查询返回量
- 逻辑更直观:无需计算半径与坐标的转换,直接使用地图原生边界参数
内容的提问来源于stack exchange,提问作者Avanish Yadav
相关产品推荐
相关产品推荐

