1,登录百度地图开放平台,获取ak

2,在配置文件application.yml里面配置
baidu-map:
ak: pwBiRaplyK5VkkT******172Od4
nearby-radius: 100000
nearby-limit: 0
default-region: 重庆市永川区
3,编写配置类BaiduMapProperties,接受配置文件的配置
package com.smart.common.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "smart.baidu-map")
public class BaiduMapProperties {
private String ak;
private Integer nearbyRadius = 100000;
private Integer nearbyLimit = 0;
private String defaultRegion = "重庆市永川区";
}
4,编写写百度地图工具类,BaiduMapUtil.java
package com.smart.common.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.smart.common.exception.BaseException;
import com.smart.common.properties.BaiduMapProperties;
import com.smart.pojo.vo.MapPlaceSearchVO;
import com.smart.pojo.vo.MapPointVO;
import com.smart.pojo.vo.MapRouteVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Slf4j
@Component
public class BaiduMapUtil {
//百度地点检索接口
private static final String PLACE_SEARCH_URL = "https://api.map.baidu.com/place/v2/search";
//百度坐标转换接口
private static final String GEO_CONVERT_URL = "https://api.map.baidu.com/geoconv/v1/";
//百度驾车路线规划接口
private static final String DRIVING_ROUTE_URL = "https://api.map.baidu.com/directionlite/v1/driving";
@Autowired
private BaiduMapProperties baiduMapProperties;
/**
* 批量坐标转换
*/
public List<MapPointVO> convertPoints(List<MapPointVO> points, String fromCoordType, String toCoordType) {
List<MapPointVO> safePoints = points == null ? new ArrayList<MapPointVO>() : points;
if (safePoints.isEmpty()) {
return new ArrayList<>();
}
String normalizedFrom = normalizeCoordType(fromCoordType);
String normalizedTo = normalizeCoordType(toCoordType);
if (normalizedFrom.equals(normalizedTo)) {
return copyPoints(safePoints);
}
StringBuilder coordsBuilder = new StringBuilder();
for (MapPointVO point : safePoints) {
if (point == null || point.getLatitude() == null || point.getLongitude() == null) {
continue;
}
if (coordsBuilder.length() > 0) {
coordsBuilder.append(";");
}
coordsBuilder.append(point.getLongitude()).append(",").append(point.getLatitude());
}
if (coordsBuilder.length() == 0) {
return new ArrayList<>();
}
Map<String, String> params = new HashMap<>();
params.put("coords", coordsBuilder.toString());
params.put("from", resolveCoordTypeCode(normalizedFrom));
params.put("to", resolveCoordTypeCode(normalizedTo));
params.put("ak", getRequiredAk());
JSONObject response = doGetJson(GEO_CONVERT_URL, params, "坐标转换");
JSONArray results = response.getJSONArray("result");
if (results == null || results.isEmpty()) {
throw new BaseException("坐标转换失败");
}
List<MapPointVO> convertedPoints = new ArrayList<>();
for (int i = 0; i < results.size(); i++) {
JSONObject item = results.getJSONObject(i);
if (item == null) {
continue;
}
convertedPoints.add(MapPointVO.builder()
.latitude(item.getDouble("y"))
.longitude(item.getDouble("x"))
.build());
}
return convertedPoints;
}
/**
* 单个坐标转换
*/
public MapPointVO convertPoint(Double latitude, Double longitude, String fromCoordType, String toCoordType) {
if (latitude == null || longitude == null) {
throw new BaseException("坐标不能为空");
}
List<MapPointVO> convertedPoints = convertPoints(buildSinglePointList(latitude, longitude), fromCoordType, toCoordType);
if (convertedPoints.isEmpty()) {
throw new BaseException("坐标转换失败");
}
return convertedPoints.get(0);
}
/**
* 地点检索
*/
public List<MapPlaceSearchVO> searchPlace(String keyword, String region) {
Map<String, String> params = new HashMap<>();
params.put("query", keyword);
params.put("region", region);
params.put("page_size", "10");
params.put("scope", "2");
params.put("output", "json");
params.put("ak", getRequiredAk());
JSONObject response = doGetJson(PLACE_SEARCH_URL, params, "地点检索");
JSONArray results = response.getJSONArray("results");
List<MapPlaceSearchVO> placeList = new ArrayList<>();
if (results == null) {
return placeList;
}
for (int i = 0; i < results.size(); i++) {
JSONObject item = results.getJSONObject(i);
if (item == null || item.getJSONObject("location") == null) {
continue;
}
JSONObject location = item.getJSONObject("location");
placeList.add(MapPlaceSearchVO.builder()
.name(item.getString("name"))
.address(item.getString("address"))
.province(item.getString("province"))
.city(item.getString("city"))
.area(item.getString("area"))
.latitude(location.getDouble("lat"))
.longitude(location.getDouble("lng"))
.build());
}
return placeList;
}
/*
* 任意坐标转成百度坐标系 bd09
*/
public MapPointVO convertToBd09(Double latitude, Double longitude, String coordType) {
return convertPoint(latitude, longitude, coordType, "bd09");
}
/**
* 路线规划
*/
public MapRouteVO planDrivingRoute(Double fromLatitude, Double fromLongitude, Double toLatitude, Double toLongitude) {
Map<String, String> params = new HashMap<>();
params.put("origin", fromLatitude + "," + fromLongitude);
params.put("destination", toLatitude + "," + toLongitude);
params.put("output", "json");
params.put("ak", getRequiredAk());
JSONObject response = doGetJson(DRIVING_ROUTE_URL, params, "路线规划");
JSONObject result = response.getJSONObject("result");
if (result == null || result.getJSONArray("routes") == null || result.getJSONArray("routes").isEmpty()) {
throw new BaseException("未查询到可用路线");
}
JSONObject route = result.getJSONArray("routes").getJSONObject(0);
List<MapPointVO> polylinePoints = parsePolylinePoints(route.getJSONArray("steps"));
if (polylinePoints.isEmpty()) {
polylinePoints.add(MapPointVO.builder().latitude(fromLatitude).longitude(fromLongitude).build());
polylinePoints.add(MapPointVO.builder().latitude(toLatitude).longitude(toLongitude).build());
}
Integer distanceMeters = route.getInteger("distance");
Integer durationSeconds = route.getInteger("duration");
return MapRouteVO.builder()
.distanceMeters(distanceMeters)
.durationSeconds(durationSeconds)
.distanceText(formatDistance(distanceMeters))
.durationText(formatDuration(durationSeconds))
.origin(MapPointVO.builder().latitude(fromLatitude).longitude(fromLongitude).build())
.destination(MapPointVO.builder().latitude(toLatitude).longitude(toLongitude).build())
.polylinePoints(polylinePoints)
.build();
}
/**
* 统一异常处理
*/
private JSONObject doGetJson(String url, Map<String, String> params, String actionName) {
String responseText = HttpClientUtil.doGet(url, params);
if (responseText == null || responseText.trim().isEmpty()) {
throw new BaseException(actionName + "失败,未获取到响应");
}
JSONObject response = JSONObject.parseObject(responseText);
Integer status = response.getInteger("status");
if (status != null && status != 0) {
String message = response.getString("message");
throw new BaseException(actionName + "失败" + (message == null || message.trim().isEmpty() ? "" : ":" + message));
}
return response;
}
/**
* 解析路线规划的轨迹点
*
* */
private List<MapPointVO> parsePolylinePoints(JSONArray steps) {
List<MapPointVO> points = new ArrayList<>();
if (steps == null || steps.isEmpty()) {
return points;
}
Set<String> uniquePoints = new LinkedHashSet<>();
for (int i = 0; i < steps.size(); i++) {
JSONObject step = steps.getJSONObject(i);
if (step == null) {
continue;
}
String path = step.getString("path");
if (path == null || path.trim().isEmpty()) {
continue;
}
String[] pathSegments = path.split(";");
for (String segment : pathSegments) {
if (segment == null || segment.trim().isEmpty()) {
continue;
}
uniquePoints.add(segment);
}
}
for (String point : uniquePoints) {
String[] coordinates = point.split(",");
if (coordinates.length != 2) {
continue;
}
try {
points.add(MapPointVO.builder()
.latitude(Double.parseDouble(coordinates[1]))
.longitude(Double.parseDouble(coordinates[0]))
.build());
} catch (NumberFormatException e) {
log.warn("解析百度路线点失败: {}", point, e);
}
}
return points;
}
/**
* 格式化距离
*/
private String formatDistance(Integer distanceMeters) {
if (distanceMeters == null) {
return "0m";
}
if (distanceMeters >= 1000) {
return String.format("%.1fkm", distanceMeters / 1000.0D);
}
return distanceMeters + "m";
}
/**
* 格式化时长
*/
private String formatDuration(Integer durationSeconds) {
if (durationSeconds == null) {
return "0分钟";
}
if (durationSeconds >= 3600) {
int hours = durationSeconds / 3600;
int minutes = (durationSeconds % 3600) / 60;
return minutes > 0 ? hours + "小时" + minutes + "分钟" : hours + "小时";
}
if (durationSeconds >= 60) {
return (durationSeconds / 60) + "分钟";
}
return durationSeconds + "秒";
}
/**
* 获取必填的AK
*/
private String getRequiredAk() {
String ak = baiduMapProperties.getAk();
if (ak == null || ak.trim().isEmpty()) {
throw new BaseException("百度地图AK未配置");
}
return ak.trim();
}
private List<MapPointVO> buildSinglePointList(Double latitude, Double longitude) {
List<MapPointVO> points = new ArrayList<>();
points.add(MapPointVO.builder().latitude(latitude).longitude(longitude).build());
return points;
}
private List<MapPointVO> copyPoints(List<MapPointVO> source) {
List<MapPointVO> copiedPoints = new ArrayList<>();
for (MapPointVO point : source) {
if (point == null) {
continue;
}
copiedPoints.add(MapPointVO.builder()
.latitude(point.getLatitude())
.longitude(point.getLongitude())
.build());
}
return copiedPoints;
}
private String normalizeCoordType(String coordType) {
if (coordType == null || coordType.trim().isEmpty()) {
return "bd09";
}
String normalized = coordType.trim().toLowerCase();
if ("bd09ll".equals(normalized)) {
return "bd09";
}
return normalized;
}
private String resolveCoordTypeCode(String coordType) {
if ("wgs84".equals(coordType)) {
return "1";
}
if ("gcj02".equals(coordType)) {
return "3";
}
if ("bd09".equals(coordType)) {
return "5";
}
throw new BaseException("暂不支持的坐标类型: " + coordType);
}
}
5,编写业务类 MapServiceImpl
package com.smart.service.impl;
import com.smart.common.exception.BaseException;
import com.smart.common.properties.BaiduMapProperties;
import com.smart.common.utils.BaiduMapUtil;
import com.smart.mapper.ParkingLotMapper;
import com.smart.pojo.dto.ParkingLotQueryDTO;
import com.smart.pojo.vo.MapPlaceSearchVO;
import com.smart.pojo.vo.MapPointVO;
import com.smart.pojo.vo.MapRouteVO;
import com.smart.pojo.vo.ParkingLotVO;
import com.smart.service.MapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Service
public class MapServiceImpl implements MapService {
private static final int DEFAULT_NEARBY_RADIUS_METERS = 100_000;
@Autowired
private ParkingLotMapper parkingLotMapper;
@Autowired
private BaiduMapUtil baiduMapUtil;
@Autowired
private BaiduMapProperties baiduMapProperties;
@Override
public List<MapPlaceSearchVO> searchPlaces(String keyword, String region, String coordType) {
String normalizedKeyword = keyword == null ? null : keyword.trim();
if (normalizedKeyword == null || normalizedKeyword.isEmpty()) {
throw new BaseException("地点关键词不能为空");
}
String normalizedRegion = region == null || region.trim().isEmpty()
? baiduMapProperties.getDefaultRegion()
: region.trim();
List<MapPlaceSearchVO> places = baiduMapUtil.searchPlace(normalizedKeyword, normalizedRegion);
String resultCoordType = normalizeCoordType(coordType);
if ("bd09".equals(resultCoordType)) {
return places;
}
for (MapPlaceSearchVO place : places) {
if (place.getLatitude() == null || place.getLongitude() == null) {
continue;
}
MapPointVO convertedPoint = baiduMapUtil.convertPoint(place.getLatitude(), place.getLongitude(), "bd09", resultCoordType);
place.setLatitude(convertedPoint.getLatitude());
place.setLongitude(convertedPoint.getLongitude());
}
return places;
}
@Override
public List<ParkingLotVO> listNearbyParkingLots(Double latitude, Double longitude, Integer radius, String coordType) {
if (latitude == null || longitude == null) {
throw new BaseException("查询附近停车场时坐标不能为空");
}
String resultCoordType = normalizeCoordType(coordType);
MapPointVO centerPoint = baiduMapUtil.convertPoint(latitude, longitude, resultCoordType, "bd09");
List<ParkingLotVO> allParkingLots = loadPublishedParkingLots();
int searchRadius = normalizeRadius(radius);
List<ParkingLotVO> nearbyParkingLots = new ArrayList<>();
for (ParkingLotVO parkingLot : allParkingLots) {
if (parkingLot.getLatitude() == null || parkingLot.getLongitude() == null) {
continue;
}
Double distanceKm = calculateDistance(centerPoint.getLatitude(), centerPoint.getLongitude(),
parkingLot.getLatitude().doubleValue(), parkingLot.getLongitude().doubleValue());
if (distanceKm == null || distanceKm * 1000 > searchRadius) {
continue;
}
if (!"bd09".equals(resultCoordType)) {
MapPointVO convertedPoint = baiduMapUtil.convertPoint(parkingLot.getLatitude().doubleValue(),
parkingLot.getLongitude().doubleValue(), "bd09", resultCoordType);
parkingLot.setLatitude(convertedPoint.getLatitude() == null ? null : java.math.BigDecimal.valueOf(convertedPoint.getLatitude()));
parkingLot.setLongitude(convertedPoint.getLongitude() == null ? null : java.math.BigDecimal.valueOf(convertedPoint.getLongitude()));
}
parkingLot.setDistanceKm(distanceKm);
parkingLot.setRecommended(0);
nearbyParkingLots.add(parkingLot);
}
nearbyParkingLots.sort(Comparator
.comparing((ParkingLotVO item) -> item.getDistanceKm() == null ? Double.MAX_VALUE : item.getDistanceKm())
.thenComparing(item -> item.getFreeSpaceCount() == null ? 0 : -item.getFreeSpaceCount())
.thenComparing(ParkingLotVO::getId));
int limit = baiduMapProperties.getNearbyLimit() == null || baiduMapProperties.getNearbyLimit() <= 0
? nearbyParkingLots.size()
: Math.min(nearbyParkingLots.size(), baiduMapProperties.getNearbyLimit());
List<ParkingLotVO> result = new ArrayList<>(nearbyParkingLots.subList(0, limit));
if (!result.isEmpty()) {
result.get(0).setRecommended(1);
}
return result;
}
@Override
public MapRouteVO planRoute(Double fromLatitude, Double fromLongitude, Double toLatitude, Double toLongitude,
String fromCoordType, String toCoordType) {
if (fromLatitude == null || fromLongitude == null || toLatitude == null || toLongitude == null) {
throw new BaseException("路线规划坐标不能为空");
}
String resultCoordType = normalizeCoordType(fromCoordType);
MapPointVO originPoint = baiduMapUtil.convertPoint(fromLatitude, fromLongitude, resultCoordType, "bd09");
MapPointVO destinationPoint = baiduMapUtil.convertPoint(toLatitude, toLongitude,
normalizeCoordType(toCoordType), "bd09");
MapRouteVO route = baiduMapUtil.planDrivingRoute(originPoint.getLatitude(), originPoint.getLongitude(),
destinationPoint.getLatitude(), destinationPoint.getLongitude());
if (!"bd09".equals(resultCoordType)) {
route.setOrigin(baiduMapUtil.convertPoint(route.getOrigin().getLatitude(), route.getOrigin().getLongitude(), "bd09", resultCoordType));
route.setDestination(baiduMapUtil.convertPoint(route.getDestination().getLatitude(), route.getDestination().getLongitude(), "bd09", resultCoordType));
route.setPolylinePoints(baiduMapUtil.convertPoints(route.getPolylinePoints(), "bd09", resultCoordType));
}
return route;
}
private List<ParkingLotVO> loadPublishedParkingLots() {
ParkingLotQueryDTO queryDTO = new ParkingLotQueryDTO();
queryDTO.setPublishStatus(1);
return parkingLotMapper.listForUser(queryDTO);
}
private int normalizeRadius(Integer radius) {
if (radius == null || radius <= 0) {
return baiduMapProperties.getNearbyRadius() == null ? DEFAULT_NEARBY_RADIUS_METERS : baiduMapProperties.getNearbyRadius();
}
return radius;
}
private String normalizeCoordType(String coordType) {
if (coordType == null || coordType.trim().isEmpty()) {
return "bd09";
}
String normalized = coordType.trim().toLowerCase();
return "bd09ll".equals(normalized) ? "bd09" : normalized;
}
private Double calculateDistance(Double fromLatitude, Double fromLongitude, Double toLatitude, Double toLongitude) {
if (fromLatitude == null || fromLongitude == null || toLatitude == null || toLongitude == null) {
return null;
}
double earthRadiusKm = 6371.0D;
double latitudeDiff = Math.toRadians(toLatitude - fromLatitude);
double longitudeDiff = Math.toRadians(toLongitude - fromLongitude);
double a = Math.sin(latitudeDiff / 2) * Math.sin(latitudeDiff / 2)
+ Math.cos(Math.toRadians(fromLatitude)) * Math.cos(Math.toRadians(toLatitude))
* Math.sin(longitudeDiff / 2) * Math.sin(longitudeDiff / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return Math.round(earthRadiusKm * c * 100.0D) / 100.0D;
}
}
6,暴露MapController 接口给前端
package com.smart.controller.user;
import com.smart.common.result.Result;
import com.smart.pojo.vo.MapPlaceSearchVO;
import com.smart.pojo.vo.MapRouteVO;
import com.smart.pojo.vo.ParkingLotVO;
import com.smart.service.MapService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/user/map")
@Api(tags = "用户端-地图相关接口")
public class MapController {
@Autowired
private MapService mapService;
@GetMapping("/place-search")
@ApiOperation("地点检索")
public Result<List<MapPlaceSearchVO>> searchPlace(String keyword, String region, String coordType) {
return Result.success(mapService.searchPlaces(keyword, region, coordType));
}
@GetMapping("/nearby")
@ApiOperation("查询附近停车场")
public Result<List<ParkingLotVO>> nearby(Double latitude, Double longitude, Integer radius, String coordType) {
return Result.success(mapService.listNearbyParkingLots(latitude, longitude, radius, coordType));
}
@GetMapping("/route")
@ApiOperation("路线规划")
public Result<MapRouteVO> route(Double fromLatitude, Double fromLongitude, Double toLatitude, Double toLongitude,
String fromCoordType, String toCoordType) {
return Result.success(mapService.planRoute(fromLatitude, fromLongitude, toLatitude, toLongitude,
fromCoordType, toCoordType));
}
}