POSTGIS中把一条线根据线外指定点打断
POSTGIS中把一条线根据线外指定点打断
思路:先做线外这个点到这条线的垂线,然后垂线和这条线相交的点,作为切点,把线切成两条。
研究发现做垂线行不通,换个说法就是找到这个点到这条线上距离最近的点,意思就是做垂线得出的点
1、先找出垂线点
第一种方法:##操作符
使用 ## 连接符 计算第二个操作数上最接近第一个操作数的点
即:
select point '(0,0)' ## line '((2,0),(0,2))'
附:PG几何函数和操作符:http://www.postgres.cn/docs/11/functions-geometry.html
select point '(-122.4855584735381, 37.83042426560317)' ## line '((-122.48695850372314, 37.82931081282506),(-122.48700141906738, 37.83080223556934))' as interrupt
---------------------------------------------------------------------------------------
(-122.486989358315,37.8303830922249)
第二种方法:空间函数
出现问题:line和lseg只能放两个点,两个点以上就是path,但是path不支持##操作符
更换postgis函数:
ST_LineLocatePoint
配合ST_LineInterpolatePoint
使用附:POSTGIS空间函数:http://www.postgis.net/docs/manual-3.2/reference.html#Geometry_Constructors
用到的函数:
ST_AsEWKT
— 返回具有SRID
元数据的几何图形的众所周知的文本 (WKT
) 表示。
ST_GeomFromText
— 从众所周知的文本表示 (WKT
) 返回指定的 ST_Geometry 值。
ST_LineLocatePoint
— 返回一条线上最近点到一个点的小数位置
ST_LineInterpolatePoint
— 返回在小数位置沿直线插值的点
SELECT
st_asewkt (
ST_LineInterpolatePoint (
st_geomfromtext ( 'LINESTRING(116.399095 40.016244, 116.399166 40.007457, 116.399266 39.997457)' ),
st_linelocatepoint (
st_geomfromtext ( 'LINESTRING(116.399095 40.016244, 116.399166 40.007457, 116.399266 39.997457)' ),
st_geomfromtext ( 'point(116.407862 40.013702)' )
)
)
);
测试垂线点的展示页面
写了个简单的mapbox小demo,方便看一下得出的点的偏差。点击地图出现垂线和垂线点。
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>垂线点测试</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.css' rel='stylesheet' />
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoiemhpd2VuaiIsImEiOiJjand2eGlzb2MwYWg3NDlyMXNmbGJyZGh2In0.VCR4KV-QWW2vBugH2G6cDw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [116.407862, 40.013702],
zoom: 15
});
map.on('click', function (e) {
new mapboxgl.Marker().setLngLat([116.39911611066522, 40.01363133217905]).addTo(map);
map.addLayer({
"id": "route2",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {
"color":"red"
},
"geometry": {
"type": "LineString",
"coordinates": [
[116.39911611066522, 40.01363133217905],
[116.407862, 40.013702]
]
}
}
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
console.log(e)
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {
"color":"red"
},
"geometry": {
"type": "LineString",
"coordinates": [
[116.399095, 40.016244],
[116.399166, 40.007457],
[116.399266, 39.997457]
]
}
}
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
new mapboxgl.Marker().setLngLat([116.407862, 40.013702]).addTo(map);
});
</script>
</body>
</html>
2、 根据垂线点切割线
第一种思路:lineSlice
一顿狂找发现前端空间分析框架
turf.js
有个方法可以间接实现,就是turf.lineSlice()
turf官网:https://turfjs.org/docs/#lineSlice
官方介绍:
采用 line 、 start point和 stop point 并返回这些点之间的线的子部分。起点和终点不需要正好落在直线上
案例:
var line = turf.lineString([
[-77.031669, 38.878605],
[-77.029609, 38.881946],
[-77.020339, 38.884084],
[-77.025661, 38.885821],
[-77.021884, 38.889563],
[-77.019824, 38.892368]
]);
var start = turf.point([-77.029609, 38.881946]);
var stop = turf.point([-77.021884, 38.889563]);
var sliced = turf.lineSlice(start, stop, line);
那么就可以想到:
第一段:线的起点作为start point,垂线点作为end point
第二段:垂线点作为start point, 线的终点作为end point
第二种思路:lineSplit
官方介绍:
用另一个
GeoJSON
特征拆分LineString
。
案例:
var line = turf.lineString([[120, -25], [145, -25]]);
var splitter = turf.lineString([[130, -15], [130, -35]]);
var split = turf.lineSplit(line, splitter);
那么就可以想到:
用线外点到垂线点做垂线,把垂线当做分割线去做分割。
找到这两种思路,就可以直接丢给前端了哈哈😄~
所以也没有再去研究实现,不过最后前端兄弟也是成功的用第一种思路实现了🎉🎉