在Java Spring Boot(Gradle)中用Mapbox获取指定坐标X公里内公共区域
问题:在Java/Spring Boot(Gradle)项目中获取指定坐标X公里内的公共区域POI列表
已尝试的两种实现及问题
方案1:使用Mapbox Geocoding SDK,结果范围偏差
public List<Point> getCoordinates2() { List<Point> points=new ArrayList<>(); MapboxGeocoding mapboxGeocoding = MapboxGeocoding.builder() .accessToken(accessToken) .query(GeocodingCriteria.TYPE_POI) .country("IN") // ISO 3166 alpha 2 country code for India .proximity(Point.fromLngLat(78.4983, 17.4399)) .build(); try { GeocodingResponse response = mapboxGeocoding.executeCall().body(); if (response != null && response.features().size() > 0) { for (int i = 0; i < response.features().size(); i++) { CarmenFeature feature = response.features().get(i); String placeName = feature.placeName(); Point point = (Point) feature.geometry(); points.add(point); String coordinates = String.format("%f,%f", point.longitude(), point.latitude()); System.out.println(placeName + ": " + coordinates); } } } catch (IOException e) { } return points; }
问题:仅设置proximity参数无法限制POI范围,返回结果分散到印度多个城市,不符合指定坐标附近的需求。
返回结果示例:
Point Pleasant Restaurant, Hyderabad, Telangana 500034, India: 78.444396,17.415243 PNT Colony, Vijayawada, NTR, Andhra Pradesh, India: 80.639416,16.538129 Poibeda, Machh Kund, Koraput, Odisha, India: 82.540986,18.584619 Poigai, Vellore, Vellore, Tamil Nadu, India: 79.053124,12.915003 Poigainallur, Arakonam, Ranipet, Tamil Nadu, India: 79.564364,12.903959
方案2:使用OkHttpClient调用Mapbox API,结果数量受限
public List<Point> getCoordinates() { List<Point> points = new ArrayList<>(); OkHttpClient client = new OkHttpClient(); String parksTemplesHotelsInJPnagar = "https://api.mapbox.com/geocoding/v5/mapbox.places/park%20and%20temple%20and%20hotels.json?bbox=77.449061%2C12.848347%2C77.695739%2C12.966143&limit=50&proximity=77.5724%2C12.907252&autocomplete=false&access_token="+accessToken; Request request = new Request.Builder() .url(parksTemplesHotelsInJPnagar) .build(); try { Response response = client.newCall(request).execute(); String responseBody = response.body().string(); JSONArray features = new JSONObject(responseBody).getJSONArray("features"); for (int i = 0; i < features.length(); i++) { JSONObject feature = features.getJSONObject(i); JSONArray coordinates = feature.getJSONArray("center"); Point point = Point.fromLngLat(coordinates.getDouble(0), coordinates.getDouble(1)); points.add(point); String placeName = feature.getString("text"); System.out.println(placeName + ": " + point.longitude() + ", " + point.latitude()); } } catch (IOException e) { e.printStackTrace(); } return points; }
问题:设置bbox后返回的POI符合范围,但实际仅返回10条结果,即使指定了limit=50,无法获取区域内全部目标POI。
返回结果示例:
Banashankari Temple: 77.573312, 12.915979 Ragiguda Temple: 77.593409, 12.91467 Dharmagiri Manjunathaswamy Temple: 77.564309, 12.921 Venkateshwara Swamy Temple: 77.589474, 12.910329 Laxman Rao Park - Pride of JAYANAGAR: 77.580493, 12.918529 Lakshman Rao Park Jayanagar: 77.578719, 12.918843 Raagi Gudda Prasanna Anjaneya Temple: 77.593239, 12.915066 KS Layout Park 2: 77.559655, 12.906428 Devagiri Temple: 77.567178, 12.926082 ISKCON @ Vaikunta Hill: 77.549434, 12.89312
解决方案:修正范围偏差与结果数量限制
1. 解决范围偏差:用bbox严格限制区域
proximity仅用于提升结果排序优先级,无法限制POI范围。要获取X公里内的POI,需将半径转换为经纬度边界框(bbox):
- 用Haversine公式计算中心坐标对应的西南、东北端点(小半径场景下足够准确,高精度场景可引入GeoTools等地理库)
- 在API请求中添加
bbox参数,确保返回POI严格落在目标区域内
2. 解决数量限制:合理设置limit与分页
- Mapbox Geocoding API最大
limit值为50,若设置后仍返回少量结果,可能是目标区域内匹配的POI数量本身较少,需优化查询关键词(如用bus station替代模糊的poi) - 若POI数量超过50,通过
offset参数分页查询,循环获取全部结果
3. 优化后的Mapbox SDK实现示例
import java.util.ArrayList; import java.util.List; import java.io.IOException; import com.mapbox.api.geocoding.v5.MapboxGeocoding; import com.mapbox.api.geocoding.v5.models.GeocodingResponse; import com.mapbox.geojson.Point; import com.mapbox.geojson.BoundingBox; import com.mapbox.api.geocoding.v5.models.CarmenFeature; public class MapboxPoiService { private String accessToken; public MapboxPoiService(String accessToken) { this.accessToken = accessToken; } public List<CarmenFeature> getNearbyPois(double centerLng, double centerLat, int radiusKm, List<String> poiTypes) { List<CarmenFeature> poiFeatures = new ArrayList<>(); // 计算半径对应的bbox边界 double earthRadius = 6371; double latRadian = Math.toRadians(centerLat); double lngDelta = Math.toDegrees(radiusKm / (earthRadius * Math.cos(latRadian))); double latDelta = Math.toDegrees(radiusKm / earthRadius); double minLng = centerLng - lngDelta; double minLat = centerLat - latDelta; double maxLng = centerLng + lngDelta; double maxLat = centerLat + latDelta; // 构建多类型POI查询 String query = String.join(",", poiTypes); // 首次请求 MapboxGeocoding geocoding = buildGeocodingRequest(query, minLng, minLat, maxLng, maxLat, 0); try { GeocodingResponse response = geocoding.executeCall().body(); if (response != null && !response.features().isEmpty()) { poiFeatures.addAll(response.features()); // 分页获取剩余结果 int total = response.features().size(); int offset = 50; while (total == 50) { geocoding = buildGeocodingRequest(query, minLng, minLat, maxLng, maxLat, offset); response = geocoding.executeCall().body(); if (response == null || response.features().isEmpty()) break; poiFeatures.addAll(response.features()); total = response.features().size(); offset += 50; } } } catch (IOException e) { e.printStackTrace(); } return poiFeatures; } private MapboxGeocoding buildGeocodingRequest(String query, double minLng, double minLat, double maxLng, double maxLat, int offset) { return MapboxGeocoding.builder() .accessToken(accessToken) .query(query) .country("IN") .bbox(BoundingBox.fromLngLat(minLng, minLat, maxLng, maxLat)) .limit(50) .offset(offset) .autocomplete(false) .build(); } // 调用示例 public static void main(String[] args) { MapboxPoiService service = new MapboxPoiService("YOUR_MAPBOX_ACCESS_TOKEN"); List<String> targetTypes = List.of("park", "theater", "bus station", "railway station", "temple"); List<CarmenFeature> pois = service.getNearbyPois(78.4983, 17.4399, 10, targetTypes); for (CarmenFeature poi : pois) { Point point = (Point) poi.geometry(); System.out.printf("%s: %.6f, %.6f%n", poi.placeName(), point.longitude(), point.latitude()); } } }
关键说明
- bbox计算:手动实现为近似值,适合小半径场景;若需高精度,可引入GeoTools等专业地理库
- POI筛选:使用具体关键词(如
railway station)而非TYPE_POI,能精准匹配目标公共区域 - 分页逻辑:通过
offset参数循环请求,确保获取区域内全部符合条件的POI
内容的提问来源于stack exchange,提问作者Balu Mahendra
相关产品推荐
相关产品推荐

