routerServer/lib/routePath.js

337 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}