数据融合工具(10)线重叠检查修复

一、需求背景

        先明确一下“线重叠”的定义。

        ArcGIS拓扑工具集中的拓扑规则:

  • 不能自重叠(线) —线要素不得与自身重叠。这些线要素可以交叉或接触但不得有重合的线段。此规则适用于街道等线段可能接触闭合线的要素,但同一街道不得出现两次相同的路线。

  • 不能重叠(线) —线不得与同一要素类(或子类型)中的线重叠。例如,当河流要素类中线段不能重复时,使用此规则。线可以交叉或相交,但不能共享线段。

图片

 1.1 拓扑错误修复

        对于“不能自重叠”错误,使用“简化”(从要素中移除导致错误的自重叠或自相交的线段),此修复可以应用到一个或多个错误。

        对于“不能重叠”错误,使用“移除重叠”(移除导致错误的要素重叠部分),此修复只能一条记录一条记录修复。

        需求来了,能不能,一个工具,解决所有的“重叠”错误,并记录修复情况呢?

        毕竟,重叠错误可能是这样的?多如烟海……

图片

1.2 剖析一下

        01 说一说?自重叠

        我们知道,线是由一系列点按给定的顺序依次连接构成的。线自重叠,只需要线节点连接出现往复或压盖即可发生,如下图所示。

图片

 

 

        在理想数据下,实现线自重叠的判断,自重叠部分的查找以及自重叠的修复代码如下:

图片

from shapely.geometry import LineString, Point, MultiLineString
from shapely.ops import linemerge
import numpy as np
from itertools import combinations


# 将折线转换为线段
def create_segments(polyline):
    segments = []

    # 如果输入是列表或ndarray
    if isinstance(polyline, (list, np.ndarray)):
        coords = polyline if isinstance(polyline, list) else polyline.tolist()
        for i in range(len(coords) - 1):
            line = LineString([coords[i], coords[i + 1]])
            segments.append(line)

    # 如果输入是LineString
    elif isinstance(polyline, LineString):
        coords = list(polyline.coords)
        for i in range(len(coords) - 1):
            line = LineString([coords[i], coords[i + 1]])
            segments.append(line)

    # 如果输入是MultiLineString
    elif isinstance(polyline, MultiLineString):
        for line_string in polyline.geoms:
            coords = list(line_string.coords)
            for i in range(len(coords) - 1):
                line = LineString([coords[i], coords[i + 1]])
                segments.append(line)

    else:
        raise TypeError("当前数据类型不支持!")

    return segments


def calculate_slope(line):
    x1, y1 = line.coords[0]
    x2, y2 = line.coords[-1]
    if x2 - x1 != 0:
        slope = (y2 - y1) / (x2 - x1)
    else:
        slope = np.inf  # 垂直线的斜率定义为无穷大
    return slope


def judge_overlapping(multi_line):
    slopes = {}
    for idx, line in enumerate(multi_line):
        slope = calculate_slope(line)
        if slope not in slopes:
            slopes[slope] = []
        slopes[slope].append(idx)

    for slope, line_indices in slopes.items():
        if len(line_indices) <= 1:
            continue
        for i, j in combinations(line_indices, 2):
            processed_points = set()  # 用于记录已处理过的点
            line1 = multi_line[i]
            line2 = multi_line[j]
            if line1.distance(line2) != 0:
                continue
            coincident_points_count = 0
            for p1 in line1.coords:
                if p1 in processed_points:  # 如果点已处理过,跳过计数
                    continue
                if line2.distance(Point(p1)) == 0:
                    coincident_points_count += 1
                    processed_points.add(p1)  # 添加已处理的点
                    if coincident_points_count >= 2:
                        return True  # 发现重叠,直接返回True
            else:
                for p2 in line2.coords:
                    if p2 in processed_points:  # 如果点已处理过,跳过计数
                        continue
                    if line1.distance(Point(p2)) == 0:
                        coincident_points_count += 1
                        processed_points.add(p2)  # 添加已处理的点
                        if coincident_points_count >= 2:
                            return True  # 发现重叠,直接返回True
                else:
                    continue
    return False  # 没有发现重叠,返回False


def find_overlapping_sections(multi_line):
    slopes = {}
    for idx, line in enumerate(multi_line):
        slope = calculate_slope(line)
        slope = np.round(slope, decimals=8)
        if slope not in slopes:
            slopes[slope] = []
        slopes[slope].append(idx)

    overlapping_sections = []
    for slope, line_indices in slopes.items():
        if len(line_indices) <= 1:
            continue
        for i, j in combinations(line_indices, 2):
            processed_points = set()  # 用于记录已处理过的点
            line1 = multi_line[i]
            line2 = multi_line[j]
            if not np.isclose(line1.distance(line2), 0):
                continue
            coincident_points_count = 0
            for p1 in line1.coords:
                if p1 in processed_points:  # 如果点已处理过,跳过计数
                    continue
                if np.isclose(line2.distance(Point(p1)), 0):
                    coincident_points_count += 1
                    processed_points.add(p1)  # 添加已处理的点
            for p2 in line2.coords:
                if p2 in processed_points:  # 如果点已处理过,跳过计数
                    continue
                if np.isclose(line1.distance(Point(p2)), 0):
                    coincident_points_count += 1
                    processed_points.add(p2)  # 添加已处理的点
            if coincident_points_count >= 2:
                overlapping_sections.append((i, j))

    return overlapping_sections


def export_overlapping(multi_line, overlapping_sections):

    # 输出重叠部分
    intersecting_sections = []
    for i, j in overlapping_sections:
        over_part = multi_line[i].intersection(multi_line[j], grid_size=0)
        intersecting_sections.append(over_part)
    union_lines = linemerge(intersecting_sections)
    if isinstance(union_lines, MultiLineString):
        # 如果结果是一个 MultiLineString,则将其拆解为单独的几何体
        overlapping = list(union_lines.geoms)
    elif isinstance(union_lines, LineString):
        # 如果结果是一个单独的 LineString,则将其包装成列表
        overlapping = [union_lines]
    else:
        # 如果结果是其他类型,这里可以根据具体情况进行处理
        overlapping = []

    return overlapping


def repair_overlapping(segments, overlapping_sections):
    multi_line = segments[::]

    # 修复多段线
    for i, j in overlapping_sections:
        if not multi_line[i] or not multi_line[j]:
            continue
        if multi_line[i].equals(multi_line[j]):
            multi_line[i] = []

        elif multi_line[i].contains(multi_line[j]):
            multi_line[j] = []

        elif multi_line[j].contains(multi_line[i]):
            multi_line[i] = []

        else:
            multi_line[i] = multi_line[i].difference(multi_line[j])

    repaired_line = linemerge([i for i in multi_line if i])

    if isinstance(repaired_line, MultiLineString):
        # 如果结果是一个 MultiLineString,则将其拆解为单独的几何体
        overlapping = list(repaired_line.geoms)
    elif isinstance(repaired_line, LineString):
        # 如果结果是一个单独的 LineString,则将其包装成列表
        overlapping = [repaired_line]
    else:
        # 如果结果是其他类型,这里可以根据具体情况进行处理
        overlapping = []

    return overlapping


if __name__ == "__main__":

    # 示例:检查多段线是否存在重叠部分
    test_line = LineString([(0, 0), (1, 1), (2, 2), (3, 3), (0.5, 0.5)])
    example_multiline = create_segments(test_line)
    result = find_overlapping_sections(example_multiline)
    print("重叠部分的线段编号:", result)

    export = export_overlapping(example_multiline, result)
    print("重叠部分:", export)

    repaired = repair_overlapping(example_multiline, result)
    print("修复后线段:", repaired)

        线自重叠一般很少发生,但是通过对自重叠的研究分析后,发现一些很有意思的事。如下图:

        按我的估计,80%以上的人会认为162、164和163三个节点在一条直线上,即线162-163的斜率和线162-164的斜率一定相等。为什么使用斜率来检测是否共线呢?

        斜率,这是一个初略过滤两条线段是否重叠的基础,是一种非常高效简洁的检测思路。

        事实上,从坐标值来看,这3个点还真不一定共线(实际上就碰到了)。但在ArcGIS中分析和查看,他们却一定是共线的。这里涉及到一个知识点,XY容差。


X,Y 容差指的是坐标之间的最小距离,小于该距离的坐标将合并到一起。

默认 X,Y 容差设置为 0.001 米,或以数据集的坐标系单位表示的等效值。例如,如果坐标系以英尺为单位,则此默认值是 0.003281 英尺(0.03937 英寸)。默认值是默认 X,Y 分辨率的 10 倍,且在大多数情况下均推荐此设置。如果坐标以经纬度表示,则默认 X,Y 容差为 0.0000000556 度。


        而点位坐标的存储精度,是通过X,Y分辨率来表达的。X,Y 分辨率(表示非常小的距离)是指用于存储 x,y 坐标值的有效数字的位数。

图片

        接着说……

        接着之前的疑问,当162、164和163三个点坐标值不共线,但在ArcGIS拓扑分析时,为什么这条折线会出现自重叠呢?

        我们知道,在创建完成拓扑数据集后,需要添加拓扑规则,最后验证拓扑,就能按拓扑规则的内容找出错误的数据记录。

        也就是这个过程,发现了一些奥妙……

        在对“重叠”验证的过程,实际上对线要素的节点进行了分析和规整,它在出现重叠的点位(或附近)进行了插入节点处理,并对线节点的顺序进行了重新调整,以确保线的is_vaild属性和is_simple属性能正确识别线。

        162-163-164节点构成的线,在使用拓扑验证前,该条线有3个节点,is_simple属性是TRUE,在使用拓扑验证后,is_simple属性为FALSE,要素的节点变成4个,其中在164节点的位置增加了一个节点。


验证拓扑包含以下四个过程:

  1. 对要素折点进行裂化和聚类以查找共享同一位置(具有通用坐标)的重合要素。

  2. 将共有坐标折点插入到共享几何的重合要素中。

  3. 运行一系列完整性检查以确定是否违反了为拓扑定义的规则。

  4. 在要素数据集中创建潜在拓扑错误的错误日志。


        为了实现该“验证”处理的内容,方便在以后写工具时解决线要素的重整,小编附上实现代码:


from shapely.geometry import LineString, Point
from rtree import index
import numpy as np


def create_point_rtree(points):
    p = index.Property()
    p.dimension = 2
    idx = index.Index(properties=p)

    for i, pt in enumerate(points):
        point = Point(pt)
        idx.insert(i, point.bounds, point)

    return idx


def verification_line(line):
    line_coords = np.array(line.coords)

    idx = create_point_rtree(line_coords)

    new_coords = []

    for i in range(len(line_coords) - 1):
        segment_coords = [line_coords[i], line_coords[i + 1]]
        segment = LineString(segment_coords)

        intersecting_nodes = idx.intersection(segment.bounds, objects=True)
        ins_seg_nodes = [i.object for i in intersecting_nodes if segment.intersects(i.object.buffer(1e-8))]

        ls_nodes = [node for node in ins_seg_nodes if not np.isclose(np.array(segment.coords) - np.array(node.coords), 0).any()]

        new_coords.append(segment_coords[0])
        if len(ls_nodes) == 0:
            continue

        elif len(ls_nodes) > 1:

            # 排序后的节点插入到新坐标串
            sorted_nodes = sorted(ls_nodes,
                                  key=lambda node: np.linalg.norm(np.array(node.coords) - line_coords[0]))

            # 逐一添加排序后的节点坐标到 new_coords
            for node in sorted_nodes:
                new_coords.append(np.array(node.coords))
        else:
            new_coords.append(np.array(ls_nodes[0].coords))

    new_coords.append(line_coords[-1])

    new_line_coords = np.vstack(new_coords)

    return LineString(new_line_coords)


if __name__ == "__main__":
    # Example usage:
    multi_line = LineString([(0, 0), (3, 3), (5, 5), (0.4999999999, 0.5)])
    updated_multi_line = verification_line(multi_line)
    print(updated_multi_line)

        拓扑容差,在ArcGIS中普遍使用。 拓扑容差是一条术语,通常用于表示两种容差:X,Y 容差和 Z 容差。拓扑容差的默认值是坐标分辨率的 10 倍。


实用提示

以下是一些有关拓扑容差的实用提示:

1. 设置容差大小

  • 建议使用 10 倍于 X 和 Y 分辨率的容差值,这通常会得到良好的结果。

  • 典型的 X 和 Y 容差要比数据采集的实际精度小。

  • 需要平衡 X、Y 容差,太小可能导致无法正确处理重叠边界的线作业,而太大可能导致要素坐标重叠,影响地图表达的精度。

2. X、Y 容差和数据采集精度

  • X、Y 容差不应接近数据采集的精度,这可能导致坐标移动过大或过小。

  • 在地图比例为 1:12,000 时,即使 1/50 英寸也对应 20 英尺,这种精度难以在数字化和扫描转换中实现。

3. 坐标等级设置

  • 在拓扑中,可以为每个要素类设置坐标精度等级,使更精确的要素具有更高的等级。

  • 更高等级的要素可以调整较低等级但更不精确的要素,以确保坐标的一致性。

4. 控制移动要素类

  • 在聚类过程中,可以设置拓扑规则以控制可能移动的要素类。

  • 通过为要素类分配不同的等级,在拓扑容差范围内,低等级要素的折点会被捕捉到高等级要素的折点,以及同级要素折点位置的几何平均。


        02 线重叠

        线重叠示意图如下:

图片

        线重叠,就比较好理解,就是两条或多条线要素,在空间位置上存在重叠和覆盖。如上图所示,折线1、9、2和折线10发生重叠,折线3与折线4发生重叠。若只是为了找出这种重叠,可使用“成对相交”工具或对该要素类创建拓扑数据集,使用拓扑规则“不能重叠”解决。

        但要实现“重叠”部分的修复,就不是一件简单的事。

        比如下图中,修复要素8和要素9重叠部分时,“移除重叠”功能需要你选择要保留的要素。

图片

        从几何的角度分析,选择保留的要素,需要考虑线的走向和连接;从属性的角度分析,选择保留的要素,某些属性值应该与线的上下连接要素保持一致。

        数据实际处理过程中,往往是优先考虑几何的走向,属性值采取属性传递或单独填写。一是属性值一般不靠谱,都需要分析判断后选择,二是属性值靠谱,无论选谁都可以。基于这两点情况,我们在做批量“线重叠”修复时,先考虑几何上的合理性。

        具体算法如下图所示描述:

二、线重叠检查修复工具

        线重叠检查修复工具,对线要素类图层中,线要素或线要素间存在重叠的部分进行查找和自动修复。

2.1 工具概述

        “线重叠检查修复修复”工具,支持的功能如下:

  1. 对线自重叠部分进行查找输出,支持自动修复功能;

  2. 对线重叠部分进行查找输出,支持批量、合理性自动修复。

2.2 功能流程

        (1)工具打开界面如下图所示:

        (2)工具参数介绍如下:

图片

        (3)工具输出:

        当要素类中存在自重叠错误时:

        输出“{要素类名}_self_overlay”,记录自重叠修复的位置。

        当要素类中存在线重叠错误时:

        输出{要素类名}_overlay”,记录线重叠修复的位置。

        若“是否另存输出”未选中,将在scratch.gdb中备份输入要素,并对其进行重叠修复。默认将直接对输入要素进行重叠修复。

        (4)注意事项:

        当工具参数“是否另存输出”未选中时,将对输入的要素进行原地修改,运行工具前,应注意数据备份。

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/802444.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入探讨极限编程(XP):技术实践与频繁发布的艺术

目录 前言1. 极限编程的核心原则1.1 沟通1.2 简单1.3 反馈1.4 勇气1.5 尊重 2. 关键实践2.1 结对编程2.1.1 提高代码质量2.1.2 促进知识共享2.1.3 增强团队协作 2.2 测试驱动开发&#xff08;TDD&#xff09;2.2.1 提升代码可靠性2.2.2 提高代码可维护性2.2.3 鼓励良好设计 2.3…

判断点与圆的位置关系(c++)

可以通过创建两个类来解决问题 &#xff1a; 代码&#xff1a; #include<iostream> using namespace std;class Point { public:void setX(int x){m_X x;}int getX(){return m_X;}void setY(int y){m_Y y;}int getY(){return m_Y;}private:int m_X;int m_Y;};class C…

【系统架构设计师】十一、系统架构设计(中间件|典型应用架构)

目录 九、中间件 9.1 基础概念 9.2 中间件分类 十、典型应用架构 10.1 J2EE和四层结构 10.2 JSPServletJavaBeanDAO 10.3 .NET和J2EE之争 往期推荐 历年真题练习 九、中间件 之前总提到中间件&#xff0c;那么中间件到底是什么&#xff1f;在系统架构中又扮演者什么角…

Spring与设计模式实战之策略模式

Spring与设计模式实战之策略模式 引言 在现代软件开发中&#xff0c;设计模式是解决常见设计问题的有效工具。它们提供了经过验证的解决方案&#xff0c;帮助开发人员构建灵活、可扩展和可维护的系统。本文将探讨策略模式在Spring框架中的应用&#xff0c;并通过实际例子展示…

C++ | Leetcode C++题解之第240题搜索二维矩阵II

题目&#xff1a; 题解&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m matrix.size(), n matrix[0].size();int x 0, y n - 1;while (x < m && y > 0) {if (matrix[x][y] targ…

LabVIEW异步和同步通信详细分析及比较

1. 基本原理 异步通信&#xff1a; 原理&#xff1a;异步通信&#xff08;Asynchronous Communication&#xff09;是一种数据传输方式&#xff0c;其中数据发送和接收操作在独立的时间进行&#xff0c;不需要在特定时刻对齐。发送方在任何时刻可以发送数据&#xff0c;而接收…

AI自动生成PPT哪个软件好?高效制作PPT优选这4个

7.15初伏的到来&#xff0c;也宣告三伏天的酷热正式拉开序幕~在这个传统的节气里&#xff0c;人们以各种方式避暑纳凉&#xff0c;享受夏日的悠闲时光。 而除了传统的避暑活动&#xff0c;我们还可以用一种新颖的方式记录和分享这份夏日的清凉——那就是通过PPT的方式将这一传…

【Linux】权限管理与相关指令

文章目录 1.权限、文件权限、用户文件权限的理解以及对应八进制数值表示、设置目录为粘滞位文件类型 2.权限相关的常用指令susudochmodchownchgrpumaskwhoamifile 1.权限、文件权限、用户 通过一定条件&#xff0c;拦住一部分人&#xff0c;给另一部分权利来访问资源&#xff0…

Amazon EC2 部署Ollama + webUI

最近和同事闲聊&#xff0c;我们能不能内网自己部署一个LLM&#xff0c;于是便有了Ollama webUI的尝试 对于Linux&#xff0c;使用一行命令即可 curl -fsSL https://ollama.com/install.sh | shollama --help Large language model runnerUsage:ollam…

【Dison夏令营 Day 21】用Python编写绘图

绘画 - 在屏幕上绘制线条和形状。单击标记形状的起点&#xff0c;再次单击标记形状的终点。可使用键盘选择不同的形状和颜色。 """Paint, for drawing shapes.Exercises1. Add a color. 2. Complete circle. 3. Complete rectangle. 4. Complete triangle. 5. A…

大厂面试官问我:为什么Redis的rehash采用渐进式,而Java的hashmap是一次性rehash?【后端八股文十二:Redis hash八股文合集】

本文为【Redis hash八股文合集】初版&#xff0c;后续还会进行优化更新&#xff0c;欢迎大家关注交流~ hello hello~ &#xff0c;这里是绝命Coding——老白~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏…

PostgreSQL使用(二)

说明&#xff1a;本文介绍PostgreSQL的DML语言&#xff1b; 插入数据 -- 1.全字段插入&#xff0c;字段名可以省略 insert into tb_student values (1, 张三, 1990-01-01, 88.88);-- 2.部分字段插入&#xff0c;字段名必须写全 insert into tb_student (id, name) values (2,…

Windows 11几个常用的快捷键

WinQ/WinS&#xff1a;快速打开一键搜索 WinCtrlD&#xff1a;快速新建虚拟桌面。可以通过四根手指在触摸板划动进行切换&#xff0c;也可以通过WinTAB进行虚拟桌面切换 WinTAB&#xff1a;虚拟桌面切换 WinA&#xff1a;打开快速设置面板 WinD&#xff1a;快速切换到桌面&a…

springboot+vue+mybatis鲜花管理系统+PPT+论文+讲解+售后

随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;鲜花管理系统当然也不能排除在外。鲜花管理系统是以实际运用为开发背景&#xff0c;运用软件工程开发方法&#xff0c;采用SSM技…

如何建设微模块数据中心机房

建设微模块数据中心机房涉及多个步骤和技术要点&#xff0c;以下是基于现代技术和行业最佳实践的建设流程概览&#xff1a; 1. 需求分析与规划 确定需求&#xff1a;分析业务需求&#xff0c;包括IT容量、电力、冷却和空间需求。 选址评估&#xff1a;选择靠近关键基础设施的位…

秋招突击——7/16——复习{滑动窗口——无重复最长子串}——新作{相交链表,环形链表,滑动窗口——找到字符串中所有字母异位词}

文章目录 引言模板——滑动窗口/双指针复习无重复最长子串复习实现 新作相交链表个人实现参考实现 环形链表个人实现参考实现——快慢指针 找到字符串中所有字母的异位词个人实现参考实现 总结 引言 今天本来想完成指标的&#xff0c;但是做着做着&#xff0c;发现大部分做的比…

Day07-ES集群加密,kibana的RBAC实战,zookeeper集群搭建,zookeeper基本管理及kafka单点部署实战

Day07-ES集群加密&#xff0c;kibana的RBAC实战&#xff0c;zookeeper集群搭建&#xff0c;zookeeper基本管理及kafka单点部署实战 0、昨日内容回顾:1、基于nginx的反向代理控制访问kibana2、配置ES集群TSL认证:3、配置kibana连接ES集群4、配置filebeat连接ES集群5、配置logsta…

用go实现限流算法

文章目录 固定窗口优缺点&#xff1a;适用场景&#xff1a;总结&#xff1a; 滑动窗口优缺点&#xff1a;适用场景&#xff1a;总结&#xff1a; 漏桶限流器优缺点&#xff1a;适用场景&#xff1a;总结&#xff1a; 令牌桶优缺点&#xff1a;适用场景&#xff1a;总结&#xf…

保障低压设备安全!中国星坤连接器精密工艺解析!

在现代电子设备中&#xff0c;连接器扮演着至关重要的角色&#xff0c;它们是电子系统之间沟通的桥梁。随着技术的发展&#xff0c;对连接器的需求也在不断提升&#xff0c;特别是在低电压应用领域。中国星坤最新推出的低压连接器&#xff0c;以其精密性和安全性&#xff0c;为…

AI算法18-最小角回归算法Least Angle Regression | LARS

​​​ 最小角回归算法简介 最小角回归&#xff08;Least Angle Regression, LAR&#xff09;是一种用于回归分析的统计方法&#xff0c;它在某些方面类似于最小二乘回归&#xff0c;但提供了一些额外的优点。最小角回归由Bradley Efron等人提出&#xff0c;主要用于处理具有…