routerServer/lib/routePath.js

337 lines
14 KiB
JavaScript
Raw Normal View History

2024-04-09 10:36:48 +08:00
const axios = require('axios');
const { gcj02towgs84, wgs84togcj02 } = require('coordtransform')
const turf = require('@turf/turf');
const { Url } = require('./urlFormat');
const { parse } = require('./handleGeojson');
const { deepClone } = require('./tools');
const urls = new Url()
const GD_URL = "https://restapi.amap.com/v5/direction/driving"
const GD_KEY = "6af6a87038f44c8c793aa70331f2b7ca"
const POSTGIS_SERVER = {
'pingyixian': 'http://123.132.248.154:9231/api/FirePrevention/LoadRoad',
'feixian': '',
'yishuixian': '',
}
//路线的图层
let pathGraphicLayers = null
//导航寻路
const getRouterFunc = (params, method = 'all') => {
/**
* method
* 当为 postgis时仅仅使用postgis导航
* 默认all :高德 + postgis
* gaode:高德
*/
let { startlng, startlat, endlng, endlat,areaname } = params
if (method == 'postgis') {
//使用gpostgis进行导航
return new Promise((resolve, reject) => {
let postgisParams = {
startlng: startlng,
startlat: startlat,
endlng: endlng,
endlat: endlat,
areaname: areaname,
}
getRouterByPostGis(postgisParams).then(geojson => {
//postGisCoordinates:postgis返回的geojson取出坐标数组
let postGisCoordinates = getOneLineCoordinatesFromGeometry(geojson)
let startRouterLngLat = postGisCoordinates[0]
let endRouterLngLat = postGisCoordinates.at(-1)
let resObject = {
allCoordinates: postGisCoordinates, //全部线路的合集
postGisRoute: postGisCoordinates, // postgis线路
gdRoute: [],
startLngLat: [startlng, startlat], // 起点
endLngLat: [endlng, endlat], //终点
startRouterLngLat: startRouterLngLat, // 路线查询结果的起点
endRouterLngLat: endRouterLngLat, //路线查询结果的终点
}
let simpleRoute = getMinimumRoute(resObject)
resolve(simpleRoute)
})
}).catch(err => {
})
}
if (method == 'all') {
// 先用高德进行导航
return new Promise((resolve, reject) => {
getRouterByGD(params).then(solution => {
// solution 为多条线路的数组,现在先用第一条线路 solution.path[0]
let gdRoute = solution.path[0]
let postgisParams = {
startlng: gdRoute.endCoordinates[0],
startlat: gdRoute.endCoordinates[1],
endlng: endlng,
endlat: endlat,
areaname: areaname,
}
//使用gpostgis求出剩下的路线
getRouterByPostGis(postgisParams).then(geojson => {
//postGisCoordinates:postgis返回的geojson取出坐标数组
let postGisCoordinates = getOneLineCoordinatesFromGeometry(geojson)
// 高德返回的第一条线路的坐标数组
let path_gd = gdRoute.path_polyline
//合并高德和postgis的路线
let allCoordinates = path_gd.concat(postGisCoordinates)
//导航线路的起点和终点
let startRouterLngLat = allCoordinates[0]
let endRouterLngLat = allCoordinates.at(-1)
let resObject = {
allCoordinates: allCoordinates, //全部线路
gdRoute: path_gd, //高德的线路
postGisRoute: postGisCoordinates, // postgis的线路
startLngLat: [startlng, startlat], // 起点
endLngLat: [endlng, endlat], //终点
startRouterLngLat: startRouterLngLat, // 路线查询结果的起点
endRouterLngLat: endRouterLngLat, //路线查询结果的终点
}
let simpleRoute = getMinimumRoute(resObject)
resolve(simpleRoute)
})
})
})
}
}
//高德路线导航
const getRouterByGD = (params) => {
/**
* type:Object
*/
let { startlng, startlat, endlng, endlat } = params
//wgs84转火星坐标系
var gcj02StartLngLat = wgs84togcj02(startlng, startlat);
var gcj02EndLngLat = wgs84togcj02(endlng, endlat);
let gd_params = {
origin: `${gcj02StartLngLat[0]},${gcj02StartLngLat[1]}`,
destination: `${gcj02EndLngLat[0]},${gcj02EndLngLat[1]}`,
show_fields: 'polyline',
key: GD_KEY,
strategy:2
}
let new_url = urls.getUrl(GD_URL, gd_params)
return new Promise((resolve, reject) => {
axios({
method: "get",
url: new_url,
}).then((res) => {
if (res.status === 200) {
let solution = []
//处理数据
res.data.route.paths.map(path => {
let route_len = path.distance
let path_polyline = path.steps.map(step => {
return step.polyline
})
let router_path_str = [] //暂时存放 ['117.927498,35.263264']
path_polyline.forEach(polyline => {
let step = polyline.split(';')
router_path_str = router_path_str.concat(step)
});
// 去掉重复点
let unique_router_path_str = [...new Set(router_path_str)]
// 坐标转数组
let unique_router_path = unique_router_path_str.map(path_str => {
let lng_lat_list = path_str.split(',')
//高德坐标系转wgs84坐标系
var wgs84Coordinate = gcj02towgs84(...lng_lat_list);
return wgs84Coordinate
})
//高德导航的起点和终点
let startCoordinates = unique_router_path[0]
let endCoordinates = unique_router_path.at(-1)
// 高德地图返回的结果:方案一。长度,线
solution.push(
{
route_len: route_len,
path_polyline: unique_router_path,
startCoordinates: startCoordinates,
endCoordinates: endCoordinates
}
)
})
let result = {
routerCount: parseInt(res.data.count),
path: solution
}
resolve(result)
} else {
reject(res)
}
}).catch(err => {
reject(err)
})
})
}
//使用postgres + postgis寻路
const getRouterByPostGis = (params) => {
return new Promise((resolve, reject) => {
axios({
method: "get",
url: POSTGIS_SERVER[params.areaname],
params
}).then((r) => {
let res = r.data
if (res.data.length > 0) {
let LineString = res.data[0].route;
if (LineString == null || LineString == "null") {
//没有找到路线,返回空
console.log('PostGIS未找到合适了路线')
resolve(parse(null))
} else {
resolve(parse(LineString))
}
} else {
console.log('PostGIS未找到合适了路线')
resolve(parse(null))
}
}).catch(err => {
console.log('PostGIS寻路算法服务端错误')
resolve(parse(null))
})
})
}
//计算最近路线
const getMinimumRoute = (pathObject) => {
//备份Object
let pathObjectClone = deepClone(pathObject)
let { allCoordinates, startLngLat, endLngLat, startRouterLngLat, endRouterLngLat, gdRoute, postGisRoute } = pathObjectClone
// 当只有一个点时终点说明高德地图和postgis都未查询到线路直接返回两点
if (allCoordinates.length <= 1) {
pathObjectClone.allCoordinates = [startLngLat, endLngLat]
return pathObjectClone
}
//当postgis寻路时计算两条线路的重叠之处
if (postGisRoute.length && gdRoute.length) {
//实例化turf标准格式
let gdRouteLine = turf.lineString(gdRoute);
let postGisRouteLine = turf.lineString(postGisRoute);
//获取postgis和高德寻路的所有交点
let intersectsGeojson = turf.lineIntersect(gdRouteLine, postGisRouteLine);
let intersectsCoordinates = getMultPointCoordinatesFromGeoJson(intersectsGeojson)
//如果相交点大于1说明路线有重复部分
if (intersectsCoordinates.length > 1) {
let lastIntersectsCoordinates = intersectsCoordinates[0]
let [slicedGdCoordinates, slicedPostGisCoordinates] = sliceByPoint(startRouterLngLat, gdRouteLine, endRouterLngLat, postGisRoute, lastIntersectsCoordinates)
allCoordinates = slicedGdCoordinates.concat(slicedPostGisCoordinates)
//处理后的结果赋值给pathObjectClone
pathObjectClone.gdRoute = slicedGdCoordinates
pathObjectClone.postGisRoute = slicedPostGisCoordinates
gdRouteLine = turf.lineString(slicedGdCoordinates);
postGisRouteLine = turf.lineString(slicedPostGisCoordinates);
}
// 阈值计算重复路线,去除重复线路
let overlapping = turf.lineOverlap(gdRouteLine, postGisRouteLine, { tolerance: 0.1 });
if (overlapping.features.length) {
let lastOverlapPoint = overlapping.features.at(-1).geometry.coordinates[0]
let [overlapGdCoordinates, overlapPostGisCoordinates] = sliceByPoint(startRouterLngLat, gdRouteLine, endRouterLngLat, postGisRoute, lastOverlapPoint)
allCoordinates = overlapGdCoordinates.concat(overlapPostGisCoordinates)
// 连接路段平滑过渡
}
}
// 转成turf标准线格式
let allRouteLine = turf.lineString(allCoordinates);
// 转成turf标准点格式
let startLngLatPoint = turf.point(startLngLat);
let startRouterLngLatPoint = turf.point(startRouterLngLat);
let endLngLatPoint = turf.point(endLngLat);
let endRouterLngLatPoint = turf.point(endRouterLngLat);
//获取终点到导航线最近的点
let snappedGeojson = turf.nearestPointOnLine(allRouteLine, endLngLatPoint, { units: 'miles' });
let snappedCoordinates = getOnePointCoordinatesFromGeoJson(snappedGeojson)
// 根据最近的点截取路线,取前半部分
let slicedGeojson = turf.lineSlice(startRouterLngLat, turf.point(snappedCoordinates), allRouteLine);
let slicedCoordinates = getOnePointCoordinatesFromGeoJson(slicedGeojson)
//把截取后的路线赋值给pathObjectClone
pathObjectClone.allCoordinates = slicedCoordinates
//计算出发地到目的地的图上距离(直线)
let distanceStartToEnd = turf.distance(startLngLatPoint, endLngLatPoint)
//计算出发点到出发导航路线出发点的步行距离
let distanceStartToStartRoute = turf.distance(startLngLatPoint, startRouterLngLatPoint)
//计算终点到出发导航路线终点的步行距离
let distanceEndToEndRoute = turf.distance(endLngLatPoint, endRouterLngLatPoint)
//如果出发点与目的地的实际距离小于步行的距离,直接使用出发点到目的地的距离,导航此时不适用
if (distanceStartToEnd < (distanceStartToStartRoute + distanceEndToEndRoute)) {
pathObjectClone.allCoordinates = [startLngLat, endLngLat]
}
//把终点到导航终点改为距离线路的最近的的点
pathObjectClone.endRouterLngLat = snappedCoordinates
return pathObjectClone
}
const sliceByPoint = (line1Start, line1, line2End, line2, point) => {
/**
* 根据点point把line1的前半部分和line2的后半部分进行拼接
* line1Start:line1的起始点 [lng.lat]
* line2End:line2 的终止点 [lng,lat]
*/
//拷贝line2的坐标数组进行倒序排列
let line2Copy = [...line2]
line2Copy.reverse()
let line2CopyReverseLineString = turf.lineString(line2Copy);
// 根据point截取路线line1取前半部分
// 根据point截取路线line2取后半部分
//然后将两部分拼接让line1路线从第一个交点处转向line2路段
let slicedLine1Geojson = turf.lineSlice(line1Start, turf.point(point), line1);
let slicedLine1Coordinates = getOnePointCoordinatesFromGeoJson(slicedLine1Geojson)
let slicedLine2Geojson = turf.lineSlice(line2End, turf.point(point), line2CopyReverseLineString);
let slicedLine2Coordinates = getOnePointCoordinatesFromGeoJson(slicedLine2Geojson)
slicedLine2Coordinates.reverse()
return [slicedLine1Coordinates, slicedLine2Coordinates]
}
// 坐标转geoJson
const comLineStringGeoJson = (coordinates) => {
return {
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": coordinates,
"type": "LineString"
}
}
}
// 从一条线的geometry中获取坐标
const getOneLineCoordinatesFromGeometry = (geometry) => {
let coordinates = geometry.coordinates
let list = []
if (geometry.type == "MultiLineString") {
coordinates.map(coord => {
list = list.concat(coord)
})
} else if (geometry.type == 'LineString') {
list = list.concat(geometry.coordinates)
} else {
list = []
}
return list
}
//从一个点的geojson中返回坐标点
const getOnePointCoordinatesFromGeoJson = (geojson) => {
return geojson.geometry.coordinates
}
//从多个点的geojson中返回坐标点
const getMultPointCoordinatesFromGeoJson = (geojson) => {
return geojson.features.map(feature => {
return feature.geometry.coordinates
})
}
module.exports = {
getRouterFunc
}