Spring Boot Telegram Bot:无需Google Maps API,如何从经纬度获取城市国家名?
无需依赖Google Maps API的经纬度转城市国家方案
方案一:使用OpenStreetMap Nominatim API
这是完全免费的反向地理编码服务,不需要API密钥,只需遵守基础使用规范(比如避免高频调用、设置明确的User-Agent标识)。
实现步骤(Spring Boot)
- 引入WebClient依赖(Spring WebFlux,也可替换为RestTemplate):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
- 编写地理编码服务类,调用接口并解析返回结果:
@Service public class ReverseGeocodingService { private final WebClient webClient; public ReverseGeocodingService(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder .baseUrl("https://nominatim.openstreetmap.org") .defaultHeader("User-Agent", "YourSpringBootTelegramBot/1.0") // 替换为你的应用标识 .build(); } public Optional<LocationDetails> getCityAndCountry(double latitude, double longitude) { try { String responseBody = webClient.get() .uri(uriBuilder -> uriBuilder .path("/reverse") .queryParam("lat", latitude) .queryParam("lon", longitude) .queryParam("format", "json") .queryParam("addressdetails", 1) // 要求返回详细地址信息 .build()) .retrieve() .bodyToMono(String.class) .block(); if (responseBody == null) { return Optional.empty(); } ObjectMapper objectMapper = new ObjectMapper(); JsonNode root = objectMapper.readTree(responseBody); JsonNode addressNode = root.get("address"); if (addressNode == null) { return Optional.empty(); } // 优先取city,无则降级取town/village String city = addressNode.has("city") ? addressNode.get("city").asText() : addressNode.has("town") ? addressNode.get("town").asText() : addressNode.has("village") ? addressNode.get("village").asText() : null; String country = addressNode.has("country") ? addressNode.get("country").asText() : null; if (city != null && country != null) { return Optional.of(new LocationDetails(city, country)); } } catch (Exception e) { e.printStackTrace(); } return Optional.empty(); } // 结果封装类 public static class LocationDetails { private final String city; private final String country; public LocationDetails(String city, String country) { this.city = city; this.country = country; } // Getters public String getCity() { return city; } public String getCountry() { return country; } } }
- 在Telegram Bot逻辑中注入服务并调用:
@Autowired private ReverseGeocodingService geocodingService; // 处理用户发送的位置信息 public void handleUserLocation(double lat, double lon) { geocodingService.getCityAndCountry(lat, lon) .ifPresent(location -> { String replyMsg = String.format("当前所在城市:%s,国家:%s", location.getCity(), location.getCountry()); // 发送消息给用户的逻辑 }); }
注意事项
- 控制请求频率,Nominatim默认限制每秒1次请求,超出可能被临时封禁;
- 必须设置
User-Agent头,明确标识你的应用信息,否则请求可能被拒绝。
方案二:离线反向地理编码(无网络场景适用)
如果Bot需要在无网络环境下运行,可考虑搭建本地Nominatim实例或使用带空间索引的本地数据库(如PostGIS)。但这种方式配置维护成本较高,仅推荐对离线访问有硬性要求的场景。
内容的提问来源于stack exchange,提问作者Yuliia Liapkalo
相关产品推荐
相关产品推荐

