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);

那么就可以想到:

​ 用线外点到垂线点做垂线,把垂线当做分割线去做分割。

找到这两种思路,就可以直接丢给前端了哈哈😄~

所以也没有再去研究实现,不过最后前端兄弟也是成功的用第一种思路实现了🎉🎉

posted @ 2024-08-07 10:59  Wenenenenen  阅读(3)  评论(0编辑  收藏  举报