Oracle Spatial中如何将有序单行线合并为单条连续线?
Oracle Spatial 有序线要素合并为单条连续线的解决方案
问题背景
现有一系列有序的MDSYS.SDO_GEOMETRY类型(类型码4402)线要素,需合并为一条同类型的单条连续线(顶点合并为单一SDO_ORDINATE_ARRAY)。但使用SDO_UTIL.APPEND、SDO_AGGR_LRS_CONCAT会生成多段线(类型码4406),SDO_UTIL.CONCAT_LINES生成单线但顶点存在错误。
可行实现方法
方法一:通过顶点提取+重组实现(纯SQL)
核心思路是提取所有线的顶点,按顺序去重相邻重复顶点,再重新构建单线要素:
WITH line_vertices AS ( -- 按线的顺序和顶点序号提取所有坐标点 SELECT line_order, -- 替换为你标识线顺序的字段 vertex_id, x, y FROM your_line_table t, TABLE(SDO_UTIL.GETVERTICES(t.geom)) v ORDER BY line_order, vertex_id ), unique_vertices AS ( -- 去重相邻重复的顶点(避免相邻线首尾重复导致冗余点) SELECT x, y FROM ( SELECT x, y, LAG(x) OVER (ORDER BY line_order, vertex_id) prev_x, LAG(y) OVER (ORDER BY line_order, vertex_id) prev_y FROM line_vertices ) WHERE prev_x IS NULL OR (x != prev_x OR y != prev_y) ), ordinate_list AS ( -- 将坐标聚合为SDO_ORDINATE_ARRAY SELECT SDO_ORDINATE_ARRAY(COLLECT(SDO_NUMBER_ARRAY(x, y))) coords FROM unique_vertices ) -- 构建最终的单条线要素 SELECT MDSYS.SDO_GEOMETRY( 4402, -- 目标类型码 (SELECT srid FROM your_line_table WHERE line_order = 1), -- 取第一条线的空间参考ID NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1, 2, 1), -- 单线元素信息:起始位置1,类型2(线),段数1 (SELECT coords FROM ordinate_list) ) AS merged_single_line FROM dual;
注:如果是3D线,需添加z坐标的处理逻辑,同时调整
SDO_ELEM_INFO_ARRAY和坐标聚合逻辑。
方法二:自定义PL/SQL函数
如果SQL方法灵活性不足,可编写自定义函数遍历每条线的顶点,拼接并去重:
CREATE OR REPLACE FUNCTION merge_to_single_line( p_ordered_line_ids IN SDO_NUMBER_ARRAY -- 按顺序传入线的ID列表 ) RETURN MDSYS.SDO_GEOMETRY IS v_merged_ords MDSYS.SDO_ORDINATE_ARRAY; v_current_geom MDSYS.SDO_GEOMETRY; v_current_ords MDSYS.SDO_ORDINATE_ARRAY; v_last_x NUMBER; v_last_y NUMBER; v_srid NUMBER; BEGIN v_merged_ords := MDSYS.SDO_ORDINATE_ARRAY(); -- 获取空间参考ID(取第一条线的SRID) SELECT srid INTO v_srid FROM your_line_table WHERE line_id = p_ordered_line_ids(1); FOR i IN 1..p_ordered_line_ids.COUNT LOOP -- 获取当前线要素 SELECT geom INTO v_current_geom FROM your_line_table WHERE line_id = p_ordered_line_ids(i); v_current_ords := v_current_geom.SDO_ORDINATES; IF v_merged_ords.COUNT = 0 THEN -- 第一条线:直接添加所有顶点 FOR j IN 1..v_current_ords.COUNT LOOP v_merged_ords.EXTEND; v_merged_ords(v_merged_ords.COUNT) := v_current_ords(j); END LOOP; -- 记录最后一个顶点坐标 v_last_x := v_current_ords(v_current_ords.COUNT - 1); v_last_y := v_current_ords(v_current_ords.COUNT); ELSE -- 后续线:跳过与前一条线终点重复的起始顶点 FOR j IN 1..v_current_ords.COUNT LOOP IF j = 1 THEN IF v_current_ords(j) = v_last_x AND v_current_ords(j+1) = v_last_y THEN CONTINUE; -- 跳过重复起始点 END IF; END IF; v_merged_ords.EXTEND; v_merged_ords(v_merged_ords.COUNT) := v_current_ords(j); END LOOP; -- 更新最后一个顶点坐标 v_last_x := v_current_ords(v_current_ords.COUNT - 1); v_last_y := v_current_ords(v_current_ords.COUNT); END IF; END LOOP; -- 返回合并后的单线要素 RETURN MDSYS.SDO_GEOMETRY( 4402, v_srid, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1, 2, 1), v_merged_ords ); EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20001, "指定的线ID不存在"); WHEN OTHERS THEN RAISE; END; /
调用示例:
SELECT merge_to_single_line(SDO_NUMBER_ARRAY(101,102,103)) AS merged_line FROM dual;
注:
SDO_NUMBER_ARRAY(101,102,103)需替换为你实际的有序线ID列表。
关键注意事项
- 必须确保输入的线要素严格连续且顺序正确,否则合并后的线会出现断裂或逻辑错误
- 合并后可用
SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT函数验证要素有效性 - 处理3D线时,需在代码中增加z坐标的判断与拼接逻辑
内容的提问来源于stack exchange,提问作者gurunara5
相关产品推荐
相关产品推荐

